patterns: Use standalone pattern language library instead of built-in one
This commit is contained in:
parent
f5fe49923b
commit
17383083fb
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -26,3 +26,6 @@
|
||||
path = lib/external/libromfs
|
||||
url = https://github.com/WerWolv/libromfs
|
||||
ignore = dirty
|
||||
[submodule "lib/external/pattern_language"]
|
||||
path = lib/external/pattern_language
|
||||
url = https://github.com/WerWolv/PatternLanguage
|
||||
|
@ -2,11 +2,16 @@
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/cmake-build-debug/_deps/imhex_patterns-src" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/cmake-build-debug/_deps/imhex_patterns-src/yara/official_rules" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/cmake-build-debug/_deps/imhex_patterns-src/yara/official_rules/utils/yara-forensics" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/lib/external/capstone" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/lib/external/curl" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/lib/external/fmt" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/lib/external/libromfs" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/lib/external/nativefiledialog" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/lib/external/pattern_language" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/lib/external/pattern_language/external/fmt" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/lib/external/xdgpp" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/lib/external/yara/yara" vcs="Git" />
|
||||
</component>
|
||||
|
1
lib/external/pattern_language
vendored
Submodule
1
lib/external/pattern_language
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 3abaa2a36d833bb3be7a580dff9b89c37c876a18
|
@ -79,7 +79,8 @@ else()
|
||||
pkg_search_module(CAPSTONE 4.0.2 REQUIRED capstone)
|
||||
endif()
|
||||
|
||||
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../external/pattern_language ${CMAKE_CURRENT_BINARY_DIR}/external/pattern_language EXCLUDE_FROM_ALL)
|
||||
set_target_properties(libpl PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
find_package(mbedTLS 2.26.0 REQUIRED)
|
||||
configurePython()
|
||||
@ -125,14 +126,6 @@ set(LIBIMHEX_SOURCES
|
||||
source/helpers/loader_script_handler.cpp
|
||||
source/helpers/logger.cpp
|
||||
|
||||
source/pattern_language/pattern_language.cpp
|
||||
source/pattern_language/preprocessor.cpp
|
||||
source/pattern_language/lexer.cpp
|
||||
source/pattern_language/parser.cpp
|
||||
source/pattern_language/validator.cpp
|
||||
source/pattern_language/evaluator.cpp
|
||||
source/pattern_language/log_console.cpp
|
||||
|
||||
source/providers/provider.cpp
|
||||
|
||||
source/ui/imgui_imhex_extensions.cpp
|
||||
@ -166,4 +159,4 @@ if (APPLE)
|
||||
target_link_libraries(libimhex PUBLIC ${FOUNDATION})
|
||||
endif ()
|
||||
|
||||
target_link_libraries(libimhex PUBLIC dl imgui nfd magic ${CAPSTONE_LIBRARIES} LLVMDemangle microtar ${NLOHMANN_JSON_LIBRARIES} ${YARA_LIBRARIES} ${LIBCURL_LIBRARIES} ${MBEDTLS_LIBRARIES} ${FMT_LIBRARIES} ${Python_LIBRARIES} libromfs)
|
||||
target_link_libraries(libimhex PUBLIC dl imgui nfd magic ${CAPSTONE_LIBRARIES} LLVMDemangle microtar ${NLOHMANN_JSON_LIBRARIES} ${YARA_LIBRARIES} ${LIBCURL_LIBRARIES} ${MBEDTLS_LIBRARIES} ${FMT_LIBRARIES} ${Python_LIBRARIES} libromfs libpl)
|
||||
|
@ -4,7 +4,7 @@
|
||||
#include <hex/helpers/concepts.hpp>
|
||||
#include <hex/helpers/fs.hpp>
|
||||
|
||||
#include <hex/pattern_language/token.hpp>
|
||||
#include <pl/pattern_language.hpp>
|
||||
#include <hex/api/imhex_api.hpp>
|
||||
#include <hex/api/event.hpp>
|
||||
|
||||
@ -16,13 +16,15 @@
|
||||
|
||||
#include <nlohmann/json_fwd.hpp>
|
||||
|
||||
namespace pl {
|
||||
class Evaluator;
|
||||
}
|
||||
|
||||
namespace hex {
|
||||
|
||||
class View;
|
||||
class LanguageDefinition;
|
||||
namespace pl {
|
||||
class Evaluator;
|
||||
}
|
||||
|
||||
namespace dp {
|
||||
class Node;
|
||||
}
|
||||
@ -117,72 +119,28 @@ namespace hex {
|
||||
|
||||
namespace impl {
|
||||
|
||||
struct ColorPalette {
|
||||
struct FunctionDefinition {
|
||||
pl::api::Namespace ns;
|
||||
std::string name;
|
||||
std::vector<u32> colors;
|
||||
|
||||
pl::api::FunctionParameterCount parameterCount;
|
||||
pl::api::FunctionCallback callback;
|
||||
|
||||
bool dangerous;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
struct ParameterCount {
|
||||
ParameterCount() = default;
|
||||
std::unique_ptr<pl::PatternLanguage> createDefaultRuntime(prv::Provider *provider);
|
||||
|
||||
constexpr bool operator==(const ParameterCount &other) const {
|
||||
return this->min == other.min && this->max == other.max;
|
||||
}
|
||||
void addPragma(const std::string &name, const pl::api::PragmaHandler &handler);
|
||||
|
||||
[[nodiscard]] static ParameterCount unlimited() {
|
||||
return ParameterCount { 0, 0xFFFF'FFFF };
|
||||
}
|
||||
void addFunction(const pl::api::Namespace &ns, const std::string &name, pl::api::FunctionParameterCount parameterCount, const pl::api::FunctionCallback &func);
|
||||
void addDangerousFunction(const pl::api::Namespace &ns, const std::string &name, pl::api::FunctionParameterCount parameterCount, const pl::api::FunctionCallback &func);
|
||||
|
||||
[[nodiscard]] static ParameterCount none() {
|
||||
return ParameterCount { 0, 0 };
|
||||
}
|
||||
std::map<std::string, pl::api::PragmaHandler> &getPragmas();
|
||||
std::vector<impl::FunctionDefinition> &getFunctions();
|
||||
|
||||
[[nodiscard]] static ParameterCount exactly(u32 value) {
|
||||
return ParameterCount { value, value };
|
||||
}
|
||||
|
||||
[[nodiscard]] static ParameterCount moreThan(u32 value) {
|
||||
return ParameterCount { value + 1, 0xFFFF'FFFF };
|
||||
}
|
||||
|
||||
[[nodiscard]] static ParameterCount lessThan(u32 value) {
|
||||
return ParameterCount { 0, u32(std::max<i64>(i64(value) - 1, 0)) };
|
||||
}
|
||||
|
||||
[[nodiscard]] static ParameterCount atLeast(u32 value) {
|
||||
return ParameterCount { value, 0xFFFF'FFFF };
|
||||
}
|
||||
|
||||
[[nodiscard]] static ParameterCount between(u32 min, u32 max) {
|
||||
return ParameterCount { min, max };
|
||||
}
|
||||
|
||||
u32 min = 0, max = 0;
|
||||
private:
|
||||
ParameterCount(u32 min, u32 max) : min(min), max(max) { }
|
||||
};
|
||||
|
||||
using Namespace = std::vector<std::string>;
|
||||
using Callback = std::function<std::optional<hex::pl::Token::Literal>(hex::pl::Evaluator *, const std::vector<hex::pl::Token::Literal> &)>;
|
||||
|
||||
struct Function {
|
||||
ParameterCount parameterCount;
|
||||
std::vector<pl::Token::Literal> defaultParameters;
|
||||
Callback func;
|
||||
bool dangerous;
|
||||
};
|
||||
|
||||
void addFunction(const Namespace &ns, const std::string &name, ParameterCount parameterCount, const Callback &func);
|
||||
void addDangerousFunction(const Namespace &ns, const std::string &name, ParameterCount parameterCount, const Callback &func);
|
||||
std::map<std::string, ContentRegistry::PatternLanguage::Function> &getFunctions();
|
||||
|
||||
std::vector<impl::ColorPalette> &getPalettes();
|
||||
void addColorPalette(const std::string &unlocalizedName, const std::vector<u32> &colors);
|
||||
void setSelectedPalette(u32 index);
|
||||
u32 getNextColor();
|
||||
void resetPalette();
|
||||
}
|
||||
|
||||
/* View Registry. Allows adding of new windows */
|
||||
|
@ -18,6 +18,10 @@
|
||||
|
||||
struct GLFWwindow;
|
||||
|
||||
namespace pl {
|
||||
class Pattern;
|
||||
}
|
||||
|
||||
namespace hex {
|
||||
|
||||
class EventId {
|
||||
@ -96,10 +100,6 @@ namespace hex {
|
||||
static EventList s_events;
|
||||
};
|
||||
|
||||
namespace pl {
|
||||
class Pattern;
|
||||
}
|
||||
|
||||
/* Default Events */
|
||||
EVENT_DEF(EventFileLoaded, std::fs::path);
|
||||
EVENT_DEF(EventFileUnloaded);
|
||||
|
@ -1,48 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/token.hpp>
|
||||
#include <hex/pattern_language/evaluator.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern.hpp>
|
||||
#include <hex/helpers/concepts.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class Pattern;
|
||||
class Evaluator;
|
||||
|
||||
class ASTNode : public Cloneable<ASTNode> {
|
||||
public:
|
||||
constexpr ASTNode() = default;
|
||||
|
||||
constexpr virtual ~ASTNode() = default;
|
||||
|
||||
constexpr ASTNode(const ASTNode &) = default;
|
||||
|
||||
[[nodiscard]] constexpr u32 getLineNumber() const { return this->m_lineNumber; }
|
||||
|
||||
[[maybe_unused]] constexpr void setLineNumber(u32 lineNumber) { this->m_lineNumber = lineNumber; }
|
||||
|
||||
[[nodiscard]] virtual std::unique_ptr<ASTNode> evaluate(Evaluator *evaluator) const {
|
||||
hex::unused(evaluator);
|
||||
|
||||
return this->clone();
|
||||
}
|
||||
|
||||
[[nodiscard]] virtual std::vector<std::unique_ptr<Pattern>> createPatterns(Evaluator *evaluator) const {
|
||||
hex::unused(evaluator);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
using FunctionResult = std::optional<Token::Literal>;
|
||||
virtual FunctionResult execute(Evaluator *evaluator) const {
|
||||
hex::unused(evaluator);
|
||||
|
||||
LogConsole::abortEvaluation("cannot execute non-function statement", this);
|
||||
}
|
||||
|
||||
private:
|
||||
u32 m_lineNumber = 1;
|
||||
};
|
||||
|
||||
}
|
@ -1,312 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_attribute.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_literal.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_builtin_type.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_while_statement.hpp>
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern_padding.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_character.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_wide_character.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_string.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_wide_string.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_array_dynamic.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_array_static.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeArrayVariableDecl : public ASTNode,
|
||||
public Attributable {
|
||||
public:
|
||||
ASTNodeArrayVariableDecl(std::string name, std::shared_ptr<ASTNodeTypeDecl> type, std::unique_ptr<ASTNode> &&size, std::unique_ptr<ASTNode> &&placementOffset = {})
|
||||
: ASTNode(), m_name(std::move(name)), m_type(std::move(type)), m_size(std::move(size)), m_placementOffset(std::move(placementOffset)) { }
|
||||
|
||||
ASTNodeArrayVariableDecl(const ASTNodeArrayVariableDecl &other) : ASTNode(other), Attributable(other) {
|
||||
this->m_name = other.m_name;
|
||||
this->m_type = other.m_type;
|
||||
if (other.m_size != nullptr)
|
||||
this->m_size = other.m_size->clone();
|
||||
else
|
||||
this->m_size = nullptr;
|
||||
|
||||
if (other.m_placementOffset != nullptr)
|
||||
this->m_placementOffset = other.m_placementOffset->clone();
|
||||
else
|
||||
this->m_placementOffset = nullptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeArrayVariableDecl(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<std::unique_ptr<Pattern>> createPatterns(Evaluator *evaluator) const override {
|
||||
auto startOffset = evaluator->dataOffset();
|
||||
|
||||
if (this->m_placementOffset != nullptr) {
|
||||
auto evaluatedPlacement = this->m_placementOffset->evaluate(evaluator);
|
||||
auto offset = dynamic_cast<ASTNodeLiteral *>(evaluatedPlacement.get());
|
||||
|
||||
evaluator->dataOffset() = std::visit(overloaded {
|
||||
[this](const std::string &) -> u64 { LogConsole::abortEvaluation("placement offset cannot be a string", this); },
|
||||
[this](Pattern *) -> u64 { LogConsole::abortEvaluation("placement offset cannot be a custom type", this); },
|
||||
[](auto &&offset) -> u64 { return offset; } },
|
||||
offset->getValue());
|
||||
}
|
||||
|
||||
auto type = this->m_type->evaluate(evaluator);
|
||||
|
||||
std::unique_ptr<Pattern> pattern;
|
||||
if (dynamic_cast<ASTNodeBuiltinType *>(type.get()))
|
||||
pattern = createStaticArray(evaluator);
|
||||
else if (auto attributable = dynamic_cast<Attributable *>(type.get())) {
|
||||
bool isStaticType = attributable->hasAttribute("static", false);
|
||||
|
||||
if (isStaticType)
|
||||
pattern = createStaticArray(evaluator);
|
||||
else
|
||||
pattern = createDynamicArray(evaluator);
|
||||
} else {
|
||||
LogConsole::abortEvaluation("invalid type used in array", this);
|
||||
}
|
||||
|
||||
applyVariableAttributes(evaluator, this, pattern.get());
|
||||
|
||||
if (this->m_placementOffset != nullptr && !evaluator->isGlobalScope()) {
|
||||
evaluator->dataOffset() = startOffset;
|
||||
}
|
||||
|
||||
return hex::moveToVector(std::move(pattern));
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
std::shared_ptr<ASTNodeTypeDecl> m_type;
|
||||
std::unique_ptr<ASTNode> m_size;
|
||||
std::unique_ptr<ASTNode> m_placementOffset;
|
||||
|
||||
std::unique_ptr<Pattern> createStaticArray(Evaluator *evaluator) const {
|
||||
u64 startOffset = evaluator->dataOffset();
|
||||
|
||||
auto templatePatterns = this->m_type->createPatterns(evaluator);
|
||||
auto &templatePattern = templatePatterns.front();
|
||||
|
||||
evaluator->dataOffset() = startOffset;
|
||||
|
||||
i128 entryCount = 0;
|
||||
|
||||
if (this->m_size != nullptr) {
|
||||
auto sizeNode = this->m_size->evaluate(evaluator);
|
||||
|
||||
if (auto literal = dynamic_cast<ASTNodeLiteral *>(sizeNode.get())) {
|
||||
entryCount = std::visit(overloaded {
|
||||
[this](const std::string &) -> i128 { LogConsole::abortEvaluation("cannot use string to index array", this); },
|
||||
[this](Pattern *) -> i128 { LogConsole::abortEvaluation("cannot use custom type to index array", this); },
|
||||
[](auto &&size) -> i128 { return size; } },
|
||||
literal->getValue());
|
||||
} else if (auto whileStatement = dynamic_cast<ASTNodeWhileStatement *>(sizeNode.get())) {
|
||||
while (whileStatement->evaluateCondition(evaluator)) {
|
||||
entryCount++;
|
||||
evaluator->dataOffset() += templatePattern->getSize();
|
||||
evaluator->handleAbort();
|
||||
}
|
||||
}
|
||||
|
||||
if (entryCount < 0)
|
||||
LogConsole::abortEvaluation("array cannot have a negative size", this);
|
||||
} else {
|
||||
std::vector<u8> buffer(templatePattern->getSize());
|
||||
while (true) {
|
||||
if (evaluator->dataOffset() > evaluator->getProvider()->getActualSize() - buffer.size())
|
||||
LogConsole::abortEvaluation("reached end of file before finding end of unsized array", this);
|
||||
|
||||
evaluator->getProvider()->read(evaluator->dataOffset(), buffer.data(), buffer.size());
|
||||
evaluator->dataOffset() += buffer.size();
|
||||
|
||||
entryCount++;
|
||||
|
||||
bool reachedEnd = true;
|
||||
for (u8 &byte : buffer) {
|
||||
if (byte != 0x00) {
|
||||
reachedEnd = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (reachedEnd) break;
|
||||
evaluator->handleAbort();
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<Pattern> outputPattern;
|
||||
if (dynamic_cast<PatternPadding *>(templatePattern.get())) {
|
||||
outputPattern = std::unique_ptr<Pattern>(new PatternPadding(evaluator, startOffset, 0));
|
||||
} else if (dynamic_cast<PatternCharacter *>(templatePattern.get())) {
|
||||
outputPattern = std::unique_ptr<Pattern>(new PatternString(evaluator, startOffset, 0));
|
||||
} else if (dynamic_cast<PatternWideCharacter *>(templatePattern.get())) {
|
||||
outputPattern = std::unique_ptr<Pattern>(new PatternWideString(evaluator, startOffset, 0));
|
||||
} else {
|
||||
auto arrayPattern = std::make_unique<PatternArrayStatic>(evaluator, startOffset, 0);
|
||||
arrayPattern->setEntries(templatePattern->clone(), entryCount);
|
||||
outputPattern = std::move(arrayPattern);
|
||||
}
|
||||
|
||||
outputPattern->setVariableName(this->m_name);
|
||||
outputPattern->setEndian(templatePattern->getEndian());
|
||||
outputPattern->setTypeName(templatePattern->getTypeName());
|
||||
outputPattern->setSize(templatePattern->getSize() * entryCount);
|
||||
|
||||
evaluator->dataOffset() = startOffset + outputPattern->getSize();
|
||||
|
||||
return outputPattern;
|
||||
}
|
||||
|
||||
std::unique_ptr<Pattern> createDynamicArray(Evaluator *evaluator) const {
|
||||
auto arrayPattern = std::make_unique<PatternArrayDynamic>(evaluator, evaluator->dataOffset(), 0);
|
||||
arrayPattern->setVariableName(this->m_name);
|
||||
|
||||
std::vector<std::shared_ptr<Pattern>> entries;
|
||||
|
||||
size_t size = 0;
|
||||
u64 entryIndex = 0;
|
||||
|
||||
auto addEntries = [&](std::vector<std::unique_ptr<Pattern>> &&patterns) {
|
||||
for (auto &pattern : patterns) {
|
||||
pattern->setVariableName(hex::format("[{}]", entryIndex));
|
||||
pattern->setEndian(arrayPattern->getEndian());
|
||||
|
||||
size += pattern->getSize();
|
||||
entryIndex++;
|
||||
|
||||
entries.push_back(std::move(pattern));
|
||||
|
||||
evaluator->handleAbort();
|
||||
}
|
||||
};
|
||||
|
||||
auto discardEntries = [&](u32 count) {
|
||||
for (u32 i = 0; i < count; i++) {
|
||||
entries.pop_back();
|
||||
entryIndex--;
|
||||
}
|
||||
};
|
||||
|
||||
if (this->m_size != nullptr) {
|
||||
auto sizeNode = this->m_size->evaluate(evaluator);
|
||||
|
||||
if (auto literal = dynamic_cast<ASTNodeLiteral *>(sizeNode.get())) {
|
||||
auto entryCount = std::visit(overloaded {
|
||||
[this](const std::string &) -> u128 { LogConsole::abortEvaluation("cannot use string to index array", this); },
|
||||
[this](Pattern *) -> u128 { LogConsole::abortEvaluation("cannot use custom type to index array", this); },
|
||||
[](auto &&size) -> u128 { return size; } },
|
||||
literal->getValue());
|
||||
|
||||
auto limit = evaluator->getArrayLimit();
|
||||
if (entryCount > limit)
|
||||
LogConsole::abortEvaluation(hex::format("array grew past set limit of {}", limit), this);
|
||||
|
||||
for (u64 i = 0; i < entryCount; i++) {
|
||||
evaluator->setCurrentControlFlowStatement(ControlFlowStatement::None);
|
||||
|
||||
auto patterns = this->m_type->createPatterns(evaluator);
|
||||
size_t patternCount = patterns.size();
|
||||
|
||||
if (!patterns.empty())
|
||||
addEntries(std::move(patterns));
|
||||
|
||||
auto ctrlFlow = evaluator->getCurrentControlFlowStatement();
|
||||
evaluator->setCurrentControlFlowStatement(ControlFlowStatement::None);
|
||||
if (ctrlFlow == ControlFlowStatement::Break)
|
||||
break;
|
||||
else if (ctrlFlow == ControlFlowStatement::Continue) {
|
||||
|
||||
discardEntries(patternCount);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else if (auto whileStatement = dynamic_cast<ASTNodeWhileStatement *>(sizeNode.get())) {
|
||||
while (whileStatement->evaluateCondition(evaluator)) {
|
||||
auto limit = evaluator->getArrayLimit();
|
||||
if (entryIndex > limit)
|
||||
LogConsole::abortEvaluation(hex::format("array grew past set limit of {}", limit), this);
|
||||
|
||||
evaluator->setCurrentControlFlowStatement(ControlFlowStatement::None);
|
||||
|
||||
auto patterns = this->m_type->createPatterns(evaluator);
|
||||
size_t patternCount = patterns.size();
|
||||
|
||||
if (!patterns.empty())
|
||||
addEntries(std::move(patterns));
|
||||
|
||||
auto ctrlFlow = evaluator->getCurrentControlFlowStatement();
|
||||
evaluator->setCurrentControlFlowStatement(ControlFlowStatement::None);
|
||||
if (ctrlFlow == ControlFlowStatement::Break)
|
||||
break;
|
||||
else if (ctrlFlow == ControlFlowStatement::Continue) {
|
||||
discardEntries(patternCount);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
while (true) {
|
||||
bool reachedEnd = true;
|
||||
auto limit = evaluator->getArrayLimit();
|
||||
if (entryIndex > limit)
|
||||
LogConsole::abortEvaluation(hex::format("array grew past set limit of {}", limit), this);
|
||||
|
||||
evaluator->setCurrentControlFlowStatement(ControlFlowStatement::None);
|
||||
|
||||
auto patterns = this->m_type->createPatterns(evaluator);
|
||||
|
||||
for (auto &pattern : patterns) {
|
||||
std::vector<u8> buffer(pattern->getSize());
|
||||
|
||||
if (evaluator->dataOffset() > evaluator->getProvider()->getActualSize() - buffer.size()) {
|
||||
LogConsole::abortEvaluation("reached end of file before finding end of unsized array", this);
|
||||
}
|
||||
|
||||
const auto patternSize = pattern->getSize();
|
||||
addEntries(hex::moveToVector(std::move(pattern)));
|
||||
|
||||
auto ctrlFlow = evaluator->getCurrentControlFlowStatement();
|
||||
if (ctrlFlow == ControlFlowStatement::None)
|
||||
break;
|
||||
|
||||
evaluator->getProvider()->read(evaluator->dataOffset() - patternSize, buffer.data(), buffer.size());
|
||||
reachedEnd = true;
|
||||
for (u8 &byte : buffer) {
|
||||
if (byte != 0x00) {
|
||||
reachedEnd = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (reachedEnd) break;
|
||||
}
|
||||
|
||||
auto ctrlFlow = evaluator->getCurrentControlFlowStatement();
|
||||
evaluator->setCurrentControlFlowStatement(ControlFlowStatement::None);
|
||||
if (ctrlFlow == ControlFlowStatement::Break)
|
||||
break;
|
||||
else if (ctrlFlow == ControlFlowStatement::Continue) {
|
||||
discardEntries(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (reachedEnd) break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (auto &arrayEntries = arrayPattern->getEntries(); !arrayEntries.empty())
|
||||
arrayPattern->setTypeName(arrayEntries.front()->getTypeName());
|
||||
|
||||
arrayPattern->setEntries(std::move(entries));
|
||||
arrayPattern->setSize(size);
|
||||
|
||||
return arrayPattern;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_literal.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeAssignment : public ASTNode {
|
||||
public:
|
||||
ASTNodeAssignment(std::string lvalueName, std::unique_ptr<ASTNode> &&rvalue) : m_lvalueName(std::move(lvalueName)), m_rvalue(std::move(rvalue)) {
|
||||
}
|
||||
|
||||
ASTNodeAssignment(const ASTNodeAssignment &other) : ASTNode(other) {
|
||||
this->m_lvalueName = other.m_lvalueName;
|
||||
this->m_rvalue = other.m_rvalue->clone();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeAssignment(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::string &getLValueName() const {
|
||||
return this->m_lvalueName;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::unique_ptr<ASTNode> &getRValue() const {
|
||||
return this->m_rvalue;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<std::unique_ptr<Pattern>> createPatterns(Evaluator *evaluator) const override {
|
||||
this->execute(evaluator);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
FunctionResult execute(Evaluator *evaluator) const override {
|
||||
const auto node = this->getRValue()->evaluate(evaluator);
|
||||
const auto literal = dynamic_cast<ASTNodeLiteral *>(node.get());
|
||||
|
||||
if (this->getLValueName() == "$")
|
||||
evaluator->dataOffset() = Token::literalToUnsigned(literal->getValue());
|
||||
else
|
||||
evaluator->setVariable(this->getLValueName(), literal->getValue());
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_lvalueName;
|
||||
std::unique_ptr<ASTNode> m_rvalue;
|
||||
};
|
||||
|
||||
}
|
@ -1,220 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern_pointer.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_array_dynamic.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeAttribute : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeAttribute(std::string attribute, std::optional<std::string> value = std::nullopt)
|
||||
: ASTNode(), m_attribute(std::move(attribute)), m_value(std::move(value)) { }
|
||||
|
||||
~ASTNodeAttribute() override = default;
|
||||
|
||||
ASTNodeAttribute(const ASTNodeAttribute &other) : ASTNode(other) {
|
||||
this->m_attribute = other.m_attribute;
|
||||
this->m_value = other.m_value;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeAttribute(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::string &getAttribute() const {
|
||||
return this->m_attribute;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::optional<std::string> &getValue() const {
|
||||
return this->m_value;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_attribute;
|
||||
std::optional<std::string> m_value;
|
||||
};
|
||||
|
||||
|
||||
class Attributable {
|
||||
protected:
|
||||
Attributable() = default;
|
||||
|
||||
Attributable(const Attributable &other) {
|
||||
for (auto &attribute : other.m_attributes) {
|
||||
auto copy = attribute->clone();
|
||||
if (auto node = dynamic_cast<ASTNodeAttribute *>(copy.get())) {
|
||||
this->m_attributes.push_back(std::unique_ptr<ASTNodeAttribute>(node));
|
||||
(void)copy.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
virtual void addAttribute(std::unique_ptr<ASTNodeAttribute> &&attribute) {
|
||||
this->m_attributes.push_back(std::move(attribute));
|
||||
}
|
||||
|
||||
[[nodiscard]] const auto &getAttributes() const {
|
||||
return this->m_attributes;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool hasAttribute(const std::string &key, bool needsParameter) const {
|
||||
return std::any_of(this->m_attributes.begin(), this->m_attributes.end(), [&](const std::unique_ptr<ASTNodeAttribute> &attribute) {
|
||||
if (attribute->getAttribute() == key) {
|
||||
if (needsParameter && !attribute->getValue().has_value())
|
||||
LogConsole::abortEvaluation(hex::format("attribute '{}' expected a parameter"), attribute);
|
||||
else if (!needsParameter && attribute->getValue().has_value())
|
||||
LogConsole::abortEvaluation(hex::format("attribute '{}' did not expect a parameter "), attribute);
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<std::string> getAttributeValue(const std::string &key) const {
|
||||
auto attribute = std::find_if(this->m_attributes.begin(), this->m_attributes.end(), [&](const std::unique_ptr<ASTNodeAttribute> &attribute) {
|
||||
return attribute->getAttribute() == key;
|
||||
});
|
||||
|
||||
if (attribute != this->m_attributes.end())
|
||||
return (*attribute)->getValue();
|
||||
else
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<ASTNodeAttribute>> m_attributes;
|
||||
};
|
||||
|
||||
|
||||
inline void applyTypeAttributes(Evaluator *evaluator, const ASTNode *node, Pattern *pattern) {
|
||||
auto attributable = dynamic_cast<const Attributable *>(node);
|
||||
if (attributable == nullptr)
|
||||
LogConsole::abortEvaluation("attribute cannot be applied here", node);
|
||||
|
||||
if (attributable->hasAttribute("inline", false)) {
|
||||
auto inlinable = dynamic_cast<Inlinable *>(pattern);
|
||||
|
||||
if (inlinable == nullptr)
|
||||
LogConsole::abortEvaluation("inline attribute can only be applied to nested types", node);
|
||||
else
|
||||
inlinable->setInlined(true);
|
||||
}
|
||||
|
||||
using namespace ContentRegistry::PatternLanguage;
|
||||
|
||||
if (auto value = attributable->getAttributeValue("format"); value) {
|
||||
auto functions = evaluator->getCustomFunctions();
|
||||
if (!functions.contains(*value))
|
||||
LogConsole::abortEvaluation(hex::format("cannot find formatter function '{}'", *value), node);
|
||||
|
||||
const auto &function = functions[*value];
|
||||
if (function.parameterCount != ParameterCount::exactly(1))
|
||||
LogConsole::abortEvaluation("formatter function needs exactly one parameter", node);
|
||||
|
||||
pattern->setFormatterFunction(function);
|
||||
}
|
||||
|
||||
if (auto value = attributable->getAttributeValue("format_entries"); value) {
|
||||
auto functions = evaluator->getCustomFunctions();
|
||||
if (!functions.contains(*value))
|
||||
LogConsole::abortEvaluation(hex::format("cannot find formatter function '{}'", *value), node);
|
||||
|
||||
const auto &function = functions[*value];
|
||||
if (function.parameterCount != ParameterCount::exactly(1))
|
||||
LogConsole::abortEvaluation("formatter function needs exactly one parameter", node);
|
||||
|
||||
auto array = dynamic_cast<PatternArrayDynamic *>(pattern);
|
||||
if (array == nullptr)
|
||||
LogConsole::abortEvaluation("inline_array attribute can only be applied to array types", node);
|
||||
|
||||
for (const auto &entry : array->getEntries()) {
|
||||
entry->setFormatterFunction(function);
|
||||
}
|
||||
}
|
||||
|
||||
if (auto value = attributable->getAttributeValue("transform"); value) {
|
||||
auto functions = evaluator->getCustomFunctions();
|
||||
if (!functions.contains(*value))
|
||||
LogConsole::abortEvaluation(hex::format("cannot find transform function '{}'", *value), node);
|
||||
|
||||
const auto &function = functions[*value];
|
||||
if (function.parameterCount != ParameterCount::exactly(1))
|
||||
LogConsole::abortEvaluation("transform function needs exactly one parameter", node);
|
||||
|
||||
pattern->setTransformFunction(function);
|
||||
}
|
||||
|
||||
if (auto value = attributable->getAttributeValue("pointer_base"); value) {
|
||||
auto functions = evaluator->getCustomFunctions();
|
||||
if (!functions.contains(*value))
|
||||
LogConsole::abortEvaluation(hex::format("cannot find pointer base function '{}'", *value), node);
|
||||
|
||||
const auto &function = functions[*value];
|
||||
if (function.parameterCount != ParameterCount::exactly(1))
|
||||
LogConsole::abortEvaluation("pointer base function needs exactly one parameter", node);
|
||||
|
||||
if (auto pointerPattern = dynamic_cast<PatternPointer *>(pattern)) {
|
||||
u128 pointerValue = pointerPattern->getPointedAtAddress();
|
||||
|
||||
auto result = function.func(evaluator, { pointerValue });
|
||||
|
||||
if (!result.has_value())
|
||||
LogConsole::abortEvaluation("pointer base function did not return a value", node);
|
||||
|
||||
pointerPattern->setPointedAtAddress(Token::literalToUnsigned(result.value()) + pointerValue);
|
||||
} else {
|
||||
LogConsole::abortEvaluation("pointer_base attribute may only be applied to a pointer");
|
||||
}
|
||||
}
|
||||
|
||||
if (attributable->hasAttribute("hidden", false)) {
|
||||
pattern->setHidden(true);
|
||||
}
|
||||
|
||||
if (!pattern->hasOverriddenColor()) {
|
||||
if (auto colorValue = attributable->getAttributeValue("color"); colorValue) {
|
||||
u32 color = strtoul(colorValue->c_str(), nullptr, 16);
|
||||
pattern->setColor(hex::changeEndianess(color, std::endian::big) >> 8);
|
||||
} else if (auto singleColor = attributable->hasAttribute("single_color", false); singleColor) {
|
||||
pattern->setColor(ContentRegistry::PatternLanguage::getNextColor());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void applyVariableAttributes(Evaluator *evaluator, const ASTNode *node, Pattern *pattern) {
|
||||
auto attributable = dynamic_cast<const Attributable *>(node);
|
||||
if (attributable == nullptr)
|
||||
LogConsole::abortEvaluation("attribute cannot be applied here", node);
|
||||
|
||||
auto endOffset = evaluator->dataOffset();
|
||||
evaluator->dataOffset() = pattern->getOffset();
|
||||
ON_SCOPE_EXIT { evaluator->dataOffset() = endOffset; };
|
||||
|
||||
applyTypeAttributes(evaluator, node, pattern);
|
||||
|
||||
if (auto colorValue = attributable->getAttributeValue("color"); colorValue) {
|
||||
u32 color = strtoul(colorValue->c_str(), nullptr, 16);
|
||||
pattern->setColor(hex::changeEndianess(color, std::endian::big) >> 8);
|
||||
} else if (auto singleColor = attributable->hasAttribute("single_color", false); singleColor) {
|
||||
pattern->setColor(ContentRegistry::PatternLanguage::getNextColor());
|
||||
}
|
||||
|
||||
if (auto value = attributable->getAttributeValue("name"); value) {
|
||||
pattern->setDisplayName(*value);
|
||||
}
|
||||
|
||||
if (auto value = attributable->getAttributeValue("comment"); value) {
|
||||
pattern->setComment(*value);
|
||||
}
|
||||
|
||||
if (attributable->hasAttribute("no_unique_address", false)) {
|
||||
endOffset -= pattern->getSize();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,85 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_attribute.hpp>
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern_bitfield.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeBitfield : public ASTNode,
|
||||
public Attributable {
|
||||
public:
|
||||
ASTNodeBitfield() : ASTNode() { }
|
||||
|
||||
ASTNodeBitfield(const ASTNodeBitfield &other) : ASTNode(other), Attributable(other) {
|
||||
for (const auto &[name, entry] : other.getEntries())
|
||||
this->m_entries.emplace_back(name, entry->clone());
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeBitfield(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::vector<std::pair<std::string, std::unique_ptr<ASTNode>>> &getEntries() const { return this->m_entries; }
|
||||
void addEntry(const std::string &name, std::unique_ptr<ASTNode> &&size) { this->m_entries.emplace_back(name, std::move(size)); }
|
||||
|
||||
[[nodiscard]] std::vector<std::unique_ptr<Pattern>> createPatterns(Evaluator *evaluator) const override {
|
||||
auto pattern = std::make_unique<PatternBitfield>(evaluator, evaluator->dataOffset(), 0);
|
||||
|
||||
size_t bitOffset = 0;
|
||||
std::vector<std::shared_ptr<Pattern>> fields;
|
||||
|
||||
BitfieldOrder order = evaluator->getBitfieldOrder();
|
||||
if (this->hasAttribute("left_to_right", false))
|
||||
order = BitfieldOrder::LeftToRight;
|
||||
else if (this->hasAttribute("right_to_left", false))
|
||||
order = BitfieldOrder::RightToLeft;
|
||||
|
||||
std::vector<std::pair<std::string, ASTNode *>> entries;
|
||||
for (const auto &[name, entry] : this->m_entries)
|
||||
entries.emplace_back(name, entry.get());
|
||||
|
||||
if (order == BitfieldOrder::LeftToRight)
|
||||
std::reverse(entries.begin(), entries.end());
|
||||
|
||||
evaluator->pushScope(pattern.get(), fields);
|
||||
ON_SCOPE_EXIT {
|
||||
evaluator->popScope();
|
||||
};
|
||||
|
||||
for (auto &[name, bitSizeNode] : entries) {
|
||||
auto literal = bitSizeNode->evaluate(evaluator);
|
||||
|
||||
u8 bitSize = std::visit(overloaded {
|
||||
[this](const std::string &) -> u8 { LogConsole::abortEvaluation("bitfield field size cannot be a string", this); },
|
||||
[this](Pattern *) -> u8 { LogConsole::abortEvaluation("bitfield field size cannot be a custom type", this); },
|
||||
[](auto &&offset) -> u8 { return static_cast<u8>(offset); } },
|
||||
dynamic_cast<ASTNodeLiteral *>(literal.get())->getValue());
|
||||
|
||||
// If a field is named padding, it was created through a padding expression and only advances the bit position
|
||||
if (name != "padding") {
|
||||
auto field = std::make_unique<PatternBitfieldField>(evaluator, evaluator->dataOffset(), bitOffset, bitSize, pattern.get());
|
||||
field->setVariableName(name);
|
||||
|
||||
fields.push_back(std::move(field));
|
||||
}
|
||||
|
||||
bitOffset += bitSize;
|
||||
}
|
||||
|
||||
pattern->setSize((bitOffset + 7) / 8);
|
||||
pattern->setFields(std::move(fields));
|
||||
|
||||
evaluator->dataOffset() += pattern->getSize();
|
||||
|
||||
applyTypeAttributes(evaluator, this, pattern.get());
|
||||
|
||||
return hex::moveToVector<std::unique_ptr<Pattern>>(std::move(pattern));
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::pair<std::string, std::unique_ptr<ASTNode>>> m_entries;
|
||||
};
|
||||
|
||||
}
|
@ -1,64 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern_padding.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_unsigned.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_signed.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_float.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_boolean.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_character.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_wide_character.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_string.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeBuiltinType : public ASTNode {
|
||||
public:
|
||||
constexpr explicit ASTNodeBuiltinType(Token::ValueType type)
|
||||
: ASTNode(), m_type(type) { }
|
||||
|
||||
[[nodiscard]] constexpr const auto &getType() const { return this->m_type; }
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeBuiltinType(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<std::unique_ptr<Pattern>> createPatterns(Evaluator *evaluator) const override {
|
||||
auto offset = evaluator->dataOffset();
|
||||
auto size = Token::getTypeSize(this->m_type);
|
||||
|
||||
evaluator->dataOffset() += size;
|
||||
|
||||
std::unique_ptr<Pattern> pattern;
|
||||
if (Token::isUnsigned(this->m_type))
|
||||
pattern = std::unique_ptr<Pattern>(new PatternUnsigned(evaluator, offset, size));
|
||||
else if (Token::isSigned(this->m_type))
|
||||
pattern = std::unique_ptr<Pattern>(new PatternSigned(evaluator, offset, size));
|
||||
else if (Token::isFloatingPoint(this->m_type))
|
||||
pattern = std::unique_ptr<Pattern>(new PatternFloat(evaluator, offset, size));
|
||||
else if (this->m_type == Token::ValueType::Boolean)
|
||||
pattern = std::unique_ptr<Pattern>(new PatternBoolean(evaluator, offset));
|
||||
else if (this->m_type == Token::ValueType::Character)
|
||||
pattern = std::unique_ptr<Pattern>(new PatternCharacter(evaluator, offset));
|
||||
else if (this->m_type == Token::ValueType::Character16)
|
||||
pattern = std::unique_ptr<Pattern>(new PatternWideCharacter(evaluator, offset));
|
||||
else if (this->m_type == Token::ValueType::Padding)
|
||||
pattern = std::unique_ptr<Pattern>(new PatternPadding(evaluator, offset, 1));
|
||||
else if (this->m_type == Token::ValueType::String)
|
||||
pattern = std::unique_ptr<Pattern>(new PatternString(evaluator, offset, 1));
|
||||
else if (this->m_type == Token::ValueType::Auto)
|
||||
return {};
|
||||
else
|
||||
LogConsole::abortEvaluation("invalid built-in type", this);
|
||||
|
||||
pattern->setTypeName(Token::getTypeName(this->m_type));
|
||||
|
||||
return hex::moveToVector(std::move(pattern));
|
||||
}
|
||||
|
||||
private:
|
||||
const Token::ValueType m_type;
|
||||
};
|
||||
|
||||
}
|
@ -1,90 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeCast : public ASTNode {
|
||||
public:
|
||||
ASTNodeCast(std::unique_ptr<ASTNode> &&value, std::unique_ptr<ASTNode> &&type) : m_value(std::move(value)), m_type(std::move(type)) { }
|
||||
|
||||
ASTNodeCast(const ASTNodeCast &other) : ASTNode(other) {
|
||||
this->m_value = other.m_value->clone();
|
||||
this->m_type = other.m_type->clone();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeCast(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> evaluate(Evaluator *evaluator) const override {
|
||||
auto evaluatedValue = this->m_value->evaluate(evaluator);
|
||||
auto evaluatedType = this->m_type->evaluate(evaluator);
|
||||
|
||||
auto literal = dynamic_cast<ASTNodeLiteral *>(evaluatedValue.get());
|
||||
auto type = dynamic_cast<ASTNodeBuiltinType *>(evaluatedType.get())->getType();
|
||||
|
||||
auto typePatterns = this->m_type->createPatterns(evaluator);
|
||||
auto &typePattern = typePatterns.front();
|
||||
|
||||
return std::unique_ptr<ASTNode>(std::visit(overloaded {
|
||||
[&, this](Pattern *value) -> ASTNode * { LogConsole::abortEvaluation(hex::format("cannot cast custom type '{}' to '{}'", value->getTypeName(), Token::getTypeName(type)), this); },
|
||||
[&, this](const std::string &) -> ASTNode * { LogConsole::abortEvaluation(hex::format("cannot cast string to '{}'", Token::getTypeName(type)), this); },
|
||||
[&, this](auto &&value) -> ASTNode * {
|
||||
auto endianAdjustedValue = hex::changeEndianess(value, typePattern->getSize(), typePattern->getEndian());
|
||||
switch (type) {
|
||||
case Token::ValueType::Unsigned8Bit:
|
||||
return new ASTNodeLiteral(u128(u8(endianAdjustedValue)));
|
||||
case Token::ValueType::Unsigned16Bit:
|
||||
return new ASTNodeLiteral(u128(u16(endianAdjustedValue)));
|
||||
case Token::ValueType::Unsigned32Bit:
|
||||
return new ASTNodeLiteral(u128(u32(endianAdjustedValue)));
|
||||
case Token::ValueType::Unsigned64Bit:
|
||||
return new ASTNodeLiteral(u128(u64(endianAdjustedValue)));
|
||||
case Token::ValueType::Unsigned128Bit:
|
||||
return new ASTNodeLiteral(u128(endianAdjustedValue));
|
||||
case Token::ValueType::Signed8Bit:
|
||||
return new ASTNodeLiteral(i128(i8(endianAdjustedValue)));
|
||||
case Token::ValueType::Signed16Bit:
|
||||
return new ASTNodeLiteral(i128(i16(endianAdjustedValue)));
|
||||
case Token::ValueType::Signed32Bit:
|
||||
return new ASTNodeLiteral(i128(i32(endianAdjustedValue)));
|
||||
case Token::ValueType::Signed64Bit:
|
||||
return new ASTNodeLiteral(i128(i64(endianAdjustedValue)));
|
||||
case Token::ValueType::Signed128Bit:
|
||||
return new ASTNodeLiteral(i128(endianAdjustedValue));
|
||||
case Token::ValueType::Float:
|
||||
return new ASTNodeLiteral(double(float(endianAdjustedValue)));
|
||||
case Token::ValueType::Double:
|
||||
return new ASTNodeLiteral(double(endianAdjustedValue));
|
||||
case Token::ValueType::Character:
|
||||
return new ASTNodeLiteral(char(endianAdjustedValue));
|
||||
case Token::ValueType::Character16:
|
||||
return new ASTNodeLiteral(u128(char16_t(endianAdjustedValue)));
|
||||
case Token::ValueType::Boolean:
|
||||
return new ASTNodeLiteral(bool(endianAdjustedValue));
|
||||
case Token::ValueType::String:
|
||||
{
|
||||
std::string string(sizeof(value), '\x00');
|
||||
std::memcpy(string.data(), &value, string.size());
|
||||
hex::trim(string);
|
||||
|
||||
if (typePattern->getEndian() != std::endian::native)
|
||||
std::reverse(string.begin(), string.end());
|
||||
|
||||
return new ASTNodeLiteral(string);
|
||||
}
|
||||
default:
|
||||
LogConsole::abortEvaluation(hex::format("cannot cast value to '{}'", Token::getTypeName(type)), this);
|
||||
}
|
||||
},
|
||||
},
|
||||
literal->getValue()));
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<ASTNode> m_value;
|
||||
std::unique_ptr<ASTNode> m_type;
|
||||
};
|
||||
|
||||
}
|
@ -1,80 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeCompoundStatement : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeCompoundStatement(std::vector<std::unique_ptr<ASTNode>> &&statements, bool newScope = false) : m_statements(std::move(statements)), m_newScope(newScope) {
|
||||
}
|
||||
|
||||
ASTNodeCompoundStatement(const ASTNodeCompoundStatement &other) : ASTNode(other) {
|
||||
for (const auto &statement : other.m_statements) {
|
||||
this->m_statements.push_back(statement->clone());
|
||||
}
|
||||
|
||||
this->m_newScope = other.m_newScope;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeCompoundStatement(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> evaluate(Evaluator *evaluator) const override {
|
||||
std::unique_ptr<ASTNode> result = nullptr;
|
||||
|
||||
for (const auto &statement : this->m_statements) {
|
||||
result = statement->evaluate(evaluator);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<std::unique_ptr<Pattern>> createPatterns(Evaluator *evaluator) const override {
|
||||
std::vector<std::unique_ptr<Pattern>> result;
|
||||
|
||||
for (const auto &statement : this->m_statements) {
|
||||
auto patterns = statement->createPatterns(evaluator);
|
||||
std::move(patterns.begin(), patterns.end(), std::back_inserter(result));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
FunctionResult execute(Evaluator *evaluator) const override {
|
||||
FunctionResult result;
|
||||
|
||||
auto variables = *evaluator->getScope(0).scope;
|
||||
u32 startVariableCount = variables.size();
|
||||
|
||||
if (this->m_newScope) {
|
||||
evaluator->pushScope(nullptr, variables);
|
||||
}
|
||||
|
||||
for (const auto &statement : this->m_statements) {
|
||||
result = statement->execute(evaluator);
|
||||
if (evaluator->getCurrentControlFlowStatement() != ControlFlowStatement::None)
|
||||
return result;
|
||||
}
|
||||
|
||||
if (this->m_newScope) {
|
||||
i64 stackSize = evaluator->getStack().size();
|
||||
for (u32 i = startVariableCount; i < variables.size(); i++) {
|
||||
stackSize--;
|
||||
}
|
||||
if (stackSize < 0) LogConsole::abortEvaluation("stack pointer underflow!", this);
|
||||
evaluator->getStack().resize(stackSize);
|
||||
|
||||
evaluator->popScope();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public:
|
||||
std::vector<std::unique_ptr<ASTNode>> m_statements;
|
||||
bool m_newScope = false;
|
||||
};
|
||||
|
||||
}
|
@ -1,92 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeConditionalStatement : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeConditionalStatement(std::unique_ptr<ASTNode> condition, std::vector<std::unique_ptr<ASTNode>> &&trueBody, std::vector<std::unique_ptr<ASTNode>> &&falseBody)
|
||||
: ASTNode(), m_condition(std::move(condition)), m_trueBody(std::move(trueBody)), m_falseBody(std::move(falseBody)) { }
|
||||
|
||||
|
||||
ASTNodeConditionalStatement(const ASTNodeConditionalStatement &other) : ASTNode(other) {
|
||||
this->m_condition = other.m_condition->clone();
|
||||
|
||||
for (auto &statement : other.m_trueBody)
|
||||
this->m_trueBody.push_back(statement->clone());
|
||||
for (auto &statement : other.m_falseBody)
|
||||
this->m_falseBody.push_back(statement->clone());
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeConditionalStatement(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<std::unique_ptr<Pattern>> createPatterns(Evaluator *evaluator) const override {
|
||||
auto &scope = *evaluator->getScope(0).scope;
|
||||
auto &body = evaluateCondition(evaluator) ? this->m_trueBody : this->m_falseBody;
|
||||
|
||||
for (auto &node : body) {
|
||||
auto newPatterns = node->createPatterns(evaluator);
|
||||
for (auto &pattern : newPatterns) {
|
||||
scope.push_back(std::move(pattern));
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::unique_ptr<ASTNode> &getCondition() {
|
||||
return this->m_condition;
|
||||
}
|
||||
|
||||
FunctionResult execute(Evaluator *evaluator) const override {
|
||||
auto &body = evaluateCondition(evaluator) ? this->m_trueBody : this->m_falseBody;
|
||||
|
||||
auto variables = *evaluator->getScope(0).scope;
|
||||
auto parameterPack = evaluator->getScope(0).parameterPack;
|
||||
|
||||
u32 startVariableCount = variables.size();
|
||||
ON_SCOPE_EXIT {
|
||||
i64 stackSize = evaluator->getStack().size();
|
||||
for (u32 i = startVariableCount; i < variables.size(); i++) {
|
||||
stackSize--;
|
||||
}
|
||||
if (stackSize < 0) LogConsole::abortEvaluation("stack pointer underflow!", this);
|
||||
evaluator->getStack().resize(stackSize);
|
||||
};
|
||||
|
||||
evaluator->pushScope(nullptr, variables);
|
||||
evaluator->getScope(0).parameterPack = parameterPack;
|
||||
ON_SCOPE_EXIT {
|
||||
evaluator->popScope();
|
||||
};
|
||||
|
||||
for (auto &statement : body) {
|
||||
auto result = statement->execute(evaluator);
|
||||
if (auto ctrlStatement = evaluator->getCurrentControlFlowStatement(); ctrlStatement != ControlFlowStatement::None) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
private:
|
||||
[[nodiscard]] bool evaluateCondition(Evaluator *evaluator) const {
|
||||
const auto node = this->m_condition->evaluate(evaluator);
|
||||
const auto literal = dynamic_cast<ASTNodeLiteral *>(node.get());
|
||||
|
||||
return std::visit(overloaded {
|
||||
[](const std::string &value) -> bool { return !value.empty(); },
|
||||
[this](Pattern *const &) -> bool { LogConsole::abortEvaluation("cannot cast custom type to bool", this); },
|
||||
[](auto &&value) -> bool { return value != 0; } },
|
||||
literal->getValue());
|
||||
}
|
||||
|
||||
std::unique_ptr<ASTNode> m_condition;
|
||||
std::vector<std::unique_ptr<ASTNode>> m_trueBody, m_falseBody;
|
||||
};
|
||||
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeControlFlowStatement : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeControlFlowStatement(ControlFlowStatement type, std::unique_ptr<ASTNode> &&rvalue) : m_type(type), m_rvalue(std::move(rvalue)) {
|
||||
}
|
||||
|
||||
ASTNodeControlFlowStatement(const ASTNodeControlFlowStatement &other) : ASTNode(other) {
|
||||
this->m_type = other.m_type;
|
||||
|
||||
if (other.m_rvalue != nullptr)
|
||||
this->m_rvalue = other.m_rvalue->clone();
|
||||
else
|
||||
this->m_rvalue = nullptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeControlFlowStatement(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<std::unique_ptr<Pattern>> createPatterns(Evaluator *evaluator) const override {
|
||||
|
||||
this->execute(evaluator);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
FunctionResult execute(Evaluator *evaluator) const override {
|
||||
if (this->m_rvalue == nullptr) {
|
||||
evaluator->setCurrentControlFlowStatement(this->m_type);
|
||||
return std::nullopt;
|
||||
} else {
|
||||
auto returnValue = this->m_rvalue->evaluate(evaluator);
|
||||
auto literal = dynamic_cast<ASTNodeLiteral *>(returnValue.get());
|
||||
|
||||
evaluator->setCurrentControlFlowStatement(this->m_type);
|
||||
|
||||
if (literal == nullptr)
|
||||
return std::nullopt;
|
||||
else
|
||||
return literal->getValue();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
ControlFlowStatement m_type;
|
||||
std::unique_ptr<ASTNode> m_rvalue;
|
||||
};
|
||||
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_attribute.hpp>
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern_enum.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeEnum : public ASTNode,
|
||||
public Attributable {
|
||||
public:
|
||||
explicit ASTNodeEnum(std::unique_ptr<ASTNode> &&underlyingType) : ASTNode(), m_underlyingType(std::move(underlyingType)) { }
|
||||
|
||||
ASTNodeEnum(const ASTNodeEnum &other) : ASTNode(other), Attributable(other) {
|
||||
for (const auto &[name, entry] : other.getEntries())
|
||||
this->m_entries.emplace(name, entry->clone());
|
||||
this->m_underlyingType = other.m_underlyingType->clone();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeEnum(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<std::unique_ptr<Pattern>> createPatterns(Evaluator *evaluator) const override {
|
||||
auto pattern = std::make_unique<PatternEnum>(evaluator, evaluator->dataOffset(), 0);
|
||||
|
||||
std::vector<std::pair<Token::Literal, std::string>> enumEntries;
|
||||
for (const auto &[name, value] : this->m_entries) {
|
||||
const auto node = value->evaluate(evaluator);
|
||||
auto literal = dynamic_cast<ASTNodeLiteral *>(node.get());
|
||||
|
||||
enumEntries.emplace_back(literal->getValue(), name);
|
||||
}
|
||||
|
||||
pattern->setEnumValues(enumEntries);
|
||||
|
||||
const auto nodes = this->m_underlyingType->createPatterns(evaluator);
|
||||
auto &underlying = nodes.front();
|
||||
|
||||
pattern->setSize(underlying->getSize());
|
||||
pattern->setEndian(underlying->getEndian());
|
||||
|
||||
applyTypeAttributes(evaluator, this, pattern.get());
|
||||
|
||||
return hex::moveToVector<std::unique_ptr<Pattern>>(std::move(pattern));
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::map<std::string, std::unique_ptr<ASTNode>> &getEntries() const { return this->m_entries; }
|
||||
void addEntry(const std::string &name, std::unique_ptr<ASTNode> &&expression) { this->m_entries.insert({ name, std::move(expression) }); }
|
||||
|
||||
[[nodiscard]] const std::unique_ptr<ASTNode> &getUnderlyingType() { return this->m_underlyingType; }
|
||||
|
||||
private:
|
||||
std::map<std::string, std::unique_ptr<ASTNode>> m_entries;
|
||||
std::unique_ptr<ASTNode> m_underlyingType;
|
||||
};
|
||||
|
||||
}
|
@ -1,137 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_parameter_pack.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_mathematical_expression.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_literal.hpp>
|
||||
|
||||
#include <thread>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeFunctionCall : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeFunctionCall(std::string functionName, std::vector<std::unique_ptr<ASTNode>> &¶ms)
|
||||
: ASTNode(), m_functionName(std::move(functionName)), m_params(std::move(params)) { }
|
||||
|
||||
ASTNodeFunctionCall(const ASTNodeFunctionCall &other) : ASTNode(other) {
|
||||
this->m_functionName = other.m_functionName;
|
||||
|
||||
for (auto ¶m : other.m_params)
|
||||
this->m_params.push_back(param->clone());
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeFunctionCall(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::string &getFunctionName() {
|
||||
return this->m_functionName;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::vector<std::unique_ptr<ASTNode>> &getParams() const {
|
||||
return this->m_params;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<std::unique_ptr<Pattern>> createPatterns(Evaluator *evaluator) const override {
|
||||
|
||||
this->execute(evaluator);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> evaluate(Evaluator *evaluator) const override {
|
||||
auto startOffset = evaluator->dataOffset();
|
||||
ON_SCOPE_EXIT { evaluator->dataOffset() = startOffset; };
|
||||
|
||||
std::vector<Token::Literal> evaluatedParams;
|
||||
for (auto ¶m : this->m_params) {
|
||||
const auto expression = param->evaluate(evaluator)->evaluate(evaluator);
|
||||
|
||||
if (auto literal = dynamic_cast<ASTNodeLiteral *>(expression.get())) {
|
||||
evaluatedParams.push_back(literal->getValue());
|
||||
} else if (auto parameterPack = dynamic_cast<ASTNodeParameterPack *>(expression.get())) {
|
||||
for (auto &value : parameterPack->getValues()) {
|
||||
evaluatedParams.push_back(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto &customFunctions = evaluator->getCustomFunctions();
|
||||
auto functions = ContentRegistry::PatternLanguage::getFunctions();
|
||||
|
||||
for (auto &func : customFunctions)
|
||||
functions.insert(func);
|
||||
|
||||
if (!functions.contains(this->m_functionName)) {
|
||||
if (this->m_functionName.starts_with("std::")) {
|
||||
evaluator->getConsole().log(LogConsole::Level::Warning, "This function might be part of the standard library.\nYou can install the standard library though\nthe Content Store found under Help -> Content Store and then\ninclude the correct file.");
|
||||
}
|
||||
|
||||
LogConsole::abortEvaluation(hex::format("call to unknown function '{}'", this->m_functionName), this);
|
||||
}
|
||||
|
||||
using namespace ContentRegistry::PatternLanguage;
|
||||
|
||||
auto function = functions[this->m_functionName];
|
||||
const auto &[min, max] = function.parameterCount;
|
||||
|
||||
if (evaluatedParams.size() >= min && evaluatedParams.size() < max) {
|
||||
while (true) {
|
||||
auto remainingParams = max - evaluatedParams.size();
|
||||
if (remainingParams <= 0)
|
||||
break;
|
||||
|
||||
auto offset = evaluatedParams.size() - min;
|
||||
if (offset >= function.defaultParameters.size())
|
||||
break;
|
||||
|
||||
evaluatedParams.push_back(function.defaultParameters[offset]);
|
||||
}
|
||||
}
|
||||
|
||||
if (evaluatedParams.size() < min)
|
||||
LogConsole::abortEvaluation(hex::format("too few parameters for function '{0}'. Expected {1} at least", this->m_functionName, min), this);
|
||||
else if (evaluatedParams.size() > max)
|
||||
LogConsole::abortEvaluation(hex::format("too many parameters for function '{0}'. Expected {1} at most", this->m_functionName, max), this);
|
||||
|
||||
try {
|
||||
if (function.dangerous && evaluator->getDangerousFunctionPermission() != DangerousFunctionPermission::Allow) {
|
||||
evaluator->dangerousFunctionCalled();
|
||||
|
||||
while (evaluator->getDangerousFunctionPermission() == DangerousFunctionPermission::Ask) {
|
||||
using namespace std::literals::chrono_literals;
|
||||
|
||||
std::this_thread::sleep_for(100ms);
|
||||
}
|
||||
|
||||
if (evaluator->getDangerousFunctionPermission() == DangerousFunctionPermission::Deny) {
|
||||
LogConsole::abortEvaluation(hex::format("calling of dangerous function '{}' is not allowed", this->m_functionName), this);
|
||||
}
|
||||
}
|
||||
|
||||
auto result = function.func(evaluator, evaluatedParams);
|
||||
|
||||
if (result.has_value())
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeLiteral(std::move(result.value())));
|
||||
else
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeMathematicalExpression(nullptr, nullptr, Token::Operator::Plus));
|
||||
} catch (std::string &error) {
|
||||
LogConsole::abortEvaluation(error, this);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FunctionResult execute(Evaluator *evaluator) const override {
|
||||
(void)this->evaluate(evaluator);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_functionName;
|
||||
std::vector<std::unique_ptr<ASTNode>> m_params;
|
||||
};
|
||||
|
||||
}
|
@ -1,129 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeFunctionDefinition : public ASTNode {
|
||||
public:
|
||||
ASTNodeFunctionDefinition(std::string name, std::vector<std::pair<std::string, std::unique_ptr<ASTNode>>> &¶ms, std::vector<std::unique_ptr<ASTNode>> &&body, std::optional<std::string> parameterPack, std::vector<std::unique_ptr<ASTNode>> &&defaultParameters)
|
||||
: m_name(std::move(name)), m_params(std::move(params)), m_body(std::move(body)), m_parameterPack(std::move(parameterPack)), m_defaultParameters(std::move(defaultParameters)) {
|
||||
}
|
||||
|
||||
ASTNodeFunctionDefinition(const ASTNodeFunctionDefinition &other) : ASTNode(other) {
|
||||
this->m_name = other.m_name;
|
||||
this->m_parameterPack = other.m_parameterPack;
|
||||
|
||||
for (const auto &[name, type] : other.m_params) {
|
||||
this->m_params.emplace_back(name, type->clone());
|
||||
}
|
||||
|
||||
for (auto &statement : other.m_body) {
|
||||
this->m_body.push_back(statement->clone());
|
||||
}
|
||||
|
||||
for (auto &statement : other.m_defaultParameters) {
|
||||
this->m_body.push_back(statement->clone());
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeFunctionDefinition(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::string &getName() const {
|
||||
return this->m_name;
|
||||
}
|
||||
|
||||
[[nodiscard]] const auto &getParams() const {
|
||||
return this->m_params;
|
||||
}
|
||||
|
||||
[[nodiscard]] const auto &getBody() const {
|
||||
return this->m_body;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> evaluate(Evaluator *evaluator) const override {
|
||||
using namespace ContentRegistry::PatternLanguage;
|
||||
|
||||
ParameterCount paramCount;
|
||||
|
||||
if (this->m_parameterPack.has_value() && !this->m_defaultParameters.empty())
|
||||
paramCount = ParameterCount::atLeast(this->m_params.size() - this->m_defaultParameters.size());
|
||||
else if (this->m_parameterPack.has_value())
|
||||
paramCount = ParameterCount::atLeast(this->m_params.size());
|
||||
else if (!this->m_defaultParameters.empty())
|
||||
paramCount = ParameterCount::between(this->m_params.size() - this->m_defaultParameters.size(), this->m_params.size());
|
||||
else
|
||||
paramCount = ParameterCount::exactly(this->m_params.size());
|
||||
|
||||
std::vector<Token::Literal> evaluatedDefaultParams;
|
||||
for (const auto ¶m : this->m_defaultParameters) {
|
||||
const auto expression = param->evaluate(evaluator)->evaluate(evaluator);
|
||||
|
||||
if (auto literal = dynamic_cast<ASTNodeLiteral *>(expression.get())) {
|
||||
evaluatedDefaultParams.push_back(literal->getValue());
|
||||
} else {
|
||||
LogConsole::abortEvaluation(hex::format("invalid default parameter for function {}", this->m_name), expression);
|
||||
}
|
||||
}
|
||||
|
||||
evaluator->addCustomFunction(this->m_name, paramCount, evaluatedDefaultParams, [this](Evaluator *ctx, const std::vector<Token::Literal> ¶ms) -> std::optional<Token::Literal> {
|
||||
std::vector<std::shared_ptr<Pattern>> variables;
|
||||
|
||||
auto startOffset = ctx->dataOffset();
|
||||
ctx->pushScope(nullptr, variables);
|
||||
ON_SCOPE_EXIT {
|
||||
ctx->popScope();
|
||||
ctx->dataOffset() = startOffset;
|
||||
};
|
||||
|
||||
if (this->m_parameterPack.has_value()) {
|
||||
std::vector<Token::Literal> parameterPackContent;
|
||||
for (u32 paramIndex = this->m_params.size(); paramIndex < params.size(); paramIndex++)
|
||||
parameterPackContent.push_back(params[paramIndex]);
|
||||
|
||||
ctx->createParameterPack(this->m_parameterPack.value(), parameterPackContent);
|
||||
}
|
||||
|
||||
for (u32 paramIndex = 0; paramIndex < this->m_params.size() && paramIndex < params.size(); paramIndex++) {
|
||||
const auto &[name, type] = this->m_params[paramIndex];
|
||||
|
||||
ctx->createVariable(name, type.get(), params[paramIndex]);
|
||||
ctx->setVariable(name, params[paramIndex]);
|
||||
}
|
||||
|
||||
for (auto &statement : this->m_body) {
|
||||
auto result = statement->execute(ctx);
|
||||
|
||||
if (ctx->getCurrentControlFlowStatement() != ControlFlowStatement::None) {
|
||||
switch (ctx->getCurrentControlFlowStatement()) {
|
||||
case ControlFlowStatement::Break:
|
||||
LogConsole::abortEvaluation("break statement not within a loop", statement);
|
||||
case ControlFlowStatement::Continue:
|
||||
LogConsole::abortEvaluation("continue statement not within a loop", statement);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ctx->setCurrentControlFlowStatement(ControlFlowStatement::None);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
});
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
std::vector<std::pair<std::string, std::unique_ptr<ASTNode>>> m_params;
|
||||
std::vector<std::unique_ptr<ASTNode>> m_body;
|
||||
std::optional<std::string> m_parameterPack;
|
||||
std::vector<std::unique_ptr<ASTNode>> m_defaultParameters;
|
||||
};
|
||||
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeLiteral : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeLiteral(Token::Literal literal) : ASTNode(), m_literal(std::move(literal)) { }
|
||||
|
||||
ASTNodeLiteral(const ASTNodeLiteral &) = default;
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeLiteral(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] const auto &getValue() const {
|
||||
return this->m_literal;
|
||||
}
|
||||
|
||||
private:
|
||||
Token::Literal m_literal;
|
||||
};
|
||||
|
||||
}
|
@ -1,216 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
#define FLOAT_BIT_OPERATION(name) \
|
||||
auto name(hex::floating_point auto left, auto right) const { \
|
||||
hex::unused(left, right); \
|
||||
LogConsole::abortEvaluation("invalid floating point operation", this); \
|
||||
return 0; \
|
||||
} \
|
||||
auto name(auto left, hex::floating_point auto right) const { \
|
||||
hex::unused(left, right); \
|
||||
LogConsole::abortEvaluation("invalid floating point operation", this); \
|
||||
return 0; \
|
||||
} \
|
||||
auto name(hex::floating_point auto left, hex::floating_point auto right) const { \
|
||||
hex::unused(left, right); \
|
||||
LogConsole::abortEvaluation("invalid floating point operation", this); \
|
||||
return 0; \
|
||||
} \
|
||||
auto name(hex::integral auto left, hex::integral auto right) const
|
||||
|
||||
class ASTNodeMathematicalExpression : public ASTNode {
|
||||
|
||||
FLOAT_BIT_OPERATION(shiftLeft) {
|
||||
return left << right;
|
||||
}
|
||||
|
||||
FLOAT_BIT_OPERATION(shiftRight) {
|
||||
return left >> right;
|
||||
}
|
||||
|
||||
FLOAT_BIT_OPERATION(bitAnd) {
|
||||
return left & right;
|
||||
}
|
||||
|
||||
FLOAT_BIT_OPERATION(bitOr) {
|
||||
return left | right;
|
||||
}
|
||||
|
||||
FLOAT_BIT_OPERATION(bitXor) {
|
||||
return left ^ right;
|
||||
}
|
||||
|
||||
FLOAT_BIT_OPERATION(bitNot) {
|
||||
hex::unused(left);
|
||||
|
||||
return ~static_cast<u128>(right);
|
||||
}
|
||||
|
||||
FLOAT_BIT_OPERATION(modulus) {
|
||||
return left % right;
|
||||
}
|
||||
|
||||
#undef FLOAT_BIT_OPERATION
|
||||
public:
|
||||
ASTNodeMathematicalExpression(std::unique_ptr<ASTNode> &&left, std::unique_ptr<ASTNode> &&right, Token::Operator op)
|
||||
: ASTNode(), m_left(std::move(left)), m_right(std::move(right)), m_operator(op) { }
|
||||
|
||||
ASTNodeMathematicalExpression(const ASTNodeMathematicalExpression &other) : ASTNode(other) {
|
||||
this->m_operator = other.m_operator;
|
||||
this->m_left = other.m_left->clone();
|
||||
this->m_right = other.m_right->clone();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeMathematicalExpression(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> evaluate(Evaluator *evaluator) const override {
|
||||
if (this->getLeftOperand() == nullptr || this->getRightOperand() == nullptr)
|
||||
LogConsole::abortEvaluation("attempted to use void expression in mathematical expression", this);
|
||||
|
||||
auto leftValue = this->getLeftOperand()->evaluate(evaluator);
|
||||
auto rightValue = this->getRightOperand()->evaluate(evaluator);
|
||||
|
||||
auto *left = dynamic_cast<ASTNodeLiteral *>(leftValue.get());
|
||||
auto *right = dynamic_cast<ASTNodeLiteral *>(rightValue.get());
|
||||
|
||||
return std::unique_ptr<ASTNode>(std::visit(overloaded {
|
||||
// TODO: :notlikethis:
|
||||
[this](u128, Pattern *const ) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
|
||||
[this](i128, Pattern *const &) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
|
||||
[this](double, Pattern *const &) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
|
||||
[this](char, Pattern *const &) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
|
||||
[this](bool, Pattern *const &) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
|
||||
[this](const std::string &, Pattern *const &) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
|
||||
[this](Pattern *const &, u128) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
|
||||
[this](Pattern *const &, i128) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
|
||||
[this](Pattern *const &, double) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
|
||||
[this](Pattern *const &, char) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
|
||||
[this](Pattern *const &, bool) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
|
||||
[this](Pattern *const &, const std::string &) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
|
||||
[this](Pattern *const &, Pattern *const &) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
|
||||
|
||||
[this](auto &&, const std::string &) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
|
||||
[this](const std::string &left, auto &&right) -> ASTNode * {
|
||||
switch (this->getOperator()) {
|
||||
case Token::Operator::Star:
|
||||
{
|
||||
if (static_cast<i128>(right) < 0)
|
||||
LogConsole::abortEvaluation("cannot repeat string a negative number of times", this);
|
||||
|
||||
std::string result;
|
||||
for (u128 i = 0; i < static_cast<u128>(right); i++)
|
||||
result += left;
|
||||
return new ASTNodeLiteral(result);
|
||||
}
|
||||
default:
|
||||
LogConsole::abortEvaluation("invalid operand used in mathematical expression", this);
|
||||
}
|
||||
},
|
||||
[this](const std::string &left, const std::string &right) -> ASTNode * {
|
||||
switch (this->getOperator()) {
|
||||
case Token::Operator::Plus:
|
||||
return new ASTNodeLiteral(left + right);
|
||||
case Token::Operator::BoolEquals:
|
||||
return new ASTNodeLiteral(left == right);
|
||||
case Token::Operator::BoolNotEquals:
|
||||
return new ASTNodeLiteral(left != right);
|
||||
case Token::Operator::BoolGreaterThan:
|
||||
return new ASTNodeLiteral(left > right);
|
||||
case Token::Operator::BoolLessThan:
|
||||
return new ASTNodeLiteral(left < right);
|
||||
case Token::Operator::BoolGreaterThanOrEquals:
|
||||
return new ASTNodeLiteral(left >= right);
|
||||
case Token::Operator::BoolLessThanOrEquals:
|
||||
return new ASTNodeLiteral(left <= right);
|
||||
default:
|
||||
LogConsole::abortEvaluation("invalid operand used in mathematical expression", this);
|
||||
}
|
||||
},
|
||||
[this](const std::string &left, char right) -> ASTNode * {
|
||||
switch (this->getOperator()) {
|
||||
case Token::Operator::Plus:
|
||||
return new ASTNodeLiteral(left + right);
|
||||
default:
|
||||
LogConsole::abortEvaluation("invalid operand used in mathematical expression", this);
|
||||
}
|
||||
},
|
||||
[this](char left, const std::string &right) -> ASTNode * {
|
||||
switch (this->getOperator()) {
|
||||
case Token::Operator::Plus:
|
||||
return new ASTNodeLiteral(left + right);
|
||||
default:
|
||||
LogConsole::abortEvaluation("invalid operand used in mathematical expression", this);
|
||||
}
|
||||
},
|
||||
[this](auto &&left, auto &&right) -> ASTNode * {
|
||||
switch (this->getOperator()) {
|
||||
case Token::Operator::Plus:
|
||||
return new ASTNodeLiteral(left + right);
|
||||
case Token::Operator::Minus:
|
||||
return new ASTNodeLiteral(left - right);
|
||||
case Token::Operator::Star:
|
||||
return new ASTNodeLiteral(left * right);
|
||||
case Token::Operator::Slash:
|
||||
if (right == 0) LogConsole::abortEvaluation("division by zero!", this);
|
||||
return new ASTNodeLiteral(left / right);
|
||||
case Token::Operator::Percent:
|
||||
if (right == 0) LogConsole::abortEvaluation("division by zero!", this);
|
||||
return new ASTNodeLiteral(modulus(left, right));
|
||||
case Token::Operator::ShiftLeft:
|
||||
return new ASTNodeLiteral(shiftLeft(left, right));
|
||||
case Token::Operator::ShiftRight:
|
||||
return new ASTNodeLiteral(shiftRight(left, right));
|
||||
case Token::Operator::BitAnd:
|
||||
return new ASTNodeLiteral(bitAnd(left, right));
|
||||
case Token::Operator::BitXor:
|
||||
return new ASTNodeLiteral(bitXor(left, right));
|
||||
case Token::Operator::BitOr:
|
||||
return new ASTNodeLiteral(bitOr(left, right));
|
||||
case Token::Operator::BitNot:
|
||||
return new ASTNodeLiteral(bitNot(left, right));
|
||||
case Token::Operator::BoolEquals:
|
||||
return new ASTNodeLiteral(bool(left == static_cast<decltype(left)>(right)));
|
||||
case Token::Operator::BoolNotEquals:
|
||||
return new ASTNodeLiteral(bool(left != static_cast<decltype(left)>(right)));
|
||||
case Token::Operator::BoolGreaterThan:
|
||||
return new ASTNodeLiteral(bool(left > static_cast<decltype(left)>(right)));
|
||||
case Token::Operator::BoolLessThan:
|
||||
return new ASTNodeLiteral(bool(left < static_cast<decltype(left)>(right)));
|
||||
case Token::Operator::BoolGreaterThanOrEquals:
|
||||
return new ASTNodeLiteral(bool(left >= static_cast<decltype(left)>(right)));
|
||||
case Token::Operator::BoolLessThanOrEquals:
|
||||
return new ASTNodeLiteral(bool(left <= static_cast<decltype(left)>(right)));
|
||||
case Token::Operator::BoolAnd:
|
||||
return new ASTNodeLiteral(bool(left && right));
|
||||
case Token::Operator::BoolXor:
|
||||
return new ASTNodeLiteral(bool((left && !right) || (!left && right)));
|
||||
case Token::Operator::BoolOr:
|
||||
return new ASTNodeLiteral(bool(left || right));
|
||||
case Token::Operator::BoolNot:
|
||||
return new ASTNodeLiteral(bool(!right));
|
||||
default:
|
||||
LogConsole::abortEvaluation("invalid operand used in mathematical expression", this);
|
||||
}
|
||||
} },
|
||||
left->getValue(),
|
||||
right->getValue()));
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::unique_ptr<ASTNode> &getLeftOperand() const { return this->m_left; }
|
||||
[[nodiscard]] const std::unique_ptr<ASTNode> &getRightOperand() const { return this->m_right; }
|
||||
[[nodiscard]] Token::Operator getOperator() const { return this->m_operator; }
|
||||
|
||||
private:
|
||||
std::unique_ptr<ASTNode> m_left, m_right;
|
||||
Token::Operator m_operator;
|
||||
};
|
||||
|
||||
#undef FLOAT_BIT_OPERATION
|
||||
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node_variable_decl.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeMultiVariableDecl : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeMultiVariableDecl(std::vector<std::unique_ptr<ASTNode>> &&variables) : m_variables(std::move(variables)) { }
|
||||
|
||||
ASTNodeMultiVariableDecl(const ASTNodeMultiVariableDecl &other) : ASTNode(other) {
|
||||
for (auto &variable : other.m_variables)
|
||||
this->m_variables.push_back(variable->clone());
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeMultiVariableDecl(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::vector<std::unique_ptr<ASTNode>> &getVariables() {
|
||||
return this->m_variables;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<std::unique_ptr<Pattern>> createPatterns(Evaluator *evaluator) const override {
|
||||
std::vector<std::unique_ptr<Pattern>> patterns;
|
||||
|
||||
for (auto &node : this->m_variables) {
|
||||
auto newPatterns = node->createPatterns(evaluator);
|
||||
std::move(newPatterns.begin(), newPatterns.end(), std::back_inserter(patterns));
|
||||
}
|
||||
|
||||
return patterns;
|
||||
}
|
||||
|
||||
FunctionResult execute(Evaluator *evaluator) const override {
|
||||
for (auto &variable : this->m_variables) {
|
||||
auto variableDecl = dynamic_cast<ASTNodeVariableDecl *>(variable.get());
|
||||
auto variableType = variableDecl->getType()->evaluate(evaluator);
|
||||
|
||||
evaluator->createVariable(variableDecl->getName(), variableType.get());
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<ASTNode>> m_variables;
|
||||
};
|
||||
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeParameterPack : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeParameterPack(std::vector<Token::Literal> &&values) : m_values(std::move(values)) { }
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeParameterPack(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::vector<Token::Literal> &getValues() const {
|
||||
return this->m_values;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<Token::Literal> m_values;
|
||||
};
|
||||
|
||||
}
|
@ -1,95 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_attribute.hpp>
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern_pointer.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodePointerVariableDecl : public ASTNode,
|
||||
public Attributable {
|
||||
public:
|
||||
ASTNodePointerVariableDecl(std::string name, std::shared_ptr<ASTNodeTypeDecl> type, std::shared_ptr<ASTNodeTypeDecl> sizeType, std::unique_ptr<ASTNode> &&placementOffset = nullptr)
|
||||
: ASTNode(), m_name(std::move(name)), m_type(std::move(type)), m_sizeType(std::move(sizeType)), m_placementOffset(std::move(placementOffset)) { }
|
||||
|
||||
ASTNodePointerVariableDecl(const ASTNodePointerVariableDecl &other) : ASTNode(other), Attributable(other) {
|
||||
this->m_name = other.m_name;
|
||||
this->m_type = other.m_type;
|
||||
this->m_sizeType = other.m_sizeType;
|
||||
|
||||
if (other.m_placementOffset != nullptr)
|
||||
this->m_placementOffset = other.m_placementOffset->clone();
|
||||
else
|
||||
this->m_placementOffset = nullptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodePointerVariableDecl(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::string &getName() const { return this->m_name; }
|
||||
[[nodiscard]] constexpr const std::shared_ptr<ASTNodeTypeDecl> &getType() const { return this->m_type; }
|
||||
[[nodiscard]] constexpr const std::shared_ptr<ASTNodeTypeDecl> &getSizeType() const { return this->m_sizeType; }
|
||||
[[nodiscard]] constexpr const std::unique_ptr<ASTNode> &getPlacementOffset() const { return this->m_placementOffset; }
|
||||
|
||||
[[nodiscard]] std::vector<std::unique_ptr<Pattern>> createPatterns(Evaluator *evaluator) const override {
|
||||
auto startOffset = evaluator->dataOffset();
|
||||
|
||||
if (this->m_placementOffset != nullptr) {
|
||||
const auto node = this->m_placementOffset->evaluate(evaluator);
|
||||
const auto offset = dynamic_cast<ASTNodeLiteral *>(node.get());
|
||||
|
||||
evaluator->dataOffset() = std::visit(overloaded {
|
||||
[this](const std::string &) -> u64 { LogConsole::abortEvaluation("placement offset cannot be a string", this); },
|
||||
[this](const std::shared_ptr<Pattern> &) -> u64 { LogConsole::abortEvaluation("placement offset cannot be a custom type", this); },
|
||||
[](auto &&offset) -> u64 { return u64(offset); } },
|
||||
offset->getValue());
|
||||
}
|
||||
|
||||
auto pointerStartOffset = evaluator->dataOffset();
|
||||
|
||||
const auto sizePatterns = this->m_sizeType->createPatterns(evaluator);
|
||||
const auto &sizePattern = sizePatterns.front();
|
||||
|
||||
auto pattern = std::make_unique<PatternPointer>(evaluator, pointerStartOffset, sizePattern->getSize());
|
||||
pattern->setVariableName(this->m_name);
|
||||
|
||||
auto pointerEndOffset = evaluator->dataOffset();
|
||||
|
||||
{
|
||||
u128 pointerAddress = 0;
|
||||
evaluator->getProvider()->read(pattern->getOffset(), &pointerAddress, pattern->getSize());
|
||||
pointerAddress = hex::changeEndianess(pointerAddress, sizePattern->getSize(), sizePattern->getEndian());
|
||||
|
||||
evaluator->dataOffset() = pointerStartOffset;
|
||||
|
||||
pattern->setPointedAtAddress(pointerAddress);
|
||||
applyVariableAttributes(evaluator, this, pattern.get());
|
||||
|
||||
evaluator->dataOffset() = pattern->getPointedAtAddress();
|
||||
|
||||
auto pointedAtPatterns = this->m_type->createPatterns(evaluator);
|
||||
auto &pointedAtPattern = pointedAtPatterns.front();
|
||||
|
||||
pattern->setPointedAtPattern(std::move(pointedAtPattern));
|
||||
pattern->setEndian(sizePattern->getEndian());
|
||||
}
|
||||
|
||||
if (this->m_placementOffset != nullptr && !evaluator->isGlobalScope()) {
|
||||
evaluator->dataOffset() = startOffset;
|
||||
} else {
|
||||
evaluator->dataOffset() = pointerEndOffset;
|
||||
}
|
||||
|
||||
return hex::moveToVector<std::unique_ptr<Pattern>>(std::move(pattern));
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
std::shared_ptr<ASTNodeTypeDecl> m_type;
|
||||
std::shared_ptr<ASTNodeTypeDecl> m_sizeType;
|
||||
std::unique_ptr<ASTNode> m_placementOffset;
|
||||
};
|
||||
|
||||
}
|
@ -1,293 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_literal.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_parameter_pack.hpp>
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern_pointer.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_unsigned.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_signed.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_float.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_boolean.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_character.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_string.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_array_dynamic.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_array_static.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_struct.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_union.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_enum.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_bitfield.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeRValue : public ASTNode {
|
||||
public:
|
||||
using PathSegment = std::variant<std::string, std::unique_ptr<ASTNode>>;
|
||||
using Path = std::vector<PathSegment>;
|
||||
|
||||
explicit ASTNodeRValue(Path &&path) : ASTNode(), m_path(std::move(path)) { }
|
||||
|
||||
ASTNodeRValue(const ASTNodeRValue &other) : ASTNode(other) {
|
||||
for (auto &part : other.m_path) {
|
||||
if (auto stringPart = std::get_if<std::string>(&part); stringPart != nullptr)
|
||||
this->m_path.push_back(*stringPart);
|
||||
else if (auto nodePart = std::get_if<std::unique_ptr<ASTNode>>(&part); nodePart != nullptr)
|
||||
this->m_path.push_back((*nodePart)->clone());
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeRValue(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] const Path &getPath() const {
|
||||
return this->m_path;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> evaluate(Evaluator *evaluator) const override {
|
||||
if (this->getPath().size() == 1) {
|
||||
if (auto name = std::get_if<std::string>(&this->getPath().front()); name != nullptr) {
|
||||
if (*name == "$") return std::unique_ptr<ASTNode>(new ASTNodeLiteral(u128(evaluator->dataOffset())));
|
||||
|
||||
auto parameterPack = evaluator->getScope(0).parameterPack;
|
||||
if (parameterPack && *name == parameterPack->name)
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeParameterPack(std::move(parameterPack->values)));
|
||||
}
|
||||
}
|
||||
|
||||
auto patterns = this->createPatterns(evaluator);
|
||||
auto &pattern = patterns.front();
|
||||
|
||||
Token::Literal literal;
|
||||
if (dynamic_cast<PatternUnsigned *>(pattern.get()) || dynamic_cast<PatternEnum *>(pattern.get())) {
|
||||
u128 value = 0;
|
||||
readVariable(evaluator, value, pattern.get());
|
||||
literal = value;
|
||||
} else if (dynamic_cast<PatternSigned *>(pattern.get())) {
|
||||
i128 value = 0;
|
||||
readVariable(evaluator, value, pattern.get());
|
||||
value = hex::signExtend(pattern->getSize() * 8, value);
|
||||
literal = value;
|
||||
} else if (dynamic_cast<PatternFloat *>(pattern.get())) {
|
||||
if (pattern->getSize() == sizeof(u16)) {
|
||||
u16 value = 0;
|
||||
readVariable(evaluator, value, pattern.get());
|
||||
literal = double(float16ToFloat32(value));
|
||||
} else if (pattern->getSize() == sizeof(float)) {
|
||||
float value = 0;
|
||||
readVariable(evaluator, value, pattern.get());
|
||||
literal = double(value);
|
||||
} else if (pattern->getSize() == sizeof(double)) {
|
||||
double value = 0;
|
||||
readVariable(evaluator, value, pattern.get());
|
||||
literal = value;
|
||||
} else LogConsole::abortEvaluation("invalid floating point type access", this);
|
||||
} else if (dynamic_cast<PatternCharacter *>(pattern.get())) {
|
||||
char value = 0;
|
||||
readVariable(evaluator, value, pattern.get());
|
||||
literal = value;
|
||||
} else if (dynamic_cast<PatternBoolean *>(pattern.get())) {
|
||||
bool value = false;
|
||||
readVariable(evaluator, value, pattern.get());
|
||||
literal = value;
|
||||
} else if (dynamic_cast<PatternString *>(pattern.get())) {
|
||||
std::string value;
|
||||
|
||||
if (pattern->isLocal()) {
|
||||
auto &variableValue = evaluator->getStack()[pattern->getOffset()];
|
||||
|
||||
std::visit(overloaded {
|
||||
[&](char assignmentValue) { if (assignmentValue != 0x00) value = std::string({ assignmentValue }); },
|
||||
[&](std::string &assignmentValue) { value = assignmentValue; },
|
||||
[&, this](Pattern *const &assignmentValue) {
|
||||
if (!dynamic_cast<PatternString *>(assignmentValue) && !dynamic_cast<PatternCharacter *>(assignmentValue))
|
||||
LogConsole::abortEvaluation(hex::format("cannot assign '{}' to string", pattern->getTypeName()), this);
|
||||
|
||||
readVariable(evaluator, value, assignmentValue);
|
||||
},
|
||||
[&, this](auto &&) { LogConsole::abortEvaluation(hex::format("cannot assign '{}' to string", pattern->getTypeName()), this); } },
|
||||
variableValue);
|
||||
} else {
|
||||
value.resize(pattern->getSize());
|
||||
evaluator->getProvider()->read(pattern->getOffset(), value.data(), value.size());
|
||||
value.erase(std::find(value.begin(), value.end(), '\0'), value.end());
|
||||
}
|
||||
|
||||
literal = value;
|
||||
} else if (auto bitfieldFieldPattern = dynamic_cast<PatternBitfieldField *>(pattern.get())) {
|
||||
u64 value = 0;
|
||||
readVariable(evaluator, value, pattern.get());
|
||||
literal = u128(hex::extract(bitfieldFieldPattern->getBitOffset() + (bitfieldFieldPattern->getBitSize() - 1), bitfieldFieldPattern->getBitOffset(), value));
|
||||
} else {
|
||||
literal = pattern.get();
|
||||
}
|
||||
|
||||
if (auto transformFunc = pattern->getTransformFunction(); transformFunc.has_value()) {
|
||||
auto result = transformFunc->func(evaluator, { std::move(literal) });
|
||||
|
||||
if (!result.has_value())
|
||||
LogConsole::abortEvaluation("transform function did not return a value", this);
|
||||
literal = std::move(result.value());
|
||||
}
|
||||
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeLiteral(std::move(literal)));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<std::unique_ptr<Pattern>> createPatterns(Evaluator *evaluator) const override {
|
||||
std::vector<std::shared_ptr<Pattern>> searchScope;
|
||||
std::unique_ptr<Pattern> currPattern;
|
||||
i32 scopeIndex = 0;
|
||||
|
||||
|
||||
if (!evaluator->isGlobalScope()) {
|
||||
auto globalScope = evaluator->getGlobalScope().scope;
|
||||
std::copy(globalScope->begin(), globalScope->end(), std::back_inserter(searchScope));
|
||||
}
|
||||
|
||||
{
|
||||
auto currScope = evaluator->getScope(scopeIndex).scope;
|
||||
std::copy(currScope->begin(), currScope->end(), std::back_inserter(searchScope));
|
||||
}
|
||||
|
||||
for (const auto &part : this->getPath()) {
|
||||
|
||||
if (part.index() == 0) {
|
||||
// Variable access
|
||||
auto name = std::get<std::string>(part);
|
||||
|
||||
if (name == "parent") {
|
||||
scopeIndex--;
|
||||
|
||||
if (static_cast<size_t>(std::abs(scopeIndex)) >= evaluator->getScopeCount())
|
||||
LogConsole::abortEvaluation("cannot access parent of global scope", this);
|
||||
|
||||
searchScope = *evaluator->getScope(scopeIndex).scope;
|
||||
auto currParent = evaluator->getScope(scopeIndex).parent;
|
||||
|
||||
if (currParent == nullptr) {
|
||||
currPattern = nullptr;
|
||||
} else {
|
||||
currPattern = currParent->clone();
|
||||
}
|
||||
|
||||
continue;
|
||||
} else if (name == "this") {
|
||||
searchScope = *evaluator->getScope(scopeIndex).scope;
|
||||
|
||||
auto currParent = evaluator->getScope(0).parent;
|
||||
|
||||
if (currParent == nullptr)
|
||||
LogConsole::abortEvaluation("invalid use of 'this' outside of struct-like type", this);
|
||||
|
||||
currPattern = currParent->clone();
|
||||
continue;
|
||||
} else {
|
||||
bool found = false;
|
||||
for (auto iter = searchScope.crbegin(); iter != searchScope.crend(); ++iter) {
|
||||
if ((*iter)->getVariableName() == name) {
|
||||
currPattern = (*iter)->clone();
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (name == "$")
|
||||
LogConsole::abortEvaluation("invalid use of placeholder operator in rvalue");
|
||||
|
||||
if (!found) {
|
||||
LogConsole::abortEvaluation(hex::format("no variable named '{}' found", name), this);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Array indexing
|
||||
auto node = std::get<std::unique_ptr<ASTNode>>(part)->evaluate(evaluator);
|
||||
auto index = dynamic_cast<ASTNodeLiteral *>(node.get());
|
||||
|
||||
std::visit(overloaded {
|
||||
[this](const std::string &) { LogConsole::abortEvaluation("cannot use string to index array", this); },
|
||||
[this](Pattern *) { LogConsole::abortEvaluation("cannot use custom type to index array", this); },
|
||||
[&, this](auto &&index) {
|
||||
if (auto dynamicArrayPattern = dynamic_cast<PatternArrayDynamic *>(currPattern.get())) {
|
||||
if (static_cast<u128>(index) >= searchScope.size() || static_cast<i128>(index) < 0)
|
||||
LogConsole::abortEvaluation("array index out of bounds", this);
|
||||
|
||||
currPattern = searchScope[index]->clone();
|
||||
} else if (auto staticArrayPattern = dynamic_cast<PatternArrayStatic *>(currPattern.get())) {
|
||||
if (static_cast<u128>(index) >= staticArrayPattern->getEntryCount() || static_cast<i128>(index) < 0)
|
||||
LogConsole::abortEvaluation("array index out of bounds", this);
|
||||
|
||||
auto newPattern = searchScope.front()->clone();
|
||||
newPattern->setOffset(staticArrayPattern->getOffset() + index * staticArrayPattern->getTemplate()->getSize());
|
||||
currPattern = std::move(newPattern);
|
||||
}
|
||||
} },
|
||||
index->getValue());
|
||||
}
|
||||
|
||||
if (currPattern == nullptr)
|
||||
break;
|
||||
|
||||
if (auto pointerPattern = dynamic_cast<PatternPointer *>(currPattern.get())) {
|
||||
currPattern = pointerPattern->getPointedAtPattern()->clone();
|
||||
}
|
||||
|
||||
Pattern *indexPattern;
|
||||
if (currPattern->isLocal()) {
|
||||
auto stackLiteral = evaluator->getStack()[currPattern->getOffset()];
|
||||
if (auto stackPattern = std::get_if<Pattern *>(&stackLiteral); stackPattern != nullptr)
|
||||
indexPattern = *stackPattern;
|
||||
else
|
||||
return hex::moveToVector<std::unique_ptr<Pattern>>(std::move(currPattern));
|
||||
} else
|
||||
indexPattern = currPattern.get();
|
||||
|
||||
if (auto structPattern = dynamic_cast<PatternStruct *>(indexPattern))
|
||||
searchScope = structPattern->getMembers();
|
||||
else if (auto unionPattern = dynamic_cast<PatternUnion *>(indexPattern))
|
||||
searchScope = unionPattern->getMembers();
|
||||
else if (auto bitfieldPattern = dynamic_cast<PatternBitfield *>(indexPattern))
|
||||
searchScope = bitfieldPattern->getFields();
|
||||
else if (auto dynamicArrayPattern = dynamic_cast<PatternArrayDynamic *>(indexPattern))
|
||||
searchScope = dynamicArrayPattern->getEntries();
|
||||
else if (auto staticArrayPattern = dynamic_cast<PatternArrayStatic *>(indexPattern))
|
||||
searchScope = { staticArrayPattern->getTemplate() };
|
||||
}
|
||||
|
||||
if (currPattern == nullptr)
|
||||
LogConsole::abortEvaluation("cannot reference global scope", this);
|
||||
|
||||
return hex::moveToVector<std::unique_ptr<Pattern>>(std::move(currPattern));
|
||||
}
|
||||
|
||||
private:
|
||||
Path m_path;
|
||||
|
||||
void readVariable(Evaluator *evaluator, auto &value, Pattern *variablePattern) const {
|
||||
constexpr bool isString = std::same_as<std::remove_cvref_t<decltype(value)>, std::string>;
|
||||
|
||||
if (variablePattern->isLocal()) {
|
||||
auto &literal = evaluator->getStack()[variablePattern->getOffset()];
|
||||
|
||||
std::visit(overloaded {
|
||||
[&](std::string &assignmentValue) {
|
||||
if constexpr (isString) value = assignmentValue;
|
||||
},
|
||||
[&](Pattern *assignmentValue) { readVariable(evaluator, value, assignmentValue); },
|
||||
[&](auto &&assignmentValue) { value = assignmentValue; } },
|
||||
literal);
|
||||
} else {
|
||||
if constexpr (isString) {
|
||||
value.resize(variablePattern->getSize());
|
||||
evaluator->getProvider()->read(variablePattern->getOffset(), value.data(), value.size());
|
||||
value.erase(std::find(value.begin(), value.end(), '\0'), value.end());
|
||||
} else {
|
||||
evaluator->getProvider()->read(variablePattern->getOffset(), &value, variablePattern->getSize());
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr (!isString)
|
||||
value = hex::changeEndianess(value, variablePattern->getSize(), variablePattern->getEndian());
|
||||
}
|
||||
};
|
||||
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeScopeResolution : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeScopeResolution(std::unique_ptr<ASTNode> &&type, std::string name) : ASTNode(), m_type(std::move(type)), m_name(std::move(name)) { }
|
||||
|
||||
ASTNodeScopeResolution(const ASTNodeScopeResolution &other) : ASTNode(other) {
|
||||
this->m_type = other.m_type->clone();
|
||||
this->m_name = other.m_name;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeScopeResolution(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> evaluate(Evaluator *evaluator) const override {
|
||||
auto type = this->m_type->evaluate(evaluator);
|
||||
|
||||
if (auto enumType = dynamic_cast<ASTNodeEnum *>(type.get())) {
|
||||
for (auto &[name, value] : enumType->getEntries()) {
|
||||
if (name == this->m_name)
|
||||
return value->evaluate(evaluator);
|
||||
}
|
||||
} else {
|
||||
LogConsole::abortEvaluation("invalid scope resolution. Cannot access this type");
|
||||
}
|
||||
|
||||
LogConsole::abortEvaluation(hex::format("could not find constant '{}'", this->m_name), this);
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<ASTNode> m_type;
|
||||
std::string m_name;
|
||||
};
|
||||
|
||||
}
|
@ -1,73 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_attribute.hpp>
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern_struct.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeStruct : public ASTNode,
|
||||
public Attributable {
|
||||
public:
|
||||
ASTNodeStruct() : ASTNode() { }
|
||||
|
||||
ASTNodeStruct(const ASTNodeStruct &other) : ASTNode(other), Attributable(other) {
|
||||
for (const auto &otherMember : other.getMembers())
|
||||
this->m_members.push_back(otherMember->clone());
|
||||
for (const auto &otherInheritance : other.getInheritance())
|
||||
this->m_inheritance.push_back(otherInheritance->clone());
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeStruct(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<std::unique_ptr<Pattern>> createPatterns(Evaluator *evaluator) const override {
|
||||
auto pattern = std::make_unique<PatternStruct>(evaluator, evaluator->dataOffset(), 0);
|
||||
|
||||
u64 startOffset = evaluator->dataOffset();
|
||||
std::vector<std::shared_ptr<Pattern>> memberPatterns;
|
||||
|
||||
evaluator->pushScope(pattern.get(), memberPatterns);
|
||||
ON_SCOPE_EXIT {
|
||||
evaluator->popScope();
|
||||
};
|
||||
|
||||
for (auto &inheritance : this->m_inheritance) {
|
||||
auto inheritancePatterns = inheritance->createPatterns(evaluator);
|
||||
auto &inheritancePattern = inheritancePatterns.front();
|
||||
|
||||
if (auto structPattern = dynamic_cast<PatternStruct *>(inheritancePattern.get())) {
|
||||
for (auto &member : structPattern->getMembers()) {
|
||||
memberPatterns.push_back(member->clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &member : this->m_members) {
|
||||
for (auto &memberPattern : member->createPatterns(evaluator)) {
|
||||
memberPatterns.push_back(std::move(memberPattern));
|
||||
}
|
||||
}
|
||||
|
||||
pattern->setMembers(std::move(memberPatterns));
|
||||
pattern->setSize(evaluator->dataOffset() - startOffset);
|
||||
|
||||
applyTypeAttributes(evaluator, this, pattern.get());
|
||||
|
||||
return hex::moveToVector<std::unique_ptr<Pattern>>(std::move(pattern));
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::vector<std::shared_ptr<ASTNode>> &getMembers() const { return this->m_members; }
|
||||
void addMember(std::shared_ptr<ASTNode> &&node) { this->m_members.push_back(std::move(node)); }
|
||||
|
||||
[[nodiscard]] const std::vector<std::shared_ptr<ASTNode>> &getInheritance() const { return this->m_inheritance; }
|
||||
void addInheritance(std::shared_ptr<ASTNode> &&node) { this->m_inheritance.push_back(std::move(node)); }
|
||||
|
||||
private:
|
||||
std::vector<std::shared_ptr<ASTNode>> m_members;
|
||||
std::vector<std::shared_ptr<ASTNode>> m_inheritance;
|
||||
};
|
||||
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeTernaryExpression : public ASTNode {
|
||||
public:
|
||||
ASTNodeTernaryExpression(std::unique_ptr<ASTNode> &&first, std::unique_ptr<ASTNode> &&second, std::unique_ptr<ASTNode> &&third, Token::Operator op)
|
||||
: ASTNode(), m_first(std::move(first)), m_second(std::move(second)), m_third(std::move(third)), m_operator(op) { }
|
||||
|
||||
ASTNodeTernaryExpression(const ASTNodeTernaryExpression &other) : ASTNode(other) {
|
||||
this->m_operator = other.m_operator;
|
||||
this->m_first = other.m_first->clone();
|
||||
this->m_second = other.m_second->clone();
|
||||
this->m_third = other.m_third->clone();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeTernaryExpression(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> evaluate(Evaluator *evaluator) const override {
|
||||
if (this->getFirstOperand() == nullptr || this->getSecondOperand() == nullptr || this->getThirdOperand() == nullptr)
|
||||
LogConsole::abortEvaluation("attempted to use void expression in mathematical expression", this);
|
||||
|
||||
auto firstNode = this->getFirstOperand()->evaluate(evaluator);
|
||||
auto secondNode = this->getSecondOperand()->evaluate(evaluator);
|
||||
auto thirdNode = this->getThirdOperand()->evaluate(evaluator);
|
||||
|
||||
auto *first = dynamic_cast<ASTNodeLiteral *>(firstNode.get());
|
||||
auto *second = dynamic_cast<ASTNodeLiteral *>(secondNode.get());
|
||||
auto *third = dynamic_cast<ASTNodeLiteral *>(thirdNode.get());
|
||||
|
||||
auto condition = std::visit(overloaded {
|
||||
[](const std::string &value) -> bool { return !value.empty(); },
|
||||
[this](const std::shared_ptr<Pattern> &) -> bool { LogConsole::abortEvaluation("cannot cast custom type to bool", this); },
|
||||
[](auto &&value) -> bool { return bool(value); } },
|
||||
first->getValue());
|
||||
|
||||
return std::visit(overloaded {
|
||||
[condition]<typename T>(const T &second, const T &third) -> std::unique_ptr<ASTNode> { return std::unique_ptr<ASTNode>(new ASTNodeLiteral(condition ? second : third)); },
|
||||
[this](auto &&, auto &&) -> std::unique_ptr<ASTNode> { LogConsole::abortEvaluation("operands to ternary expression have different types", this); } },
|
||||
second->getValue(),
|
||||
third->getValue());
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::unique_ptr<ASTNode> &getFirstOperand() const { return this->m_first; }
|
||||
[[nodiscard]] const std::unique_ptr<ASTNode> &getSecondOperand() const { return this->m_second; }
|
||||
[[nodiscard]] const std::unique_ptr<ASTNode> &getThirdOperand() const { return this->m_third; }
|
||||
[[nodiscard]] Token::Operator getOperator() const { return this->m_operator; }
|
||||
|
||||
private:
|
||||
std::unique_ptr<ASTNode> m_first, m_second, m_third;
|
||||
Token::Operator m_operator;
|
||||
};
|
||||
|
||||
}
|
@ -1,97 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_attribute.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeTypeDecl : public ASTNode,
|
||||
public Attributable {
|
||||
public:
|
||||
explicit ASTNodeTypeDecl(std::string name) : m_forwardDeclared(true), m_name(std::move(name)) { }
|
||||
|
||||
ASTNodeTypeDecl(std::string name, std::shared_ptr<ASTNode> type, std::optional<std::endian> endian = std::nullopt)
|
||||
: ASTNode(), m_name(std::move(name)), m_type(std::move(type)), m_endian(endian) { }
|
||||
|
||||
ASTNodeTypeDecl(const ASTNodeTypeDecl &other) : ASTNode(other), Attributable(other) {
|
||||
this->m_name = other.m_name;
|
||||
this->m_type = other.m_type;
|
||||
this->m_endian = other.m_endian;
|
||||
this->m_forwardDeclared = other.m_forwardDeclared;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeTypeDecl(*this));
|
||||
}
|
||||
|
||||
void setName(const std::string &name) { this->m_name = name; }
|
||||
[[nodiscard]] const std::string &getName() const { return this->m_name; }
|
||||
[[nodiscard]] const std::shared_ptr<ASTNode> &getType() const {
|
||||
if (this->isForwardDeclared())
|
||||
LogConsole::abortEvaluation(hex::format("cannot use incomplete type '{}'", this->m_name), this);
|
||||
|
||||
return this->m_type;
|
||||
}
|
||||
[[nodiscard]] std::optional<std::endian> getEndian() const { return this->m_endian; }
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> evaluate(Evaluator *evaluator) const override {
|
||||
auto type = this->getType()->evaluate(evaluator);
|
||||
|
||||
if (auto attributable = dynamic_cast<Attributable *>(type.get())) {
|
||||
for (auto &attribute : this->getAttributes()) {
|
||||
auto copy = attribute->clone();
|
||||
if (auto node = dynamic_cast<ASTNodeAttribute *>(copy.get())) {
|
||||
attributable->addAttribute(std::unique_ptr<ASTNodeAttribute>(node));
|
||||
(void)copy.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<std::unique_ptr<Pattern>> createPatterns(Evaluator *evaluator) const override {
|
||||
auto patterns = this->getType()->createPatterns(evaluator);
|
||||
|
||||
for (auto &pattern : patterns) {
|
||||
if (pattern == nullptr)
|
||||
continue;
|
||||
|
||||
if (!this->m_name.empty())
|
||||
pattern->setTypeName(this->m_name);
|
||||
|
||||
if (this->m_endian.has_value())
|
||||
pattern->setEndian(this->m_endian.value());
|
||||
|
||||
applyTypeAttributes(evaluator, this, pattern.get());
|
||||
}
|
||||
|
||||
return patterns;
|
||||
}
|
||||
|
||||
void addAttribute(std::unique_ptr<ASTNodeAttribute> &&attribute) override {
|
||||
if (auto attributable = dynamic_cast<Attributable *>(this->getType().get()); attributable != nullptr) {
|
||||
attributable->addAttribute(std::unique_ptr<ASTNodeAttribute>(static_cast<ASTNodeAttribute *>(attribute->clone().release())));
|
||||
}
|
||||
|
||||
Attributable::addAttribute(std::move(attribute));
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
bool isForwardDeclared() const {
|
||||
return this->m_forwardDeclared;
|
||||
}
|
||||
|
||||
void setType(std::shared_ptr<ASTNode> type) {
|
||||
this->m_forwardDeclared = false;
|
||||
this->m_type = type;
|
||||
}
|
||||
|
||||
private:
|
||||
bool m_forwardDeclared = false;
|
||||
std::string m_name;
|
||||
std::shared_ptr<ASTNode> m_type;
|
||||
std::optional<std::endian> m_endian;
|
||||
};
|
||||
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeTypeOperator : public ASTNode {
|
||||
public:
|
||||
ASTNodeTypeOperator(Token::Operator op, std::unique_ptr<ASTNode> &&expression) : m_op(op), m_expression(std::move(expression)) {
|
||||
}
|
||||
|
||||
ASTNodeTypeOperator(const ASTNodeTypeOperator &other) : ASTNode(other) {
|
||||
this->m_op = other.m_op;
|
||||
this->m_expression = other.m_expression->clone();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeTypeOperator(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] Token::Operator getOperator() const {
|
||||
return this->m_op;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::unique_ptr<ASTNode> &getExpression() const {
|
||||
return this->m_expression;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> evaluate(Evaluator *evaluator) const override {
|
||||
auto patterns = this->m_expression->createPatterns(evaluator);
|
||||
auto &pattern = patterns.front();
|
||||
|
||||
switch (this->getOperator()) {
|
||||
case Token::Operator::AddressOf:
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeLiteral(u128(pattern->getOffset())));
|
||||
case Token::Operator::SizeOf:
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeLiteral(u128(pattern->getSize())));
|
||||
default:
|
||||
LogConsole::abortEvaluation("invalid type operator", this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
Token::Operator m_op;
|
||||
std::unique_ptr<ASTNode> m_expression;
|
||||
};
|
||||
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_attribute.hpp>
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern_union.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeUnion : public ASTNode,
|
||||
public Attributable {
|
||||
public:
|
||||
ASTNodeUnion() : ASTNode() { }
|
||||
|
||||
ASTNodeUnion(const ASTNodeUnion &other) : ASTNode(other), Attributable(other) {
|
||||
for (const auto &otherMember : other.getMembers())
|
||||
this->m_members.push_back(otherMember->clone());
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeUnion(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<std::unique_ptr<Pattern>> createPatterns(Evaluator *evaluator) const override {
|
||||
auto pattern = std::make_unique<PatternUnion>(evaluator, evaluator->dataOffset(), 0);
|
||||
|
||||
size_t size = 0;
|
||||
std::vector<std::shared_ptr<Pattern>> memberPatterns;
|
||||
u64 startOffset = evaluator->dataOffset();
|
||||
|
||||
evaluator->pushScope(pattern.get(), memberPatterns);
|
||||
ON_SCOPE_EXIT {
|
||||
evaluator->popScope();
|
||||
};
|
||||
|
||||
for (auto &member : this->m_members) {
|
||||
for (auto &memberPattern : member->createPatterns(evaluator)) {
|
||||
memberPattern->setOffset(startOffset);
|
||||
size = std::max(memberPattern->getSize(), size);
|
||||
|
||||
memberPatterns.push_back(std::move(memberPattern));
|
||||
}
|
||||
}
|
||||
|
||||
evaluator->dataOffset() = startOffset + size;
|
||||
pattern->setMembers(std::move(memberPatterns));
|
||||
pattern->setSize(size);
|
||||
|
||||
applyTypeAttributes(evaluator, this, pattern.get());
|
||||
|
||||
return hex::moveToVector<std::unique_ptr<Pattern>>(std::move(pattern));
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::vector<std::shared_ptr<ASTNode>> &getMembers() const { return this->m_members; }
|
||||
|
||||
private:
|
||||
std::vector<std::shared_ptr<ASTNode>> m_members;
|
||||
};
|
||||
|
||||
}
|
@ -1,81 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_attribute.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_literal.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_type_decl.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeVariableDecl : public ASTNode,
|
||||
public Attributable {
|
||||
public:
|
||||
ASTNodeVariableDecl(std::string name, std::shared_ptr<ASTNodeTypeDecl> type, std::unique_ptr<ASTNode> &&placementOffset = nullptr, bool inVariable = false, bool outVariable = false)
|
||||
: ASTNode(), m_name(std::move(name)), m_type(std::move(type)), m_placementOffset(std::move(placementOffset)), m_inVariable(inVariable), m_outVariable(outVariable) { }
|
||||
|
||||
ASTNodeVariableDecl(const ASTNodeVariableDecl &other) : ASTNode(other), Attributable(other) {
|
||||
this->m_name = other.m_name;
|
||||
this->m_type = other.m_type;
|
||||
|
||||
if (other.m_placementOffset != nullptr)
|
||||
this->m_placementOffset = other.m_placementOffset->clone();
|
||||
else
|
||||
this->m_placementOffset = nullptr;
|
||||
|
||||
this->m_inVariable = other.m_inVariable;
|
||||
this->m_outVariable = other.m_outVariable;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeVariableDecl(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::string &getName() const { return this->m_name; }
|
||||
[[nodiscard]] constexpr const std::shared_ptr<ASTNodeTypeDecl> &getType() const { return this->m_type; }
|
||||
[[nodiscard]] constexpr const std::unique_ptr<ASTNode> &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<std::unique_ptr<Pattern>> createPatterns(Evaluator *evaluator) const override {
|
||||
u64 startOffset = evaluator->dataOffset();
|
||||
|
||||
if (this->m_placementOffset != nullptr) {
|
||||
const auto node = this->m_placementOffset->evaluate(evaluator);
|
||||
const auto offset = dynamic_cast<ASTNodeLiteral *>(node.get());
|
||||
|
||||
evaluator->dataOffset() = std::visit(overloaded {
|
||||
[this](const std::string &) -> u64 { LogConsole::abortEvaluation("placement offset cannot be a string", this); },
|
||||
[this](Pattern *) -> u64 { LogConsole::abortEvaluation("placement offset cannot be a custom type", this); },
|
||||
[](auto &&offset) -> u64 { return offset; } },
|
||||
offset->getValue());
|
||||
}
|
||||
|
||||
auto patterns = this->m_type->createPatterns(evaluator);
|
||||
auto &pattern = patterns.front();
|
||||
pattern->setVariableName(this->m_name);
|
||||
|
||||
applyVariableAttributes(evaluator, this, pattern.get());
|
||||
|
||||
if (this->m_placementOffset != nullptr && !evaluator->isGlobalScope()) {
|
||||
evaluator->dataOffset() = startOffset;
|
||||
}
|
||||
|
||||
return hex::moveToVector<std::unique_ptr<Pattern>>(std::move(pattern));
|
||||
}
|
||||
|
||||
FunctionResult execute(Evaluator *evaluator) const override {
|
||||
evaluator->createVariable(this->getName(), this->getType().get());
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
std::shared_ptr<ASTNodeTypeDecl> m_type;
|
||||
std::unique_ptr<ASTNode> m_placementOffset;
|
||||
|
||||
bool m_inVariable = false, m_outVariable = false;
|
||||
};
|
||||
|
||||
}
|
@ -1,107 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeWhileStatement : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeWhileStatement(std::unique_ptr<ASTNode> &&condition, std::vector<std::unique_ptr<ASTNode>> &&body, std::unique_ptr<ASTNode> &&postExpression = nullptr)
|
||||
: ASTNode(), m_condition(std::move(condition)), m_body(std::move(body)), m_postExpression(std::move(postExpression)) { }
|
||||
|
||||
ASTNodeWhileStatement(const ASTNodeWhileStatement &other) : ASTNode(other) {
|
||||
this->m_condition = other.m_condition->clone();
|
||||
|
||||
for (auto &statement : other.m_body)
|
||||
this->m_body.push_back(statement->clone());
|
||||
|
||||
if (other.m_postExpression != nullptr)
|
||||
this->m_postExpression = other.m_postExpression->clone();
|
||||
else
|
||||
this->m_postExpression = nullptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeWhileStatement(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::unique_ptr<ASTNode> &getCondition() {
|
||||
return this->m_condition;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::vector<std::unique_ptr<ASTNode>> &getBody() {
|
||||
return this->m_body;
|
||||
}
|
||||
|
||||
FunctionResult execute(Evaluator *evaluator) const override {
|
||||
|
||||
u64 loopIterations = 0;
|
||||
while (evaluateCondition(evaluator)) {
|
||||
evaluator->handleAbort();
|
||||
|
||||
auto variables = *evaluator->getScope(0).scope;
|
||||
auto parameterPack = evaluator->getScope(0).parameterPack;
|
||||
u32 startVariableCount = variables.size();
|
||||
ON_SCOPE_EXIT {
|
||||
ssize_t stackSize = evaluator->getStack().size();
|
||||
for (u32 i = startVariableCount; i < variables.size(); i++) {
|
||||
stackSize--;
|
||||
}
|
||||
if (stackSize < 0) LogConsole::abortEvaluation("stack pointer underflow!", this);
|
||||
evaluator->getStack().resize(stackSize);
|
||||
};
|
||||
|
||||
evaluator->pushScope(nullptr, variables);
|
||||
evaluator->getScope(0).parameterPack = parameterPack;
|
||||
ON_SCOPE_EXIT {
|
||||
evaluator->popScope();
|
||||
};
|
||||
|
||||
auto ctrlFlow = ControlFlowStatement::None;
|
||||
for (auto &statement : this->m_body) {
|
||||
auto result = statement->execute(evaluator);
|
||||
|
||||
ctrlFlow = evaluator->getCurrentControlFlowStatement();
|
||||
evaluator->setCurrentControlFlowStatement(ControlFlowStatement::None);
|
||||
if (ctrlFlow == ControlFlowStatement::Return)
|
||||
return result;
|
||||
else if (ctrlFlow != ControlFlowStatement::None)
|
||||
break;
|
||||
}
|
||||
|
||||
if (this->m_postExpression != nullptr)
|
||||
this->m_postExpression->execute(evaluator);
|
||||
|
||||
loopIterations++;
|
||||
if (loopIterations >= evaluator->getLoopLimit())
|
||||
LogConsole::abortEvaluation(hex::format("loop iterations exceeded limit of {}", evaluator->getLoopLimit()), this);
|
||||
|
||||
evaluator->handleAbort();
|
||||
|
||||
if (ctrlFlow == ControlFlowStatement::Break)
|
||||
break;
|
||||
else if (ctrlFlow == ControlFlowStatement::Continue)
|
||||
continue;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool evaluateCondition(Evaluator *evaluator) const {
|
||||
const auto node = this->m_condition->evaluate(evaluator);
|
||||
const auto literal = dynamic_cast<ASTNodeLiteral *>(node.get());
|
||||
|
||||
return std::visit(overloaded {
|
||||
[](const std::string &value) -> bool { return !value.empty(); },
|
||||
[this](Pattern *const &) -> bool { LogConsole::abortEvaluation("cannot cast custom type to bool", this); },
|
||||
[](auto &&value) -> bool { return value != 0; } },
|
||||
literal->getValue());
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<ASTNode> m_condition;
|
||||
std::vector<std::unique_ptr<ASTNode>> m_body;
|
||||
std::unique_ptr<ASTNode> m_postExpression;
|
||||
};
|
||||
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex.hpp>
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class PatternLanguageError : public std::exception {
|
||||
public:
|
||||
PatternLanguageError(u32 lineNumber, std::string message) : m_lineNumber(lineNumber), m_message(std::move(message)) { }
|
||||
|
||||
[[nodiscard]] const char *what() const noexcept override {
|
||||
return this->m_message.c_str();
|
||||
}
|
||||
|
||||
[[nodiscard]] u32 getLineNumber() const {
|
||||
return this->m_lineNumber;
|
||||
}
|
||||
|
||||
private:
|
||||
u32 m_lineNumber;
|
||||
std::string m_message;
|
||||
};
|
||||
|
||||
}
|
@ -1,293 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <bit>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include <hex/pattern_language/log_console.hpp>
|
||||
|
||||
#include <hex/api/content_registry.hpp>
|
||||
|
||||
#include <hex/helpers/fmt.hpp>
|
||||
#include <hex/helpers/concepts.hpp>
|
||||
|
||||
namespace hex::prv {
|
||||
class Provider;
|
||||
}
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
enum class DangerousFunctionPermission
|
||||
{
|
||||
Ask,
|
||||
Deny,
|
||||
Allow
|
||||
};
|
||||
|
||||
enum class ControlFlowStatement
|
||||
{
|
||||
None,
|
||||
Continue,
|
||||
Break,
|
||||
Return
|
||||
};
|
||||
|
||||
enum class BitfieldOrder
|
||||
{
|
||||
RightToLeft,
|
||||
LeftToRight
|
||||
};
|
||||
|
||||
class Pattern;
|
||||
class PatternCreationLimiter;
|
||||
class ASTNode;
|
||||
|
||||
class Evaluator {
|
||||
public:
|
||||
Evaluator() = default;
|
||||
|
||||
std::optional<std::vector<std::shared_ptr<Pattern>>> evaluate(const std::vector<std::shared_ptr<ASTNode>> &ast);
|
||||
|
||||
[[nodiscard]] LogConsole &getConsole() {
|
||||
return this->m_console;
|
||||
}
|
||||
|
||||
struct ParameterPack {
|
||||
std::string name;
|
||||
std::vector<Token::Literal> values;
|
||||
};
|
||||
|
||||
struct Scope {
|
||||
Pattern *parent;
|
||||
std::vector<std::shared_ptr<Pattern>> *scope;
|
||||
std::optional<ParameterPack> parameterPack;
|
||||
};
|
||||
|
||||
void pushScope(Pattern *parent, std::vector<std::shared_ptr<Pattern>> &scope) {
|
||||
if (this->m_scopes.size() > this->getEvaluationDepth())
|
||||
LogConsole::abortEvaluation(hex::format("evaluation depth exceeded set limit of {}", this->getEvaluationDepth()));
|
||||
|
||||
this->handleAbort();
|
||||
|
||||
this->m_scopes.push_back({ parent, &scope, { } });
|
||||
}
|
||||
|
||||
void popScope() {
|
||||
this->m_scopes.pop_back();
|
||||
}
|
||||
|
||||
[[nodiscard]] Scope &getScope(i32 index) {
|
||||
return this->m_scopes[this->m_scopes.size() - 1 + index];
|
||||
}
|
||||
|
||||
[[nodiscard]] const Scope &getScope(i32 index) const {
|
||||
return this->m_scopes[this->m_scopes.size() - 1 + index];
|
||||
}
|
||||
|
||||
[[nodiscard]] Scope &getGlobalScope() {
|
||||
return this->m_scopes.front();
|
||||
}
|
||||
|
||||
[[nodiscard]] const Scope &getGlobalScope() const {
|
||||
return this->m_scopes.front();
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t getScopeCount() {
|
||||
return this->m_scopes.size();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool isGlobalScope() {
|
||||
return this->m_scopes.size() == 1;
|
||||
}
|
||||
|
||||
void setProvider(prv::Provider *provider) {
|
||||
this->m_provider = provider;
|
||||
}
|
||||
|
||||
void setInVariables(const std::map<std::string, Token::Literal> &inVariables) {
|
||||
this->m_inVariables = inVariables;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::map<std::string, Token::Literal> getOutVariables() const {
|
||||
std::map<std::string, Token::Literal> 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;
|
||||
}
|
||||
|
||||
void setDefaultEndian(std::endian endian) {
|
||||
this->m_defaultEndian = endian;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::endian getDefaultEndian() const {
|
||||
return this->m_defaultEndian;
|
||||
}
|
||||
|
||||
void setEvaluationDepth(u64 evalDepth) {
|
||||
this->m_evalDepth = evalDepth;
|
||||
}
|
||||
|
||||
[[nodiscard]] u64 getEvaluationDepth() const {
|
||||
return this->m_evalDepth;
|
||||
}
|
||||
|
||||
void setArrayLimit(u64 arrayLimit) {
|
||||
this->m_arrayLimit = arrayLimit;
|
||||
}
|
||||
|
||||
[[nodiscard]] u64 getArrayLimit() const {
|
||||
return this->m_arrayLimit;
|
||||
}
|
||||
|
||||
void setPatternLimit(u64 limit) {
|
||||
this->m_patternLimit = limit;
|
||||
}
|
||||
|
||||
[[nodiscard]] u64 getPatternLimit() const {
|
||||
return this->m_patternLimit;
|
||||
}
|
||||
|
||||
[[nodiscard]] u64 getPatternCount() const {
|
||||
return this->m_currPatternCount;
|
||||
}
|
||||
|
||||
void setLoopLimit(u64 limit) {
|
||||
this->m_loopLimit = limit;
|
||||
}
|
||||
|
||||
[[nodiscard]] u64 getLoopLimit() const {
|
||||
return this->m_loopLimit;
|
||||
}
|
||||
|
||||
void setBitfieldOrder(BitfieldOrder order) {
|
||||
this->m_bitfieldOrder = order;
|
||||
}
|
||||
|
||||
[[nodiscard]] BitfieldOrder getBitfieldOrder() {
|
||||
return this->m_bitfieldOrder;
|
||||
}
|
||||
|
||||
u64 &dataOffset() { return this->m_currOffset; }
|
||||
|
||||
bool addCustomFunction(const std::string &name, ContentRegistry::PatternLanguage::ParameterCount numParams, std::vector<Token::Literal> defaultParameters, const ContentRegistry::PatternLanguage::Callback &function) {
|
||||
const auto [iter, inserted] = this->m_customFunctions.insert({
|
||||
name, {numParams, std::move(defaultParameters), function, false}
|
||||
});
|
||||
|
||||
return inserted;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::map<std::string, ContentRegistry::PatternLanguage::Function> &getCustomFunctions() const {
|
||||
return this->m_customFunctions;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<Token::Literal> &getStack() {
|
||||
return this->m_stack;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::vector<Token::Literal> &getStack() const {
|
||||
return this->m_stack;
|
||||
}
|
||||
|
||||
void createParameterPack(const std::string &name, const std::vector<Token::Literal> &values);
|
||||
void createVariable(const std::string &name, ASTNode *type, const std::optional<Token::Literal> &value = std::nullopt, bool outVariable = false);
|
||||
void setVariable(const std::string &name, const Token::Literal &value);
|
||||
|
||||
void abort() {
|
||||
this->m_aborted = true;
|
||||
}
|
||||
|
||||
void handleAbort() {
|
||||
if (this->m_aborted)
|
||||
LogConsole::abortEvaluation("evaluation aborted by user");
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<Token::Literal> getEnvVariable(const std::string &name) const {
|
||||
if (this->m_envVariables.contains(name)) {
|
||||
auto value = this->m_envVariables.at(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;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool hasDangerousFunctionBeenCalled() const {
|
||||
return this->m_dangerousFunctionCalled;
|
||||
}
|
||||
|
||||
void dangerousFunctionCalled() {
|
||||
this->m_dangerousFunctionCalled = true;
|
||||
}
|
||||
|
||||
void allowDangerousFunctions(bool allow) {
|
||||
this->m_allowDangerousFunctions = allow ? DangerousFunctionPermission::Allow : DangerousFunctionPermission::Deny;
|
||||
this->m_dangerousFunctionCalled = false;
|
||||
}
|
||||
|
||||
[[nodiscard]] DangerousFunctionPermission getDangerousFunctionPermission() const {
|
||||
return this->m_allowDangerousFunctions;
|
||||
}
|
||||
|
||||
void setCurrentControlFlowStatement(ControlFlowStatement statement) {
|
||||
this->m_currControlFlowStatement = statement;
|
||||
}
|
||||
|
||||
[[nodiscard]] ControlFlowStatement getCurrentControlFlowStatement() const {
|
||||
return this->m_currControlFlowStatement;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::optional<Token::Literal> &getMainResult() {
|
||||
return this->m_mainResult;
|
||||
}
|
||||
|
||||
private:
|
||||
void patternCreated();
|
||||
void patternDestroyed();
|
||||
|
||||
private:
|
||||
u64 m_currOffset;
|
||||
prv::Provider *m_provider = nullptr;
|
||||
LogConsole m_console;
|
||||
|
||||
std::endian m_defaultEndian = std::endian::native;
|
||||
u64 m_evalDepth;
|
||||
u64 m_arrayLimit;
|
||||
u64 m_patternLimit;
|
||||
u64 m_loopLimit;
|
||||
|
||||
u64 m_currPatternCount;
|
||||
|
||||
std::atomic<bool> m_aborted;
|
||||
|
||||
std::vector<Scope> m_scopes;
|
||||
std::map<std::string, ContentRegistry::PatternLanguage::Function> m_customFunctions;
|
||||
std::vector<std::unique_ptr<ASTNode>> m_customFunctionDefinitions;
|
||||
std::vector<Token::Literal> m_stack;
|
||||
|
||||
std::optional<Token::Literal> m_mainResult;
|
||||
|
||||
std::map<std::string, Token::Literal> m_envVariables;
|
||||
std::map<std::string, Token::Literal> m_inVariables;
|
||||
std::map<std::string, size_t> m_outVariables;
|
||||
|
||||
std::atomic<bool> m_dangerousFunctionCalled = false;
|
||||
std::atomic<DangerousFunctionPermission> m_allowDangerousFunctions = DangerousFunctionPermission::Ask;
|
||||
ControlFlowStatement m_currControlFlowStatement;
|
||||
BitfieldOrder m_bitfieldOrder = BitfieldOrder::RightToLeft;
|
||||
|
||||
friend class PatternCreationLimiter;
|
||||
};
|
||||
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex.hpp>
|
||||
|
||||
#include <hex/pattern_language/token.hpp>
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class Lexer {
|
||||
public:
|
||||
Lexer() = default;
|
||||
|
||||
std::optional<std::vector<Token>> lex(const std::string &code);
|
||||
const std::optional<PatternLanguageError> &getError() { return this->m_error; }
|
||||
|
||||
private:
|
||||
std::optional<PatternLanguageError> m_error;
|
||||
|
||||
[[noreturn]] static void throwLexerError(const std::string &error, u32 lineNumber) {
|
||||
throw PatternLanguageError(lineNumber, "Lexer: " + error);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex.hpp>
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <hex/helpers/concepts.hpp>
|
||||
|
||||
#include <hex/pattern_language/error.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNode;
|
||||
|
||||
class LogConsole {
|
||||
public:
|
||||
enum Level
|
||||
{
|
||||
Debug,
|
||||
Info,
|
||||
Warning,
|
||||
Error
|
||||
};
|
||||
|
||||
[[nodiscard]] const auto &getLog() const { return this->m_consoleLog; }
|
||||
|
||||
void log(Level level, const std::string &message) {
|
||||
this->m_consoleLog.emplace_back(level, message);
|
||||
}
|
||||
|
||||
[[noreturn]] static void abortEvaluation(const std::string &message) {
|
||||
abortEvaluation(message, nullptr);
|
||||
}
|
||||
|
||||
template<typename T = ASTNode>
|
||||
[[noreturn]] static void abortEvaluation(const std::string &message, const std::unique_ptr<T> &node) {
|
||||
abortEvaluation(message, node.get());
|
||||
}
|
||||
|
||||
[[noreturn]] static void abortEvaluation(const std::string &message, const ASTNode *node);
|
||||
|
||||
void clear() {
|
||||
this->m_consoleLog.clear();
|
||||
this->m_lastHardError.reset();
|
||||
}
|
||||
|
||||
void setHardError(const PatternLanguageError &error) { this->m_lastHardError = error; }
|
||||
|
||||
[[nodiscard]] const std::optional<PatternLanguageError> &getLastHardError() { return this->m_lastHardError; };
|
||||
|
||||
private:
|
||||
std::vector<std::pair<Level, std::string>> m_consoleLog;
|
||||
std::optional<PatternLanguageError> m_lastHardError;
|
||||
};
|
||||
|
||||
}
|
@ -1,293 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex.hpp>
|
||||
#include <hex/helpers/utils.hpp>
|
||||
#include <hex/helpers/intrinsics.hpp>
|
||||
|
||||
#include <hex/pattern_language/error.hpp>
|
||||
#include <hex/pattern_language/token.hpp>
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_rvalue.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_attribute.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_type_decl.hpp>
|
||||
|
||||
#include <unordered_map>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class Parser {
|
||||
public:
|
||||
using TokenIter = std::vector<Token>::const_iterator;
|
||||
|
||||
Parser() = default;
|
||||
~Parser() = default;
|
||||
|
||||
std::optional<std::vector<std::shared_ptr<ASTNode>>> parse(const std::vector<Token> &tokens);
|
||||
const std::optional<PatternLanguageError> &getError() { return this->m_error; }
|
||||
|
||||
private:
|
||||
std::optional<PatternLanguageError> m_error;
|
||||
TokenIter m_curr;
|
||||
TokenIter m_originalPosition, m_partOriginalPosition;
|
||||
|
||||
std::unordered_map<std::string, std::shared_ptr<ASTNodeTypeDecl>> m_types;
|
||||
std::vector<TokenIter> m_matchedOptionals;
|
||||
std::vector<std::vector<std::string>> m_currNamespace;
|
||||
|
||||
u32 getLineNumber(i32 index) const {
|
||||
return this->m_curr[index].lineNumber;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::unique_ptr<T> create(T *node) {
|
||||
node->setLineNumber(this->getLineNumber(-1));
|
||||
return std::unique_ptr<T>(node);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
const T &getValue(i32 index) const {
|
||||
auto value = std::get_if<T>(&this->m_curr[index].value);
|
||||
|
||||
if (value == nullptr)
|
||||
throwParserError("failed to decode token. Invalid type.", getLineNumber(index));
|
||||
|
||||
return *value;
|
||||
}
|
||||
|
||||
Token::Type getType(i32 index) const {
|
||||
return this->m_curr[index].type;
|
||||
}
|
||||
|
||||
std::string getNamespacePrefixedName(const std::string &name) {
|
||||
std::string result;
|
||||
for (const auto &part : this->m_currNamespace.back()) {
|
||||
result += part + "::";
|
||||
}
|
||||
|
||||
result += name;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::unique_ptr<ASTNode> parseFunctionCall();
|
||||
std::unique_ptr<ASTNode> parseStringLiteral();
|
||||
std::string parseNamespaceResolution();
|
||||
std::unique_ptr<ASTNode> parseScopeResolution();
|
||||
std::unique_ptr<ASTNode> parseRValue();
|
||||
std::unique_ptr<ASTNode> parseRValue(ASTNodeRValue::Path &path);
|
||||
std::unique_ptr<ASTNode> parseFactor();
|
||||
std::unique_ptr<ASTNode> parseCastExpression();
|
||||
std::unique_ptr<ASTNode> parseUnaryExpression();
|
||||
std::unique_ptr<ASTNode> parseMultiplicativeExpression();
|
||||
std::unique_ptr<ASTNode> parseAdditiveExpression();
|
||||
std::unique_ptr<ASTNode> parseShiftExpression();
|
||||
std::unique_ptr<ASTNode> parseBinaryAndExpression();
|
||||
std::unique_ptr<ASTNode> parseBinaryXorExpression();
|
||||
std::unique_ptr<ASTNode> parseBinaryOrExpression();
|
||||
std::unique_ptr<ASTNode> parseBooleanAnd();
|
||||
std::unique_ptr<ASTNode> parseBooleanXor();
|
||||
std::unique_ptr<ASTNode> parseBooleanOr();
|
||||
std::unique_ptr<ASTNode> parseRelationExpression();
|
||||
std::unique_ptr<ASTNode> parseEqualityExpression();
|
||||
std::unique_ptr<ASTNode> parseTernaryConditional();
|
||||
std::unique_ptr<ASTNode> parseMathematicalExpression();
|
||||
|
||||
std::unique_ptr<ASTNode> parseFunctionDefinition();
|
||||
std::unique_ptr<ASTNode> parseFunctionVariableDecl();
|
||||
std::unique_ptr<ASTNode> parseFunctionStatement();
|
||||
std::unique_ptr<ASTNode> parseFunctionVariableAssignment(const std::string &lvalue);
|
||||
std::unique_ptr<ASTNode> parseFunctionVariableCompoundAssignment(const std::string &lvalue);
|
||||
std::unique_ptr<ASTNode> parseFunctionControlFlowStatement();
|
||||
std::vector<std::unique_ptr<ASTNode>> parseStatementBody();
|
||||
std::unique_ptr<ASTNode> parseFunctionConditional();
|
||||
std::unique_ptr<ASTNode> parseFunctionWhileLoop();
|
||||
std::unique_ptr<ASTNode> parseFunctionForLoop();
|
||||
|
||||
void parseAttribute(Attributable *currNode);
|
||||
std::unique_ptr<ASTNode> parseConditional();
|
||||
std::unique_ptr<ASTNode> parseWhileStatement();
|
||||
std::unique_ptr<ASTNodeTypeDecl> parseType(bool allowFunctionTypes = false);
|
||||
std::shared_ptr<ASTNodeTypeDecl> parseUsingDeclaration();
|
||||
std::unique_ptr<ASTNode> parsePadding();
|
||||
std::unique_ptr<ASTNode> parseMemberVariable(const std::shared_ptr<ASTNodeTypeDecl> &type);
|
||||
std::unique_ptr<ASTNode> parseMemberArrayVariable(const std::shared_ptr<ASTNodeTypeDecl> &type);
|
||||
std::unique_ptr<ASTNode> parseMemberPointerVariable(const std::shared_ptr<ASTNodeTypeDecl> &type);
|
||||
std::unique_ptr<ASTNode> parseMember();
|
||||
std::shared_ptr<ASTNodeTypeDecl> parseStruct();
|
||||
std::shared_ptr<ASTNodeTypeDecl> parseUnion();
|
||||
std::shared_ptr<ASTNodeTypeDecl> parseEnum();
|
||||
std::shared_ptr<ASTNodeTypeDecl> parseBitfield();
|
||||
void parseForwardDeclaration();
|
||||
std::unique_ptr<ASTNode> parseVariablePlacement(const std::shared_ptr<ASTNodeTypeDecl> &type);
|
||||
std::unique_ptr<ASTNode> parseArrayVariablePlacement(const std::shared_ptr<ASTNodeTypeDecl> &type);
|
||||
std::unique_ptr<ASTNode> parsePointerVariablePlacement(const std::shared_ptr<ASTNodeTypeDecl> &type);
|
||||
std::unique_ptr<ASTNode> parsePlacement();
|
||||
std::vector<std::shared_ptr<ASTNode>> parseNamespace();
|
||||
std::vector<std::shared_ptr<ASTNode>> parseStatements();
|
||||
|
||||
std::shared_ptr<ASTNodeTypeDecl> addType(const std::string &name, std::unique_ptr<ASTNode> &&node, std::optional<std::endian> endian = std::nullopt);
|
||||
|
||||
std::vector<std::shared_ptr<ASTNode>> parseTillToken(Token::Type endTokenType, const auto value) {
|
||||
std::vector<std::shared_ptr<ASTNode>> program;
|
||||
|
||||
while (this->m_curr->type != endTokenType || (*this->m_curr) != value) {
|
||||
for (auto &statement : parseStatements())
|
||||
program.push_back(std::move(statement));
|
||||
}
|
||||
|
||||
this->m_curr++;
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
[[noreturn]] void throwParserError(const std::string &error, i32 token = -1) const {
|
||||
throw PatternLanguageError(this->m_curr[token].lineNumber, "Parser: " + error);
|
||||
}
|
||||
|
||||
/* Token consuming */
|
||||
|
||||
enum class Setting
|
||||
{
|
||||
};
|
||||
constexpr static auto Normal = static_cast<Setting>(0);
|
||||
constexpr static auto Not = static_cast<Setting>(1);
|
||||
|
||||
bool begin() {
|
||||
this->m_originalPosition = this->m_curr;
|
||||
this->m_matchedOptionals.clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool partBegin() {
|
||||
this->m_partOriginalPosition = this->m_curr;
|
||||
this->m_matchedOptionals.clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
this->m_curr = this->m_originalPosition;
|
||||
}
|
||||
|
||||
void partReset() {
|
||||
this->m_curr = this->m_partOriginalPosition;
|
||||
}
|
||||
|
||||
bool resetIfFailed(bool value) {
|
||||
if (!value) reset();
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
template<Setting S = Normal>
|
||||
bool sequenceImpl() {
|
||||
if constexpr (S == Normal)
|
||||
return true;
|
||||
else if constexpr (S == Not)
|
||||
return false;
|
||||
else
|
||||
hex::unreachable();
|
||||
}
|
||||
|
||||
template<Setting S = Normal>
|
||||
bool sequenceImpl(Token::Type type, auto value, auto... args) {
|
||||
if constexpr (S == Normal) {
|
||||
if (!peek(type, value)) {
|
||||
partReset();
|
||||
return false;
|
||||
}
|
||||
|
||||
this->m_curr++;
|
||||
|
||||
if (!sequenceImpl<Normal>(args...)) {
|
||||
partReset();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
} else if constexpr (S == Not) {
|
||||
if (!peek(type, value))
|
||||
return true;
|
||||
|
||||
this->m_curr++;
|
||||
|
||||
if (!sequenceImpl<Normal>(args...))
|
||||
return true;
|
||||
|
||||
partReset();
|
||||
return false;
|
||||
} else
|
||||
hex::unreachable();
|
||||
}
|
||||
|
||||
template<Setting S = Normal>
|
||||
bool sequence(Token::Type type, auto value, auto... args) {
|
||||
return partBegin() && sequenceImpl<S>(type, value, args...);
|
||||
}
|
||||
|
||||
template<Setting S = Normal>
|
||||
bool oneOfImpl() {
|
||||
if constexpr (S == Normal)
|
||||
return false;
|
||||
else if constexpr (S == Not)
|
||||
return true;
|
||||
else
|
||||
hex::unreachable();
|
||||
}
|
||||
|
||||
template<Setting S = Normal>
|
||||
bool oneOfImpl(Token::Type type, auto value, auto... args) {
|
||||
if constexpr (S == Normal)
|
||||
return sequenceImpl<Normal>(type, value) || oneOfImpl(args...);
|
||||
else if constexpr (S == Not)
|
||||
return sequenceImpl<Not>(type, value) && oneOfImpl(args...);
|
||||
else
|
||||
hex::unreachable();
|
||||
}
|
||||
|
||||
template<Setting S = Normal>
|
||||
bool oneOf(Token::Type type, auto value, auto... args) {
|
||||
return partBegin() && oneOfImpl<S>(type, value, args...);
|
||||
}
|
||||
|
||||
bool variantImpl(Token::Type type1, auto value1, Token::Type type2, auto value2) {
|
||||
if (!peek(type1, value1)) {
|
||||
if (!peek(type2, value2)) {
|
||||
partReset();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
this->m_curr++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool variant(Token::Type type1, auto value1, Token::Type type2, auto value2) {
|
||||
return partBegin() && variantImpl(type1, value1, type2, value2);
|
||||
}
|
||||
|
||||
bool optionalImpl(Token::Type type, auto value) {
|
||||
if (peek(type, value)) {
|
||||
this->m_matchedOptionals.push_back(this->m_curr);
|
||||
this->m_curr++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool optional(Token::Type type, auto value) {
|
||||
return partBegin() && optionalImpl(type, value);
|
||||
}
|
||||
|
||||
bool peek(Token::Type type, auto value, i32 index = 0) {
|
||||
return this->m_curr[index].type == type && this->m_curr[index] == value;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
@ -1,80 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#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>
|
||||
#include <hex/pattern_language/patterns/pattern.hpp>
|
||||
|
||||
namespace hex::prv {
|
||||
class Provider;
|
||||
}
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class Preprocessor;
|
||||
class Lexer;
|
||||
class Parser;
|
||||
class Validator;
|
||||
class Evaluator;
|
||||
class Pattern;
|
||||
|
||||
class ASTNode;
|
||||
|
||||
class PatternLanguage {
|
||||
public:
|
||||
PatternLanguage();
|
||||
~PatternLanguage();
|
||||
|
||||
[[nodiscard]] std::optional<std::vector<std::shared_ptr<ASTNode>>> parseString(const std::string &code);
|
||||
[[nodiscard]] bool executeString(prv::Provider *provider, const std::string &string, const std::map<std::string, Token::Literal> &envVars = {}, const std::map<std::string, Token::Literal> &inVariables = {}, bool checkResult = true);
|
||||
[[nodiscard]] bool executeFile(prv::Provider *provider, const std::fs::path &path, const std::map<std::string, Token::Literal> &envVars = {}, const std::map<std::string, Token::Literal> &inVariables = {});
|
||||
[[nodiscard]] std::pair<bool, std::optional<Token::Literal>> executeFunction(prv::Provider *provider, const std::string &code);
|
||||
[[nodiscard]] const std::vector<std::shared_ptr<ASTNode>> &getCurrentAST() const;
|
||||
|
||||
void abort();
|
||||
|
||||
[[nodiscard]] const std::vector<std::pair<LogConsole::Level, std::string>> &getConsoleLog();
|
||||
[[nodiscard]] const std::optional<PatternLanguageError> &getError();
|
||||
[[nodiscard]] std::map<std::string, Token::Literal> getOutVariables() const;
|
||||
|
||||
[[nodiscard]] u32 getCreatedPatternCount();
|
||||
[[nodiscard]] u32 getMaximumPatternCount();
|
||||
|
||||
[[nodiscard]] bool hasDangerousFunctionBeenCalled() const;
|
||||
void allowDangerousFunctions(bool allow);
|
||||
|
||||
[[nodiscard]] const std::vector<std::shared_ptr<Pattern>> &getPatterns() {
|
||||
const static std::vector<std::shared_ptr<Pattern>> empty;
|
||||
|
||||
if (isRunning()) return empty;
|
||||
else return this->m_patterns;
|
||||
}
|
||||
|
||||
void reset();
|
||||
[[nodiscard]] bool isRunning() const { return this->m_running; }
|
||||
|
||||
private:
|
||||
Preprocessor *m_preprocessor;
|
||||
Lexer *m_lexer;
|
||||
Parser *m_parser;
|
||||
Validator *m_validator;
|
||||
Evaluator *m_evaluator;
|
||||
|
||||
std::vector<std::shared_ptr<ASTNode>> m_currAST;
|
||||
|
||||
std::optional<PatternLanguageError> m_currError;
|
||||
|
||||
std::vector<std::shared_ptr<Pattern>> m_patterns;
|
||||
|
||||
bool m_running = false;
|
||||
};
|
||||
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class PatternArrayDynamic;
|
||||
class PatternArrayStatic;
|
||||
class PatternBitfield;
|
||||
class PatternBitfieldField;
|
||||
class PatternBoolean;
|
||||
class PatternCharacter;
|
||||
class PatternEnum;
|
||||
class PatternFloat;
|
||||
class PatternPadding;
|
||||
class PatternPointer;
|
||||
class PatternSigned;
|
||||
class PatternString;
|
||||
class PatternStruct;
|
||||
class PatternUnion;
|
||||
class PatternUnsigned;
|
||||
class PatternWideCharacter;
|
||||
class PatternWideString;
|
||||
|
||||
class PatternVisitor
|
||||
{
|
||||
public:
|
||||
virtual void visit(PatternArrayDynamic& pattern) = 0;
|
||||
virtual void visit(PatternArrayStatic& pattern) = 0;
|
||||
virtual void visit(PatternBitfield& pattern) = 0;
|
||||
virtual void visit(PatternBitfieldField& pattern) = 0;
|
||||
virtual void visit(PatternBoolean& pattern) = 0;
|
||||
virtual void visit(PatternCharacter& pattern) = 0;
|
||||
virtual void visit(PatternEnum& pattern) = 0;
|
||||
virtual void visit(PatternFloat& pattern) = 0;
|
||||
virtual void visit(PatternPadding& pattern) = 0;
|
||||
virtual void visit(PatternPointer& pattern) = 0;
|
||||
virtual void visit(PatternSigned& pattern) = 0;
|
||||
virtual void visit(PatternString& pattern) = 0;
|
||||
virtual void visit(PatternStruct& pattern) = 0;
|
||||
virtual void visit(PatternUnion& pattern) = 0;
|
||||
virtual void visit(PatternUnsigned& pattern) = 0;
|
||||
virtual void visit(PatternWideCharacter& pattern) = 0;
|
||||
virtual void visit(PatternWideString& pattern) = 0;
|
||||
};
|
||||
};
|
@ -1,275 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex.hpp>
|
||||
|
||||
#include <imgui.h>
|
||||
#include <hex/ui/imgui_imhex_extensions.h>
|
||||
|
||||
#include <hex/pattern_language/evaluator.hpp>
|
||||
#include <hex/pattern_language/pattern_visitor.hpp>
|
||||
#include <hex/providers/provider.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
using namespace ::std::literals::string_literals;
|
||||
|
||||
class Inlinable {
|
||||
public:
|
||||
[[nodiscard]] bool isInlined() const { return this->m_inlined; }
|
||||
void setInlined(bool inlined) { this->m_inlined = inlined; }
|
||||
|
||||
private:
|
||||
bool m_inlined = false;
|
||||
};
|
||||
|
||||
class PatternCreationLimiter {
|
||||
public:
|
||||
explicit PatternCreationLimiter(Evaluator *evaluator) : m_evaluator(evaluator) {
|
||||
if (getEvaluator() == nullptr) return;
|
||||
|
||||
getEvaluator()->patternCreated();
|
||||
}
|
||||
|
||||
PatternCreationLimiter(const PatternCreationLimiter &other) : PatternCreationLimiter(other.m_evaluator) { }
|
||||
|
||||
virtual ~PatternCreationLimiter() {
|
||||
if (getEvaluator() == nullptr) return;
|
||||
|
||||
getEvaluator()->patternDestroyed();
|
||||
}
|
||||
|
||||
[[nodiscard]] Evaluator *getEvaluator() const {
|
||||
return this->m_evaluator;
|
||||
}
|
||||
|
||||
private:
|
||||
Evaluator *m_evaluator = nullptr;
|
||||
};
|
||||
|
||||
class Pattern : public PatternCreationLimiter,
|
||||
public Cloneable<Pattern> {
|
||||
public:
|
||||
Pattern(Evaluator *evaluator, u64 offset, size_t size, u32 color = 0)
|
||||
: PatternCreationLimiter(evaluator), m_offset(offset), m_size(size), m_color(color) {
|
||||
|
||||
if (color != 0)
|
||||
return;
|
||||
|
||||
this->m_color = ContentRegistry::PatternLanguage::getNextColor();
|
||||
this->m_manualColor = false;
|
||||
}
|
||||
|
||||
Pattern(const Pattern &other) = default;
|
||||
|
||||
~Pattern() override = default;
|
||||
|
||||
[[nodiscard]] u64 getOffset() const { return this->m_offset; }
|
||||
virtual void setOffset(u64 offset) { this->m_offset = offset; }
|
||||
|
||||
[[nodiscard]] size_t getSize() const { return this->m_size; }
|
||||
void setSize(size_t size) { this->m_size = size; }
|
||||
|
||||
[[nodiscard]] const std::string &getVariableName() const { return this->m_variableName; }
|
||||
void setVariableName(std::string name) { this->m_variableName = std::move(name); }
|
||||
|
||||
[[nodiscard]] const std::optional<std::string> &getComment() const { return this->m_comment; }
|
||||
void setComment(std::string comment) { this->m_comment = std::move(comment); }
|
||||
|
||||
[[nodiscard]] virtual std::string getTypeName() const { return this->m_typeName; }
|
||||
void setTypeName(std::string name) { this->m_typeName = std::move(name); }
|
||||
|
||||
[[nodiscard]] u32 getColor() const { return this->m_color; }
|
||||
virtual void setColor(u32 color) {
|
||||
this->m_color = color;
|
||||
this->m_manualColor = true;
|
||||
}
|
||||
void setBaseColor(u32 color) {
|
||||
if (this->hasOverriddenColor())
|
||||
this->setColor(color);
|
||||
else
|
||||
this->m_color = color;
|
||||
}
|
||||
[[nodiscard]] bool hasOverriddenColor() const { return this->m_manualColor; }
|
||||
|
||||
[[nodiscard]] std::endian getEndian() const {
|
||||
if (this->getEvaluator() == nullptr) return std::endian::native;
|
||||
else return this->m_endian.value_or(this->getEvaluator()->getDefaultEndian());
|
||||
}
|
||||
virtual void setEndian(std::endian endian) { this->m_endian = endian; }
|
||||
[[nodiscard]] bool hasOverriddenEndian() const { return this->m_endian.has_value(); }
|
||||
|
||||
[[nodiscard]] std::string getDisplayName() const { return this->m_displayName.value_or(this->m_variableName); }
|
||||
void setDisplayName(const std::string &name) { this->m_displayName = name; }
|
||||
|
||||
[[nodiscard]] const auto &getTransformFunction() const { return this->m_transformFunction; }
|
||||
void setTransformFunction(const ContentRegistry::PatternLanguage::Function &function) { this->m_transformFunction = function; }
|
||||
[[nodiscard]] const auto &getFormatterFunction() const { return this->m_formatterFunction; }
|
||||
void setFormatterFunction(const ContentRegistry::PatternLanguage::Function &function) { this->m_formatterFunction = function; }
|
||||
|
||||
[[nodiscard]] virtual std::string getFormattedName() const = 0;
|
||||
|
||||
[[nodiscard]] virtual const Pattern *getPattern(u64 offset) const {
|
||||
if (offset >= this->getOffset() && offset < (this->getOffset() + this->getSize()) && !this->isHidden())
|
||||
return this;
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
virtual void getHighlightedAddresses(std::map<u64, u32> &highlight) const {
|
||||
if (this->isHidden()) return;
|
||||
|
||||
for (u64 i = 0; i < this->getSize(); i++)
|
||||
highlight.insert({ this->getOffset() + i, this->getColor() });
|
||||
|
||||
this->getEvaluator()->handleAbort();
|
||||
}
|
||||
|
||||
virtual void sort(ImGuiTableSortSpecs *sortSpecs, prv::Provider *provider) {
|
||||
hex::unused(sortSpecs, provider);
|
||||
}
|
||||
|
||||
[[nodiscard]] virtual std::string toString(prv::Provider *provider) const {
|
||||
hex::unused(provider);
|
||||
|
||||
return hex::format("{} {} @ 0x{:X}", this->getTypeName(), this->getVariableName(), this->getOffset());
|
||||
}
|
||||
|
||||
static bool sortPatternTable(ImGuiTableSortSpecs *sortSpecs, prv::Provider *provider, pl::Pattern *left, pl::Pattern *right) {
|
||||
if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("name")) {
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return left->getDisplayName() > right->getDisplayName();
|
||||
else
|
||||
return left->getDisplayName() < right->getDisplayName();
|
||||
} else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("offset")) {
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return left->getOffset() > right->getOffset();
|
||||
else
|
||||
return left->getOffset() < right->getOffset();
|
||||
} else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("size")) {
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return left->getSize() > right->getSize();
|
||||
else
|
||||
return left->getSize() < right->getSize();
|
||||
} else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("value")) {
|
||||
size_t biggerSize = std::max(left->getSize(), right->getSize());
|
||||
std::vector<u8> leftBuffer(biggerSize, 0x00), rightBuffer(biggerSize, 0x00);
|
||||
|
||||
provider->read(left->getOffset(), leftBuffer.data(), left->getSize());
|
||||
provider->read(right->getOffset(), rightBuffer.data(), right->getSize());
|
||||
|
||||
if (left->m_endian != std::endian::native)
|
||||
std::reverse(leftBuffer.begin(), leftBuffer.end());
|
||||
if (right->m_endian != std::endian::native)
|
||||
std::reverse(rightBuffer.begin(), rightBuffer.end());
|
||||
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return leftBuffer > rightBuffer;
|
||||
else
|
||||
return leftBuffer < rightBuffer;
|
||||
} else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("type")) {
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return left->getTypeName() > right->getTypeName();
|
||||
else
|
||||
return left->getTypeName() < right->getTypeName();
|
||||
} else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("color")) {
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return left->getColor() > right->getColor();
|
||||
else
|
||||
return left->getColor() < right->getColor();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void setHidden(bool hidden) {
|
||||
this->m_hidden = hidden;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool isHidden() const {
|
||||
return this->m_hidden;
|
||||
}
|
||||
|
||||
void setLocal(bool local) {
|
||||
this->m_local = local;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool isLocal() const {
|
||||
return this->m_local;
|
||||
}
|
||||
|
||||
[[nodiscard]] virtual bool operator!=(const Pattern &other) const final { return !operator==(other); }
|
||||
[[nodiscard]] virtual bool operator==(const Pattern &other) const = 0;
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]] bool areCommonPropertiesEqual(const Pattern &other) const {
|
||||
return typeid(other) == typeid(std::remove_cvref_t<T>) &&
|
||||
this->m_offset == other.m_offset &&
|
||||
this->m_size == other.m_size &&
|
||||
this->m_hidden == other.m_hidden &&
|
||||
(this->m_endian == other.m_endian || (!this->m_endian.has_value() && other.m_endian == std::endian::native) || (!other.m_endian.has_value() && this->m_endian == std::endian::native)) &&
|
||||
this->m_variableName == other.m_variableName &&
|
||||
this->m_typeName == other.m_typeName &&
|
||||
this->m_comment == other.m_comment &&
|
||||
this->m_local == other.m_local;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string calcDisplayValue(const std::string &value, const Token::Literal &literal) const {
|
||||
if (!this->m_formatterFunction.has_value())
|
||||
return value;
|
||||
else {
|
||||
try {
|
||||
auto result = this->m_formatterFunction->func(this->getEvaluator(), { literal });
|
||||
|
||||
if (result.has_value()) {
|
||||
if (auto displayValue = std::get_if<std::string>(&result.value()); displayValue != nullptr)
|
||||
return *displayValue;
|
||||
else
|
||||
return "???";
|
||||
} else {
|
||||
return "???";
|
||||
}
|
||||
} catch (PatternLanguageError &error) {
|
||||
return "Error: "s + error.what();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string formatDisplayValue(const std::string &value, const Token::Literal &literal) const {
|
||||
if (!this->m_cachedDisplayValue.has_value()) {
|
||||
this->m_cachedDisplayValue = calcDisplayValue(value, literal);
|
||||
}
|
||||
|
||||
return this->m_cachedDisplayValue.value();
|
||||
}
|
||||
|
||||
void clearFormatCache() {
|
||||
this->m_cachedDisplayValue.reset();
|
||||
}
|
||||
|
||||
virtual void accept(PatternVisitor &v) = 0;
|
||||
|
||||
protected:
|
||||
std::optional<std::endian> m_endian;
|
||||
bool m_hidden = false;
|
||||
|
||||
private:
|
||||
u64 m_offset = 0x00;
|
||||
size_t m_size = 0x00;
|
||||
|
||||
u32 m_color = 0x00;
|
||||
std::optional<std::string> m_displayName;
|
||||
mutable std::optional<std::string> m_cachedDisplayValue;
|
||||
std::string m_variableName;
|
||||
std::optional<std::string> m_comment;
|
||||
std::string m_typeName;
|
||||
|
||||
std::optional<ContentRegistry::PatternLanguage::Function> m_formatterFunction;
|
||||
std::optional<ContentRegistry::PatternLanguage::Function> m_transformFunction;
|
||||
|
||||
bool m_local = false;
|
||||
bool m_manualColor = false;
|
||||
};
|
||||
|
||||
}
|
@ -1,118 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class PatternArrayDynamic : public Pattern,
|
||||
public Inlinable {
|
||||
public:
|
||||
PatternArrayDynamic(Evaluator *evaluator, u64 offset, size_t size, u32 color = 0)
|
||||
: Pattern(evaluator, offset, size, color) {
|
||||
}
|
||||
|
||||
PatternArrayDynamic(const PatternArrayDynamic &other) : Pattern(other) {
|
||||
std::vector<std::shared_ptr<Pattern>> entries;
|
||||
for (const auto &entry : other.m_entries)
|
||||
entries.push_back(entry->clone());
|
||||
|
||||
this->setEntries(std::move(entries));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<Pattern> clone() const override {
|
||||
return std::unique_ptr<Pattern>(new PatternArrayDynamic(*this));
|
||||
}
|
||||
|
||||
void setColor(u32 color) override {
|
||||
Pattern::setColor(color);
|
||||
for (auto &entry : this->m_entries)
|
||||
entry->setColor(color);
|
||||
}
|
||||
|
||||
void getHighlightedAddresses(std::map<u64, u32> &highlight) const override {
|
||||
for (auto &entry : this->m_entries) {
|
||||
entry->getHighlightedAddresses(highlight);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string getFormattedName() const override {
|
||||
return this->m_entries[0]->getTypeName() + "[" + std::to_string(this->m_entries.size()) + "]";
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string getTypeName() const override {
|
||||
return this->m_entries[0]->getTypeName();
|
||||
}
|
||||
|
||||
void setOffset(u64 offset) override {
|
||||
for (auto &entry : this->m_entries)
|
||||
entry->setOffset(entry->getOffset() - this->getOffset() + offset);
|
||||
|
||||
Pattern::setOffset(offset);
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t getEntryCount() const {
|
||||
return this->m_entries.size();
|
||||
}
|
||||
|
||||
[[nodiscard]] const auto &getEntries() {
|
||||
return this->m_entries;
|
||||
}
|
||||
|
||||
void forEachArrayEntry(const std::function<void(u64, Pattern&)>& fn) {
|
||||
for (u64 i = 0; i < this->m_entries.size(); i++)
|
||||
fn(i, *this->m_entries[i]);
|
||||
}
|
||||
|
||||
void setEntries(std::vector<std::shared_ptr<Pattern>> &&entries) {
|
||||
this->m_entries = std::move(entries);
|
||||
|
||||
for (auto &entry : this->m_entries) {
|
||||
entry->setBaseColor(this->getColor());
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(const Pattern &other) const override {
|
||||
if (!areCommonPropertiesEqual<decltype(*this)>(other))
|
||||
return false;
|
||||
|
||||
auto &otherArray = *static_cast<const PatternArrayDynamic *>(&other);
|
||||
if (this->m_entries.size() != otherArray.m_entries.size())
|
||||
return false;
|
||||
|
||||
for (u64 i = 0; i < this->m_entries.size(); i++) {
|
||||
if (*this->m_entries[i] != *otherArray.m_entries[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] const Pattern *getPattern(u64 offset) const override {
|
||||
if (this->isHidden()) return nullptr;
|
||||
|
||||
for (auto &pattern : this->m_entries) {
|
||||
auto result = pattern->getPattern(offset);
|
||||
if (result != nullptr)
|
||||
return result;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void setEndian(std::endian endian) override {
|
||||
for (auto &entry : this->m_entries) {
|
||||
entry->setEndian(endian);
|
||||
}
|
||||
|
||||
Pattern::setEndian(endian);
|
||||
}
|
||||
|
||||
void accept(PatternVisitor &v) override {
|
||||
v.visit(*this);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::shared_ptr<Pattern>> m_entries;
|
||||
};
|
||||
|
||||
}
|
@ -1,120 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class PatternArrayStatic : public Pattern,
|
||||
public Inlinable {
|
||||
public:
|
||||
PatternArrayStatic(Evaluator *evaluator, u64 offset, size_t size, u32 color = 0)
|
||||
: Pattern(evaluator, offset, size, color) {
|
||||
}
|
||||
|
||||
PatternArrayStatic(const PatternArrayStatic &other) : Pattern(other) {
|
||||
this->setEntries(other.getTemplate()->clone(), other.getEntryCount());
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<Pattern> clone() const override {
|
||||
return std::unique_ptr<Pattern>(new PatternArrayStatic(*this));
|
||||
}
|
||||
|
||||
void forEachArrayEntry(const std::function<void(u64, Pattern&)>& fn) {
|
||||
auto entry = std::shared_ptr(this->m_template->clone());
|
||||
for (u64 index = 0; index < this->m_entryCount; index++) {
|
||||
entry->clearFormatCache();
|
||||
entry->setVariableName(hex::format("[{0}]", index));
|
||||
entry->setOffset(this->getOffset() + index * this->m_template->getSize());
|
||||
fn(index, *entry);
|
||||
}
|
||||
}
|
||||
|
||||
void getHighlightedAddresses(std::map<u64, u32> &highlight) const override {
|
||||
auto entry = this->m_template->clone();
|
||||
|
||||
for (u64 address = this->getOffset(); address < this->getOffset() + this->getSize(); address += entry->getSize()) {
|
||||
entry->setOffset(address);
|
||||
entry->getHighlightedAddresses(highlight);
|
||||
}
|
||||
}
|
||||
|
||||
void setOffset(u64 offset) override {
|
||||
this->m_template->setOffset(this->m_template->getOffset() - this->getOffset() + offset);
|
||||
|
||||
Pattern::setOffset(offset);
|
||||
}
|
||||
|
||||
void setColor(u32 color) override {
|
||||
Pattern::setColor(color);
|
||||
this->m_template->setColor(color);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string getFormattedName() const override {
|
||||
return this->m_template->getTypeName() + "[" + std::to_string(this->m_entryCount) + "]";
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string getTypeName() const override {
|
||||
return this->m_template->getTypeName();
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::shared_ptr<Pattern> &getTemplate() const {
|
||||
return this->m_template;
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t getEntryCount() const {
|
||||
return this->m_entryCount;
|
||||
}
|
||||
|
||||
void setEntryCount(size_t count) {
|
||||
this->m_entryCount = count;
|
||||
}
|
||||
|
||||
void setEntries(std::unique_ptr<Pattern> &&templatePattern, size_t count) {
|
||||
this->m_template = std::move(templatePattern);
|
||||
this->m_highlightTemplate = this->m_template->clone();
|
||||
this->m_entryCount = count;
|
||||
|
||||
this->m_template->setBaseColor(this->getColor());
|
||||
this->m_highlightTemplate->setBaseColor(this->getColor());
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(const Pattern &other) const override {
|
||||
if (!areCommonPropertiesEqual<decltype(*this)>(other))
|
||||
return false;
|
||||
|
||||
auto &otherArray = *static_cast<const PatternArrayStatic *>(&other);
|
||||
return *this->m_template == *otherArray.m_template && this->m_entryCount == otherArray.m_entryCount;
|
||||
}
|
||||
|
||||
[[nodiscard]] const Pattern *getPattern(u64 offset) const override {
|
||||
if (this->isHidden()) return nullptr;
|
||||
|
||||
this->m_highlightTemplate->setBaseColor(this->getColor());
|
||||
this->m_highlightTemplate->setVariableName(this->getVariableName());
|
||||
this->m_highlightTemplate->setDisplayName(this->getDisplayName());
|
||||
|
||||
if (offset >= this->getOffset() && offset < (this->getOffset() + this->getSize())) {
|
||||
this->m_highlightTemplate->setOffset((offset / this->m_highlightTemplate->getSize()) * this->m_highlightTemplate->getSize());
|
||||
return this->m_highlightTemplate->getPattern(offset);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void setEndian(std::endian endian) override {
|
||||
this->m_template->setEndian(endian);
|
||||
|
||||
Pattern::setEndian(endian);
|
||||
}
|
||||
|
||||
void accept(PatternVisitor &v) override {
|
||||
v.visit(*this);
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<Pattern> m_template = nullptr;
|
||||
mutable std::unique_ptr<Pattern> m_highlightTemplate = nullptr;
|
||||
size_t m_entryCount = 0;
|
||||
};
|
||||
|
||||
}
|
@ -1,141 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class PatternBitfieldField : public Pattern {
|
||||
public:
|
||||
PatternBitfieldField(Evaluator *evaluator, u64 offset, u8 bitOffset, u8 bitSize, Pattern *bitField, u32 color = 0)
|
||||
: Pattern(evaluator, offset, 0, color), m_bitOffset(bitOffset), m_bitSize(bitSize), m_bitField(bitField) {
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<Pattern> clone() const override {
|
||||
return std::unique_ptr<Pattern>(new PatternBitfieldField(*this));
|
||||
}
|
||||
|
||||
u64 getValue(prv::Provider *&provider) {
|
||||
std::vector<u8> value(this->m_bitField->getSize(), 0);
|
||||
provider->read(this->m_bitField->getOffset(), &value[0], value.size());
|
||||
|
||||
if (this->m_bitField->getEndian() != std::endian::native)
|
||||
std::reverse(value.begin(), value.end());
|
||||
|
||||
return hex::extract(this->m_bitOffset + (this->m_bitSize - 1), this->m_bitOffset, value);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string getFormattedName() const override {
|
||||
return "bits";
|
||||
}
|
||||
|
||||
[[nodiscard]] u8 getBitOffset() const {
|
||||
return this->m_bitOffset;
|
||||
}
|
||||
|
||||
[[nodiscard]] u8 getBitSize() const {
|
||||
return this->m_bitSize;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(const Pattern &other) const override {
|
||||
if (!areCommonPropertiesEqual<decltype(*this)>(other))
|
||||
return false;
|
||||
|
||||
auto &otherBitfieldField = *static_cast<const PatternBitfieldField *>(&other);
|
||||
return this->m_bitOffset == otherBitfieldField.m_bitOffset && this->m_bitSize == otherBitfieldField.m_bitSize;
|
||||
}
|
||||
|
||||
void accept(PatternVisitor &v) override {
|
||||
v.visit(*this);
|
||||
}
|
||||
|
||||
private:
|
||||
u8 m_bitOffset, m_bitSize;
|
||||
Pattern *m_bitField;
|
||||
};
|
||||
|
||||
class PatternBitfield : public Pattern,
|
||||
public Inlinable {
|
||||
public:
|
||||
PatternBitfield(Evaluator *evaluator, u64 offset, size_t size, u32 color = 0)
|
||||
: Pattern(evaluator, offset, size, color) {
|
||||
}
|
||||
|
||||
PatternBitfield(const PatternBitfield &other) : Pattern(other) {
|
||||
for (auto &field : other.m_fields)
|
||||
this->m_fields.push_back(field->clone());
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<Pattern> clone() const override {
|
||||
return std::unique_ptr<Pattern>(new PatternBitfield(*this));
|
||||
}
|
||||
|
||||
std::vector<u8> getValue(prv::Provider *&provider) const {
|
||||
std::vector<u8> value(this->getSize(), 0);
|
||||
provider->read(this->getOffset(), &value[0], value.size());
|
||||
|
||||
if (this->getEndian() == std::endian::little)
|
||||
std::reverse(value.begin(), value.end());
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
void forEachMember(const std::function<void(Pattern&)>& fn) {
|
||||
for (auto &field : this->m_fields)
|
||||
fn(*field);
|
||||
}
|
||||
|
||||
void setOffset(u64 offset) override {
|
||||
for (auto &field : this->m_fields)
|
||||
field->setOffset(field->getOffset() - this->getOffset() + offset);
|
||||
|
||||
Pattern::setOffset(offset);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string getFormattedName() const override {
|
||||
return "bitfield " + Pattern::getTypeName();
|
||||
}
|
||||
|
||||
[[nodiscard]] const auto &getFields() const {
|
||||
return this->m_fields;
|
||||
}
|
||||
|
||||
void setFields(std::vector<std::shared_ptr<Pattern>> &&fields) {
|
||||
this->m_fields = std::move(fields);
|
||||
|
||||
for (auto &field : this->m_fields) {
|
||||
field->setSize(this->getSize());
|
||||
field->setColor(this->getColor());
|
||||
}
|
||||
}
|
||||
|
||||
void setColor(u32 color) override {
|
||||
Pattern::setColor(color);
|
||||
for (auto &field : this->m_fields)
|
||||
field->setColor(color);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(const Pattern &other) const override {
|
||||
if (!areCommonPropertiesEqual<decltype(*this)>(other))
|
||||
return false;
|
||||
|
||||
auto &otherBitfield = *static_cast<const PatternBitfield *>(&other);
|
||||
if (this->m_fields.size() != otherBitfield.m_fields.size())
|
||||
return false;
|
||||
|
||||
for (u64 i = 0; i < this->m_fields.size(); i++) {
|
||||
if (*this->m_fields[i] != *otherBitfield.m_fields[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void accept(PatternVisitor &v) override {
|
||||
v.visit(*this);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::shared_ptr<Pattern>> m_fields;
|
||||
};
|
||||
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class PatternBoolean : public Pattern {
|
||||
public:
|
||||
explicit PatternBoolean(Evaluator *evaluator, u64 offset, u32 color = 0)
|
||||
: Pattern(evaluator, offset, 1, color) { }
|
||||
|
||||
[[nodiscard]] std::unique_ptr<Pattern> clone() const override {
|
||||
return std::unique_ptr<Pattern>(new PatternBoolean(*this));
|
||||
}
|
||||
|
||||
u8 getValue(prv::Provider *&provider) {
|
||||
u8 boolean;
|
||||
provider->read(this->getOffset(), &boolean, 1);
|
||||
return boolean;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string getFormattedName() const override {
|
||||
return "bool";
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(const Pattern &other) const override { return areCommonPropertiesEqual<decltype(*this)>(other); }
|
||||
|
||||
void accept(PatternVisitor &v) override {
|
||||
v.visit(*this);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class PatternCharacter : public Pattern {
|
||||
public:
|
||||
explicit PatternCharacter(Evaluator *evaluator, u64 offset, u32 color = 0)
|
||||
: Pattern(evaluator, offset, 1, color) { }
|
||||
|
||||
[[nodiscard]] std::unique_ptr<Pattern> clone() const override {
|
||||
return std::unique_ptr<Pattern>(new PatternCharacter(*this));
|
||||
}
|
||||
|
||||
char getValue(prv::Provider *&provider) {
|
||||
char character;
|
||||
provider->read(this->getOffset(), &character, 1);
|
||||
return character;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string getFormattedName() const override {
|
||||
return "char";
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(const Pattern &other) const override { return areCommonPropertiesEqual<decltype(*this)>(other); }
|
||||
|
||||
void accept(PatternVisitor &v) override {
|
||||
v.visit(*this);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
@ -1,63 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class PatternEnum : public Pattern {
|
||||
public:
|
||||
PatternEnum(Evaluator *evaluator, u64 offset, size_t size, u32 color = 0)
|
||||
: Pattern(evaluator, offset, size, color) {
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<Pattern> clone() const override {
|
||||
return std::unique_ptr<Pattern>(new PatternEnum(*this));
|
||||
}
|
||||
|
||||
u64 getValue(prv::Provider *&provider) {
|
||||
u64 value = 0;
|
||||
provider->read(this->getOffset(), &value, this->getSize());
|
||||
return hex::changeEndianess(value, this->getSize(), this->getEndian());
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string getFormattedName() const override {
|
||||
return "enum " + Pattern::getTypeName();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string getTypeName() const override {
|
||||
return Pattern::getTypeName();
|
||||
}
|
||||
|
||||
void setEnumValues(const std::vector<std::pair<Token::Literal, std::string>> &enumValues) {
|
||||
this->m_enumValues = enumValues;
|
||||
}
|
||||
|
||||
const auto& getEnumValues() const {
|
||||
return this->m_enumValues;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(const Pattern &other) const override {
|
||||
if (!areCommonPropertiesEqual<decltype(*this)>(other))
|
||||
return false;
|
||||
|
||||
auto &otherEnum = *static_cast<const PatternEnum *>(&other);
|
||||
if (this->m_enumValues.size() != otherEnum.m_enumValues.size())
|
||||
return false;
|
||||
|
||||
for (u64 i = 0; i < this->m_enumValues.size(); i++) {
|
||||
if (this->m_enumValues[i] != otherEnum.m_enumValues[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void accept(PatternVisitor &v) override {
|
||||
v.visit(*this);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::pair<Token::Literal, std::string>> m_enumValues;
|
||||
};
|
||||
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class PatternFloat : public Pattern {
|
||||
public:
|
||||
PatternFloat(Evaluator *evaluator, u64 offset, size_t size, u32 color = 0)
|
||||
: Pattern(evaluator, offset, size, color) { }
|
||||
|
||||
[[nodiscard]] std::unique_ptr<Pattern> clone() const override {
|
||||
return std::unique_ptr<Pattern>(new PatternFloat(*this));
|
||||
}
|
||||
|
||||
double getValue(prv::Provider *&provider) {
|
||||
if (this->getSize() == 4) {
|
||||
u32 data = 0;
|
||||
provider->read(this->getOffset(), &data, 4);
|
||||
data = hex::changeEndianess(data, 4, this->getEndian());
|
||||
|
||||
float result = 0;
|
||||
std::memcpy(&result, &data, sizeof(float));
|
||||
return result;
|
||||
} else if (this->getSize() == 8) {
|
||||
u64 data = 0;
|
||||
provider->read(this->getOffset(), &data, 8);
|
||||
data = hex::changeEndianess(data, 8, this->getEndian());
|
||||
|
||||
double result = 0;
|
||||
std::memcpy(&result, &data, sizeof(double));
|
||||
return result;
|
||||
} else {
|
||||
assert(false);
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string getFormattedName() const override {
|
||||
switch (this->getSize()) {
|
||||
case 4:
|
||||
return "float";
|
||||
case 8:
|
||||
return "double";
|
||||
default:
|
||||
return "Floating point data";
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(const Pattern &other) const override { return areCommonPropertiesEqual<decltype(*this)>(other); }
|
||||
|
||||
void accept(PatternVisitor &v) override {
|
||||
v.visit(*this);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class PatternPadding : public Pattern {
|
||||
public:
|
||||
PatternPadding(Evaluator *evaluator, u64 offset, size_t size) : Pattern(evaluator, offset, size, 0xFF000000) { }
|
||||
|
||||
[[nodiscard]] std::unique_ptr<Pattern> clone() const override {
|
||||
return std::unique_ptr<Pattern>(new PatternPadding(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string getFormattedName() const override {
|
||||
return "";
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(const Pattern &other) const override { return areCommonPropertiesEqual<decltype(*this)>(other); }
|
||||
|
||||
void accept(PatternVisitor &v) override {
|
||||
v.visit(*this);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
@ -1,117 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class PatternPointer : public Pattern,
|
||||
public Inlinable {
|
||||
public:
|
||||
PatternPointer(Evaluator *evaluator, u64 offset, size_t size, u32 color = 0)
|
||||
: Pattern(evaluator, offset, size, color), m_pointedAt(nullptr) {
|
||||
}
|
||||
|
||||
PatternPointer(const PatternPointer &other) : Pattern(other) {
|
||||
this->m_pointedAt = other.m_pointedAt->clone();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<Pattern> clone() const override {
|
||||
return std::unique_ptr<Pattern>(new PatternPointer(*this));
|
||||
}
|
||||
|
||||
u64 getValue(prv::Provider *&provider) {
|
||||
u64 data = 0;
|
||||
provider->read(this->getOffset(), &data, this->getSize());
|
||||
return hex::changeEndianess(data, this->getSize(), this->getEndian());
|
||||
}
|
||||
|
||||
void getHighlightedAddresses(std::map<u64, u32> &highlight) const override {
|
||||
Pattern::getHighlightedAddresses(highlight);
|
||||
this->m_pointedAt->getHighlightedAddresses(highlight);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string getFormattedName() const override {
|
||||
std::string result = (this->getTypeName().empty() ? this->m_pointedAt->getTypeName() : this->getTypeName()) + "* : ";
|
||||
switch (this->getSize()) {
|
||||
case 1:
|
||||
result += "u8";
|
||||
break;
|
||||
case 2:
|
||||
result += "u16";
|
||||
break;
|
||||
case 4:
|
||||
result += "u32";
|
||||
break;
|
||||
case 8:
|
||||
result += "u64";
|
||||
break;
|
||||
case 16:
|
||||
result += "u128";
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void setPointedAtPattern(std::unique_ptr<Pattern> &&pattern) {
|
||||
this->m_pointedAt = std::move(pattern);
|
||||
this->m_pointedAt->setVariableName(hex::format("*({})", this->getVariableName()));
|
||||
this->m_pointedAt->setOffset(this->m_pointedAtAddress);
|
||||
}
|
||||
|
||||
void setPointedAtAddress(u64 address) {
|
||||
this->m_pointedAtAddress = address;
|
||||
}
|
||||
|
||||
[[nodiscard]] u64 getPointedAtAddress() const {
|
||||
return this->m_pointedAtAddress;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::unique_ptr<Pattern> &getPointedAtPattern() {
|
||||
return this->m_pointedAt;
|
||||
}
|
||||
|
||||
void setColor(u32 color) override {
|
||||
Pattern::setColor(color);
|
||||
this->m_pointedAt->setColor(color);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(const Pattern &other) const override {
|
||||
return areCommonPropertiesEqual<decltype(*this)>(other) &&
|
||||
*static_cast<const PatternPointer *>(&other)->m_pointedAt == *this->m_pointedAt;
|
||||
}
|
||||
|
||||
void rebase(u64 base) {
|
||||
if (this->m_pointedAt != nullptr) {
|
||||
this->m_pointedAtAddress = (this->m_pointedAt->getOffset() - this->m_pointerBase) + base;
|
||||
this->m_pointedAt->setOffset(this->m_pointedAtAddress);
|
||||
}
|
||||
|
||||
this->m_pointerBase = base;
|
||||
}
|
||||
|
||||
[[nodiscard]] const Pattern *getPattern(u64 offset) const override {
|
||||
if (offset >= this->getOffset() && offset < (this->getOffset() + this->getSize()) && !this->isHidden())
|
||||
return this;
|
||||
else
|
||||
return this->m_pointedAt->getPattern(offset);
|
||||
}
|
||||
|
||||
void setEndian(std::endian endian) override {
|
||||
this->m_pointedAt->setEndian(endian);
|
||||
|
||||
Pattern::setEndian(endian);
|
||||
}
|
||||
|
||||
void accept(PatternVisitor &v) override {
|
||||
v.visit(*this);
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<Pattern> m_pointedAt;
|
||||
u64 m_pointedAtAddress = 0;
|
||||
|
||||
u64 m_pointerBase = 0;
|
||||
};
|
||||
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class PatternSigned : public Pattern {
|
||||
public:
|
||||
PatternSigned(Evaluator *evaluator, u64 offset, size_t size, u32 color = 0)
|
||||
: Pattern(evaluator, offset, size, color) { }
|
||||
|
||||
[[nodiscard]] std::unique_ptr<Pattern> clone() const override {
|
||||
return std::unique_ptr<Pattern>(new PatternSigned(*this));
|
||||
}
|
||||
|
||||
i128 getValue(prv::Provider *&provider) {
|
||||
i128 data = 0;
|
||||
provider->read(this->getOffset(), &data, this->getSize());
|
||||
data = hex::changeEndianess(data, this->getSize(), this->getEndian());
|
||||
|
||||
return hex::signExtend(this->getSize() * 8, data);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string getFormattedName() const override {
|
||||
switch (this->getSize()) {
|
||||
case 1:
|
||||
return "s8";
|
||||
case 2:
|
||||
return "s16";
|
||||
case 4:
|
||||
return "s32";
|
||||
case 8:
|
||||
return "s64";
|
||||
case 16:
|
||||
return "s128";
|
||||
default:
|
||||
return "Signed data";
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(const Pattern &other) const override { return areCommonPropertiesEqual<decltype(*this)>(other); }
|
||||
|
||||
void accept(PatternVisitor &v) override {
|
||||
v.visit(*this);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class PatternString : public Pattern {
|
||||
public:
|
||||
PatternString(Evaluator *evaluator, u64 offset, size_t size, u32 color = 0)
|
||||
: Pattern(evaluator, offset, size, color) { }
|
||||
|
||||
[[nodiscard]] std::unique_ptr<Pattern> clone() const override {
|
||||
return std::unique_ptr<Pattern>(new PatternString(*this));
|
||||
}
|
||||
|
||||
std::string getValue(prv::Provider *&provider) const {
|
||||
return this->getValue(provider, this->getSize());
|
||||
}
|
||||
|
||||
std::string getValue(prv::Provider *&provider, size_t size) const {
|
||||
std::vector<u8> buffer(size, 0x00);
|
||||
provider->read(this->getOffset(), buffer.data(), size);
|
||||
return hex::encodeByteString(buffer);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string getFormattedName() const override {
|
||||
return "String";
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string toString(prv::Provider *provider) const override {
|
||||
return this->getValue(provider);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(const Pattern &other) const override { return areCommonPropertiesEqual<decltype(*this)>(other); }
|
||||
|
||||
void accept(PatternVisitor &v) override {
|
||||
v.visit(*this);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
@ -1,133 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class PatternStruct : public Pattern,
|
||||
public Inlinable {
|
||||
public:
|
||||
PatternStruct(Evaluator *evaluator, u64 offset, size_t size, u32 color = 0)
|
||||
: Pattern(evaluator, offset, size, color) {
|
||||
}
|
||||
|
||||
PatternStruct(const PatternStruct &other) : Pattern(other) {
|
||||
for (const auto &member : other.m_members) {
|
||||
auto copy = member->clone();
|
||||
|
||||
this->m_sortedMembers.push_back(copy.get());
|
||||
this->m_members.push_back(std::move(copy));
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<Pattern> clone() const override {
|
||||
return std::unique_ptr<Pattern>(new PatternStruct(*this));
|
||||
}
|
||||
|
||||
void getHighlightedAddresses(std::map<u64, u32> &highlight) const override {
|
||||
for (auto &member : this->m_members) {
|
||||
member->getHighlightedAddresses(highlight);
|
||||
}
|
||||
}
|
||||
|
||||
void forEachMember(const std::function<void(Pattern&)>& fn) {
|
||||
for (auto &member : this->m_sortedMembers)
|
||||
fn(*member);
|
||||
}
|
||||
|
||||
void setOffset(u64 offset) override {
|
||||
for (auto &member : this->m_members)
|
||||
member->setOffset(member->getOffset() - this->getOffset() + offset);
|
||||
|
||||
Pattern::setOffset(offset);
|
||||
}
|
||||
|
||||
void setColor(u32 color) override {
|
||||
Pattern::setColor(color);
|
||||
for (auto &member : this->m_members) {
|
||||
if (!member->hasOverriddenColor())
|
||||
member->setColor(color);
|
||||
}
|
||||
}
|
||||
|
||||
void sort(ImGuiTableSortSpecs *sortSpecs, prv::Provider *provider) override {
|
||||
this->m_sortedMembers.clear();
|
||||
for (auto &member : this->m_members)
|
||||
this->m_sortedMembers.push_back(member.get());
|
||||
|
||||
std::sort(this->m_sortedMembers.begin(), this->m_sortedMembers.end(), [&sortSpecs, &provider](Pattern *left, Pattern *right) {
|
||||
return Pattern::sortPatternTable(sortSpecs, provider, left, right);
|
||||
});
|
||||
|
||||
for (auto &member : this->m_members)
|
||||
member->sort(sortSpecs, provider);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string getFormattedName() const override {
|
||||
return "struct " + Pattern::getTypeName();
|
||||
}
|
||||
|
||||
[[nodiscard]] const auto &getMembers() const {
|
||||
return this->m_members;
|
||||
}
|
||||
|
||||
void setMembers(std::vector<std::shared_ptr<Pattern>> &&members) {
|
||||
this->m_members.clear();
|
||||
|
||||
for (auto &member : members) {
|
||||
if (member == nullptr) continue;
|
||||
|
||||
this->m_sortedMembers.push_back(member.get());
|
||||
this->m_members.push_back(std::move(member));
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(const Pattern &other) const override {
|
||||
if (!areCommonPropertiesEqual<decltype(*this)>(other))
|
||||
return false;
|
||||
|
||||
auto &otherStruct = *static_cast<const PatternStruct *>(&other);
|
||||
if (this->m_members.size() != otherStruct.m_members.size())
|
||||
return false;
|
||||
|
||||
for (u64 i = 0; i < this->m_members.size(); i++) {
|
||||
if (*this->m_members[i] != *otherStruct.m_members[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] const Pattern *getPattern(u64 offset) const override {
|
||||
if (this->isHidden()) return nullptr;
|
||||
|
||||
for (auto member : this->m_members) {
|
||||
if (offset >= member->getOffset() && offset < (member->getOffset() + member->getSize())) {
|
||||
auto candidate = member->getPattern(offset);
|
||||
if (candidate != nullptr)
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void setEndian(std::endian endian) override {
|
||||
for (auto &member : this->m_members) {
|
||||
if (!member->hasOverriddenEndian())
|
||||
member->setEndian(endian);
|
||||
}
|
||||
|
||||
Pattern::setEndian(endian);
|
||||
}
|
||||
|
||||
void accept(PatternVisitor &v) override {
|
||||
v.visit(*this);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::shared_ptr<Pattern>> m_members;
|
||||
std::vector<Pattern *> m_sortedMembers;
|
||||
};
|
||||
|
||||
}
|
@ -1,132 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class PatternUnion : public Pattern,
|
||||
public Inlinable {
|
||||
public:
|
||||
PatternUnion(Evaluator *evaluator, u64 offset, size_t size, u32 color = 0)
|
||||
: Pattern(evaluator, offset, size, color) {
|
||||
}
|
||||
|
||||
PatternUnion(const PatternUnion &other) : Pattern(other) {
|
||||
for (const auto &member : other.m_members) {
|
||||
auto copy = member->clone();
|
||||
|
||||
this->m_sortedMembers.push_back(copy.get());
|
||||
this->m_members.push_back(std::move(copy));
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<Pattern> clone() const override {
|
||||
return std::unique_ptr<Pattern>(new PatternUnion(*this));
|
||||
}
|
||||
|
||||
void forEachMember(const std::function<void(Pattern&)>& fn) {
|
||||
for (auto &member : this->m_sortedMembers)
|
||||
fn(*member);
|
||||
}
|
||||
|
||||
void getHighlightedAddresses(std::map<u64, u32> &highlight) const override {
|
||||
for (auto &member : this->m_members) {
|
||||
member->getHighlightedAddresses(highlight);
|
||||
}
|
||||
}
|
||||
|
||||
void setOffset(u64 offset) override {
|
||||
for (auto &member : this->m_members)
|
||||
member->setOffset(member->getOffset() - this->getOffset() + offset);
|
||||
|
||||
Pattern::setOffset(offset);
|
||||
}
|
||||
|
||||
void setColor(u32 color) override {
|
||||
Pattern::setColor(color);
|
||||
for (auto &member : this->m_members) {
|
||||
if (!member->hasOverriddenColor())
|
||||
member->setColor(color);
|
||||
}
|
||||
}
|
||||
|
||||
void sort(ImGuiTableSortSpecs *sortSpecs, prv::Provider *provider) override {
|
||||
this->m_sortedMembers.clear();
|
||||
for (auto &member : this->m_members)
|
||||
this->m_sortedMembers.push_back(member.get());
|
||||
|
||||
std::sort(this->m_sortedMembers.begin(), this->m_sortedMembers.end(), [&sortSpecs, &provider](Pattern *left, Pattern *right) {
|
||||
return Pattern::sortPatternTable(sortSpecs, provider, left, right);
|
||||
});
|
||||
|
||||
for (auto &member : this->m_members)
|
||||
member->sort(sortSpecs, provider);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string getFormattedName() const override {
|
||||
return "union " + Pattern::getTypeName();
|
||||
}
|
||||
|
||||
[[nodiscard]] const auto &getMembers() const {
|
||||
return this->m_members;
|
||||
}
|
||||
|
||||
void setMembers(std::vector<std::shared_ptr<Pattern>> &&members) {
|
||||
this->m_members.clear();
|
||||
for (auto &member : members) {
|
||||
if (member == nullptr) continue;
|
||||
|
||||
this->m_sortedMembers.push_back(member.get());
|
||||
this->m_members.push_back(std::move(member));
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(const Pattern &other) const override {
|
||||
if (!areCommonPropertiesEqual<decltype(*this)>(other))
|
||||
return false;
|
||||
|
||||
auto &otherUnion = *static_cast<const PatternUnion *>(&other);
|
||||
if (this->m_members.size() != otherUnion.m_members.size())
|
||||
return false;
|
||||
|
||||
for (u64 i = 0; i < this->m_members.size(); i++) {
|
||||
if (*this->m_members[i] != *otherUnion.m_members[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] const Pattern *getPattern(u64 offset) const override {
|
||||
if (this->isHidden()) return nullptr;
|
||||
|
||||
auto largestMember = std::find_if(this->m_members.begin(), this->m_members.end(), [this](const std::shared_ptr<Pattern> &pattern) {
|
||||
return pattern->getSize() == this->getSize();
|
||||
});
|
||||
|
||||
if (largestMember == this->m_members.end())
|
||||
return nullptr;
|
||||
else
|
||||
return (*largestMember)->getPattern(offset);
|
||||
;
|
||||
}
|
||||
|
||||
void setEndian(std::endian endian) override {
|
||||
for (auto &member : this->m_members) {
|
||||
if (!member->hasOverriddenEndian())
|
||||
member->setEndian(endian);
|
||||
}
|
||||
|
||||
Pattern::setEndian(endian);
|
||||
}
|
||||
|
||||
void accept(PatternVisitor &v) override {
|
||||
v.visit(*this);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::shared_ptr<Pattern>> m_members;
|
||||
std::vector<Pattern *> m_sortedMembers;
|
||||
};
|
||||
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class PatternUnsigned : public Pattern {
|
||||
public:
|
||||
PatternUnsigned(Evaluator *evaluator, u64 offset, size_t size, u32 color = 0)
|
||||
: Pattern(evaluator, offset, size, color) { }
|
||||
|
||||
[[nodiscard]] std::unique_ptr<Pattern> clone() const override {
|
||||
return std::unique_ptr<Pattern>(new PatternUnsigned(*this));
|
||||
}
|
||||
|
||||
u128 getValue(prv::Provider *&provider) {
|
||||
u128 data = 0;
|
||||
provider->read(this->getOffset(), &data, this->getSize());
|
||||
return hex::changeEndianess(data, this->getSize(), this->getEndian());
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string getFormattedName() const override {
|
||||
switch (this->getSize()) {
|
||||
case 1:
|
||||
return "u8";
|
||||
case 2:
|
||||
return "u16";
|
||||
case 4:
|
||||
return "u32";
|
||||
case 8:
|
||||
return "u64";
|
||||
case 16:
|
||||
return "u128";
|
||||
default:
|
||||
return "Unsigned data";
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(const Pattern &other) const override { return areCommonPropertiesEqual<decltype(*this)>(other); }
|
||||
|
||||
void accept(PatternVisitor &v) override {
|
||||
v.visit(*this);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern.hpp>
|
||||
|
||||
#include <codecvt>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class PatternWideCharacter : public Pattern {
|
||||
public:
|
||||
explicit PatternWideCharacter(Evaluator *evaluator, u64 offset, u32 color = 0)
|
||||
: Pattern(evaluator, offset, 2, color) { }
|
||||
|
||||
[[nodiscard]] std::unique_ptr<Pattern> clone() const override {
|
||||
return std::unique_ptr<Pattern>(new PatternWideCharacter(*this));
|
||||
}
|
||||
|
||||
char16_t getValue(prv::Provider *&provider) {
|
||||
char16_t character;
|
||||
provider->read(this->getOffset(), &character, 2);
|
||||
return hex::changeEndianess(character, this->getEndian());
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string getFormattedName() const override {
|
||||
return "char16";
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string toString(prv::Provider *provider) const override {
|
||||
char16_t character;
|
||||
provider->read(this->getOffset(), &character, 2);
|
||||
character = hex::changeEndianess(character, this->getEndian());
|
||||
|
||||
return std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> {}.to_bytes(character);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(const Pattern &other) const override { return areCommonPropertiesEqual<decltype(*this)>(other); }
|
||||
|
||||
void accept(PatternVisitor &v) override {
|
||||
v.visit(*this);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern.hpp>
|
||||
|
||||
#include <codecvt>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class PatternWideString : public Pattern {
|
||||
public:
|
||||
PatternWideString(Evaluator *evaluator, u64 offset, size_t size, u32 color = 0)
|
||||
: Pattern(evaluator, offset, size, color) { }
|
||||
|
||||
[[nodiscard]] std::unique_ptr<Pattern> clone() const override {
|
||||
return std::unique_ptr<Pattern>(new PatternWideString(*this));
|
||||
}
|
||||
|
||||
std::string getValue(prv::Provider *&provider) {
|
||||
return this->getValue(provider, this->getSize());
|
||||
}
|
||||
|
||||
std::string getValue(prv::Provider *&provider, size_t size) {
|
||||
std::u16string buffer(this->getSize() / sizeof(char16_t), 0x00);
|
||||
provider->read(this->getOffset(), buffer.data(), size);
|
||||
|
||||
for (auto &c : buffer)
|
||||
c = hex::changeEndianess(c, 2, this->getEndian());
|
||||
|
||||
auto it = std::remove_if(buffer.begin(), buffer.end(),
|
||||
[](auto c) { return c == 0x00; });
|
||||
buffer.erase(it, buffer.end());
|
||||
|
||||
return std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> {}.to_bytes(buffer);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string getFormattedName() const override {
|
||||
return "String16";
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string toString(prv::Provider *provider) const override {
|
||||
std::u16string buffer(this->getSize() / sizeof(char16_t), 0x00);
|
||||
provider->read(this->getOffset(), buffer.data(), this->getSize());
|
||||
|
||||
for (auto &c : buffer)
|
||||
c = hex::changeEndianess(c, 2, this->getEndian());
|
||||
|
||||
std::erase_if(buffer, [](auto c) {
|
||||
return c == 0x00;
|
||||
});
|
||||
|
||||
return std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> {}.to_bytes(buffer);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(const Pattern &other) const override { return areCommonPropertiesEqual<decltype(*this)>(other); }
|
||||
|
||||
void accept(PatternVisitor &v) override {
|
||||
v.visit(*this);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
#include <hex/helpers/fs.hpp>
|
||||
|
||||
#include <hex/pattern_language/error.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class Preprocessor {
|
||||
public:
|
||||
Preprocessor() = default;
|
||||
|
||||
std::optional<std::string> preprocess(std::string code, bool initialRun = true);
|
||||
|
||||
void addPragmaHandler(const std::string &pragmaType, const std::function<bool(const std::string &)> &function);
|
||||
void removePragmaHandler(const std::string &pragmaType);
|
||||
void addDefaultPragmaHandlers();
|
||||
|
||||
const std::optional<PatternLanguageError> &getError() { return this->m_error; }
|
||||
|
||||
[[nodiscard]] bool shouldOnlyIncludeOnce() const {
|
||||
return this->m_onlyIncludeOnce;
|
||||
}
|
||||
|
||||
private:
|
||||
[[noreturn]] static void throwPreprocessorError(const std::string &error, u32 lineNumber) {
|
||||
throw PatternLanguageError(lineNumber, "Preprocessor: " + error);
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, std::function<bool(std::string)>> m_pragmaHandlers;
|
||||
|
||||
std::set<std::tuple<std::string, std::string, u32>> m_defines;
|
||||
std::set<std::tuple<std::string, std::string, u32>> m_pragmas;
|
||||
|
||||
std::set<std::fs::path> m_onceIncludedFiles;
|
||||
|
||||
std::optional<PatternLanguageError> m_error;
|
||||
|
||||
bool m_onlyIncludeOnce = false;
|
||||
};
|
||||
|
||||
}
|
@ -1,365 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex.hpp>
|
||||
|
||||
#include <utility>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
|
||||
#include <hex/helpers/utils.hpp>
|
||||
#include <hex/pattern_language/log_console.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNode;
|
||||
class Pattern;
|
||||
|
||||
class Token {
|
||||
public:
|
||||
enum class Type : u64
|
||||
{
|
||||
Keyword,
|
||||
ValueType,
|
||||
Operator,
|
||||
Integer,
|
||||
String,
|
||||
Identifier,
|
||||
Separator
|
||||
};
|
||||
|
||||
enum class Keyword
|
||||
{
|
||||
Struct,
|
||||
Union,
|
||||
Using,
|
||||
Enum,
|
||||
Bitfield,
|
||||
LittleEndian,
|
||||
BigEndian,
|
||||
If,
|
||||
Else,
|
||||
Parent,
|
||||
This,
|
||||
While,
|
||||
For,
|
||||
Function,
|
||||
Return,
|
||||
Namespace,
|
||||
In,
|
||||
Out,
|
||||
Break,
|
||||
Continue
|
||||
};
|
||||
|
||||
enum class Operator
|
||||
{
|
||||
AtDeclaration,
|
||||
Assignment,
|
||||
Inherit,
|
||||
Plus,
|
||||
Minus,
|
||||
Star,
|
||||
Slash,
|
||||
Percent,
|
||||
ShiftLeft,
|
||||
ShiftRight,
|
||||
BitOr,
|
||||
BitAnd,
|
||||
BitXor,
|
||||
BitNot,
|
||||
BoolEquals,
|
||||
BoolNotEquals,
|
||||
BoolGreaterThan,
|
||||
BoolLessThan,
|
||||
BoolGreaterThanOrEquals,
|
||||
BoolLessThanOrEquals,
|
||||
BoolAnd,
|
||||
BoolOr,
|
||||
BoolXor,
|
||||
BoolNot,
|
||||
TernaryConditional,
|
||||
Dollar,
|
||||
AddressOf,
|
||||
SizeOf,
|
||||
ScopeResolution
|
||||
};
|
||||
|
||||
enum class ValueType
|
||||
{
|
||||
Unsigned8Bit = 0x10,
|
||||
Signed8Bit = 0x11,
|
||||
Unsigned16Bit = 0x20,
|
||||
Signed16Bit = 0x21,
|
||||
Unsigned32Bit = 0x40,
|
||||
Signed32Bit = 0x41,
|
||||
Unsigned64Bit = 0x80,
|
||||
Signed64Bit = 0x81,
|
||||
Unsigned128Bit = 0x100,
|
||||
Signed128Bit = 0x101,
|
||||
Character = 0x13,
|
||||
Character16 = 0x23,
|
||||
Boolean = 0x14,
|
||||
Float = 0x42,
|
||||
Double = 0x82,
|
||||
String = 0x15,
|
||||
Auto = 0x16,
|
||||
CustomType = 0x00,
|
||||
Padding = 0x1F,
|
||||
|
||||
Unsigned = 0xFF00,
|
||||
Signed = 0xFF01,
|
||||
FloatingPoint = 0xFF02,
|
||||
Integer = 0xFF03,
|
||||
Any = 0xFFFF
|
||||
};
|
||||
|
||||
enum class Separator
|
||||
{
|
||||
RoundBracketOpen,
|
||||
RoundBracketClose,
|
||||
CurlyBracketOpen,
|
||||
CurlyBracketClose,
|
||||
SquareBracketOpen,
|
||||
SquareBracketClose,
|
||||
Comma,
|
||||
Dot,
|
||||
EndOfExpression,
|
||||
EndOfProgram
|
||||
};
|
||||
|
||||
struct Identifier {
|
||||
explicit Identifier(std::string identifier) : m_identifier(std::move(identifier)) { }
|
||||
|
||||
[[nodiscard]] const std::string &get() const { return this->m_identifier; }
|
||||
|
||||
bool operator==(const Identifier &) const = default;
|
||||
|
||||
private:
|
||||
std::string m_identifier;
|
||||
};
|
||||
|
||||
using Literal = std::variant<char, bool, u128, i128, double, std::string, Pattern *>;
|
||||
using ValueTypes = std::variant<Keyword, Identifier, Operator, Literal, ValueType, Separator>;
|
||||
|
||||
Token(Type type, auto value, u32 lineNumber) : type(type), value(value), lineNumber(lineNumber) {
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr static inline bool isUnsigned(const ValueType type) {
|
||||
return (static_cast<u32>(type) & 0x0F) == 0x00;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr static inline bool isSigned(const ValueType type) {
|
||||
return (static_cast<u32>(type) & 0x0F) == 0x01;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr static inline bool isFloatingPoint(const ValueType type) {
|
||||
return (static_cast<u32>(type) & 0x0F) == 0x02;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr static inline u32 getTypeSize(const ValueType type) {
|
||||
return static_cast<u32>(type) >> 4;
|
||||
}
|
||||
|
||||
static u128 literalToUnsigned(const pl::Token::Literal &literal) {
|
||||
return std::visit(overloaded {
|
||||
[](const std::string &) -> u128 { LogConsole::abortEvaluation("expected integral type, got string"); },
|
||||
[](Pattern *) -> u128 { LogConsole::abortEvaluation("expected integral type, got custom type"); },
|
||||
[](auto &&result) -> u128 { return result; } },
|
||||
literal);
|
||||
}
|
||||
|
||||
static i128 literalToSigned(const pl::Token::Literal &literal) {
|
||||
return std::visit(overloaded {
|
||||
[](const std::string &) -> i128 { LogConsole::abortEvaluation("expected integral type, got string"); },
|
||||
[](Pattern *) -> i128 { LogConsole::abortEvaluation("expected integral type, got custom type"); },
|
||||
[](auto &&result) -> i128 { return result; } },
|
||||
literal);
|
||||
}
|
||||
|
||||
static double literalToFloatingPoint(const pl::Token::Literal &literal) {
|
||||
return std::visit(overloaded {
|
||||
[](const std::string &) -> double { LogConsole::abortEvaluation("expected integral type, got string"); },
|
||||
[](Pattern *) -> double { LogConsole::abortEvaluation("expected integral type, got custom type"); },
|
||||
[](auto &&result) -> double { return result; } },
|
||||
literal);
|
||||
}
|
||||
|
||||
static bool literalToBoolean(const pl::Token::Literal &literal) {
|
||||
return std::visit(overloaded {
|
||||
[](const std::string &) -> bool { LogConsole::abortEvaluation("expected integral type, got string"); },
|
||||
[](Pattern *) -> bool { LogConsole::abortEvaluation("expected integral type, got custom type"); },
|
||||
[](auto &&result) -> bool { return result != 0; } },
|
||||
literal);
|
||||
}
|
||||
|
||||
static std::string literalToString(const pl::Token::Literal &literal, bool cast) {
|
||||
if (!cast && std::get_if<std::string>(&literal) == nullptr)
|
||||
LogConsole::abortEvaluation("expected string type, got integral");
|
||||
|
||||
return std::visit(overloaded {
|
||||
[](std::string result) -> std::string { return result; },
|
||||
[](u128 result) -> std::string { return hex::to_string(result); },
|
||||
[](i128 result) -> std::string { return hex::to_string(result); },
|
||||
[](bool result) -> std::string { return result ? "true" : "false"; },
|
||||
[](char result) -> std::string { return { 1, result }; },
|
||||
[](Pattern *) -> std::string { LogConsole::abortEvaluation("expected integral type, got custom type"); },
|
||||
[](auto &&result) -> std::string { return std::to_string(result); } },
|
||||
literal);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr static auto getTypeName(const pl::Token::ValueType type) {
|
||||
switch (type) {
|
||||
case ValueType::Signed8Bit:
|
||||
return "s8";
|
||||
case ValueType::Signed16Bit:
|
||||
return "s16";
|
||||
case ValueType::Signed32Bit:
|
||||
return "s32";
|
||||
case ValueType::Signed64Bit:
|
||||
return "s64";
|
||||
case ValueType::Signed128Bit:
|
||||
return "s128";
|
||||
case ValueType::Unsigned8Bit:
|
||||
return "u8";
|
||||
case ValueType::Unsigned16Bit:
|
||||
return "u16";
|
||||
case ValueType::Unsigned32Bit:
|
||||
return "u32";
|
||||
case ValueType::Unsigned64Bit:
|
||||
return "u64";
|
||||
case ValueType::Unsigned128Bit:
|
||||
return "u128";
|
||||
case ValueType::Float:
|
||||
return "float";
|
||||
case ValueType::Double:
|
||||
return "double";
|
||||
case ValueType::Character:
|
||||
return "char";
|
||||
case ValueType::Character16:
|
||||
return "char16";
|
||||
case ValueType::Padding:
|
||||
return "padding";
|
||||
case ValueType::String:
|
||||
return "str";
|
||||
case ValueType::Boolean:
|
||||
return "bool";
|
||||
default:
|
||||
return "< ??? >";
|
||||
}
|
||||
}
|
||||
|
||||
bool operator==(const ValueTypes &other) const {
|
||||
if (this->type == Type::Integer || this->type == Type::Identifier || this->type == Type::String)
|
||||
return true;
|
||||
else if (this->type == Type::ValueType) {
|
||||
auto otherValueType = std::get_if<ValueType>(&other);
|
||||
auto valueType = std::get_if<ValueType>(&this->value);
|
||||
|
||||
if (otherValueType == nullptr) return false;
|
||||
if (valueType == nullptr) return false;
|
||||
|
||||
if (*otherValueType == *valueType)
|
||||
return true;
|
||||
else if (*otherValueType == ValueType::Any)
|
||||
return *valueType != ValueType::CustomType && *valueType != ValueType::Padding;
|
||||
else if (*otherValueType == ValueType::Unsigned)
|
||||
return isUnsigned(*valueType);
|
||||
else if (*otherValueType == ValueType::Signed)
|
||||
return isSigned(*valueType);
|
||||
else if (*otherValueType == ValueType::FloatingPoint)
|
||||
return isFloatingPoint(*valueType);
|
||||
else if (*otherValueType == ValueType::Integer)
|
||||
return isUnsigned(*valueType) || isSigned(*valueType);
|
||||
} else
|
||||
return other == this->value;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool operator!=(const ValueTypes &other) const {
|
||||
return !operator==(other);
|
||||
}
|
||||
|
||||
Type type;
|
||||
ValueTypes value;
|
||||
u32 lineNumber;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#define COMPONENT(type, value) hex::pl::Token::Type::type, hex::pl::Token::type::value
|
||||
|
||||
#define KEYWORD_STRUCT COMPONENT(Keyword, Struct)
|
||||
#define KEYWORD_UNION COMPONENT(Keyword, Union)
|
||||
#define KEYWORD_USING COMPONENT(Keyword, Using)
|
||||
#define KEYWORD_ENUM COMPONENT(Keyword, Enum)
|
||||
#define KEYWORD_BITFIELD COMPONENT(Keyword, Bitfield)
|
||||
#define KEYWORD_LE COMPONENT(Keyword, LittleEndian)
|
||||
#define KEYWORD_BE COMPONENT(Keyword, BigEndian)
|
||||
#define KEYWORD_IF COMPONENT(Keyword, If)
|
||||
#define KEYWORD_ELSE COMPONENT(Keyword, Else)
|
||||
#define KEYWORD_PARENT COMPONENT(Keyword, Parent)
|
||||
#define KEYWORD_THIS COMPONENT(Keyword, This)
|
||||
#define KEYWORD_WHILE COMPONENT(Keyword, While)
|
||||
#define KEYWORD_FOR COMPONENT(Keyword, For)
|
||||
#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 KEYWORD_BREAK COMPONENT(Keyword, Break)
|
||||
#define KEYWORD_CONTINUE COMPONENT(Keyword, Continue)
|
||||
|
||||
#define INTEGER hex::pl::Token::Type::Integer, hex::pl::Token::Literal(u128(0))
|
||||
#define IDENTIFIER hex::pl::Token::Type::Identifier, ""
|
||||
#define STRING hex::pl::Token::Type::String, hex::pl::Token::Literal("")
|
||||
|
||||
#define OPERATOR_ANY COMPONENT(Operator, Any)
|
||||
#define OPERATOR_AT COMPONENT(Operator, AtDeclaration)
|
||||
#define OPERATOR_ASSIGNMENT COMPONENT(Operator, Assignment)
|
||||
#define OPERATOR_INHERIT COMPONENT(Operator, Inherit)
|
||||
#define OPERATOR_PLUS COMPONENT(Operator, Plus)
|
||||
#define OPERATOR_MINUS COMPONENT(Operator, Minus)
|
||||
#define OPERATOR_STAR COMPONENT(Operator, Star)
|
||||
#define OPERATOR_SLASH COMPONENT(Operator, Slash)
|
||||
#define OPERATOR_PERCENT COMPONENT(Operator, Percent)
|
||||
#define OPERATOR_SHIFTLEFT COMPONENT(Operator, ShiftLeft)
|
||||
#define OPERATOR_SHIFTRIGHT COMPONENT(Operator, ShiftRight)
|
||||
#define OPERATOR_BITOR COMPONENT(Operator, BitOr)
|
||||
#define OPERATOR_BITAND COMPONENT(Operator, BitAnd)
|
||||
#define OPERATOR_BITXOR COMPONENT(Operator, BitXor)
|
||||
#define OPERATOR_BITNOT COMPONENT(Operator, BitNot)
|
||||
#define OPERATOR_BOOLEQUALS COMPONENT(Operator, BoolEquals)
|
||||
#define OPERATOR_BOOLNOTEQUALS COMPONENT(Operator, BoolNotEquals)
|
||||
#define OPERATOR_BOOLGREATERTHAN COMPONENT(Operator, BoolGreaterThan)
|
||||
#define OPERATOR_BOOLLESSTHAN COMPONENT(Operator, BoolLessThan)
|
||||
#define OPERATOR_BOOLGREATERTHANOREQUALS COMPONENT(Operator, BoolGreaterThanOrEquals)
|
||||
#define OPERATOR_BOOLLESSTHANOREQUALS COMPONENT(Operator, BoolLessThanOrEquals)
|
||||
#define OPERATOR_BOOLAND COMPONENT(Operator, BoolAnd)
|
||||
#define OPERATOR_BOOLOR COMPONENT(Operator, BoolOr)
|
||||
#define OPERATOR_BOOLXOR COMPONENT(Operator, BoolXor)
|
||||
#define OPERATOR_BOOLNOT COMPONENT(Operator, BoolNot)
|
||||
#define OPERATOR_TERNARYCONDITIONAL COMPONENT(Operator, TernaryConditional)
|
||||
#define OPERATOR_DOLLAR COMPONENT(Operator, Dollar)
|
||||
#define OPERATOR_ADDRESSOF COMPONENT(Operator, AddressOf)
|
||||
#define OPERATOR_SIZEOF COMPONENT(Operator, SizeOf)
|
||||
#define OPERATOR_SCOPERESOLUTION COMPONENT(Operator, ScopeResolution)
|
||||
|
||||
#define VALUETYPE_CUSTOMTYPE COMPONENT(ValueType, CustomType)
|
||||
#define VALUETYPE_PADDING COMPONENT(ValueType, Padding)
|
||||
#define VALUETYPE_UNSIGNED COMPONENT(ValueType, Unsigned)
|
||||
#define VALUETYPE_SIGNED COMPONENT(ValueType, Signed)
|
||||
#define VALUETYPE_FLOATINGPOINT COMPONENT(ValueType, FloatingPoint)
|
||||
#define VALUETYPE_AUTO COMPONENT(ValueType, Auto)
|
||||
#define VALUETYPE_ANY COMPONENT(ValueType, Any)
|
||||
|
||||
#define SEPARATOR_ROUNDBRACKETOPEN COMPONENT(Separator, RoundBracketOpen)
|
||||
#define SEPARATOR_ROUNDBRACKETCLOSE COMPONENT(Separator, RoundBracketClose)
|
||||
#define SEPARATOR_CURLYBRACKETOPEN COMPONENT(Separator, CurlyBracketOpen)
|
||||
#define SEPARATOR_CURLYBRACKETCLOSE COMPONENT(Separator, CurlyBracketClose)
|
||||
#define SEPARATOR_SQUAREBRACKETOPEN COMPONENT(Separator, SquareBracketOpen)
|
||||
#define SEPARATOR_SQUAREBRACKETCLOSE COMPONENT(Separator, SquareBracketClose)
|
||||
#define SEPARATOR_COMMA COMPONENT(Separator, Comma)
|
||||
#define SEPARATOR_DOT COMPONENT(Separator, Dot)
|
||||
#define SEPARATOR_ENDOFEXPRESSION COMPONENT(Separator, EndOfExpression)
|
||||
#define SEPARATOR_ENDOFPROGRAM COMPONENT(Separator, EndOfProgram)
|
@ -1,32 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <hex/pattern_language/error.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNode;
|
||||
|
||||
class Validator {
|
||||
public:
|
||||
Validator() = default;
|
||||
|
||||
bool validate(const std::vector<std::shared_ptr<ASTNode>> &ast);
|
||||
|
||||
const std::optional<PatternLanguageError> &getError() { return this->m_error; }
|
||||
|
||||
private:
|
||||
std::optional<PatternLanguageError> m_error;
|
||||
|
||||
[[noreturn]] static void throwValidatorError(const std::string &error, u32 lineNumber) {
|
||||
throw PatternLanguageError(lineNumber, "Validator: " + error);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
@ -11,7 +11,7 @@
|
||||
#include <hex/providers/overlay.hpp>
|
||||
#include <hex/helpers/fs.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
namespace pl {
|
||||
class PatternLanguage;
|
||||
}
|
||||
|
||||
|
@ -220,7 +220,7 @@ namespace hex {
|
||||
|
||||
namespace ContentRegistry::PatternLanguage {
|
||||
|
||||
static std::string getFunctionName(const Namespace &ns, const std::string &name) {
|
||||
static std::string getFunctionName(const pl::api::Namespace &ns, const std::string &name) {
|
||||
std::string functionName;
|
||||
for (auto &scope : ns)
|
||||
functionName += scope + "::";
|
||||
@ -230,63 +230,67 @@ namespace hex {
|
||||
return functionName;
|
||||
}
|
||||
|
||||
void addFunction(const Namespace &ns, const std::string &name, ParameterCount parameterCount, const Callback &func) {
|
||||
std::unique_ptr<pl::PatternLanguage> createDefaultRuntime(prv::Provider *provider) {
|
||||
auto runtime = std::make_unique<pl::PatternLanguage>();
|
||||
|
||||
runtime->setDataSource([provider](u64 offset, u8 *buffer, size_t size) {
|
||||
provider->read(offset, buffer, size);
|
||||
}, 0, 0);
|
||||
|
||||
runtime->setIncludePaths(fs::getDefaultPaths(fs::ImHexPath::PatternsInclude));
|
||||
|
||||
for (const auto &func : getFunctions()) {
|
||||
if (func.dangerous)
|
||||
runtime->addDangerousFunction(func.ns, func.name, func.parameterCount, func.callback);
|
||||
else
|
||||
runtime->addFunction(func.ns, func.name, func.parameterCount, func.callback);
|
||||
}
|
||||
|
||||
for (const auto &[name, callback] : getPragmas()) {
|
||||
runtime->addPragma(name, callback);
|
||||
}
|
||||
|
||||
return runtime;
|
||||
}
|
||||
|
||||
void addPragma(const std::string &name, const pl::api::PragmaHandler &handler) {
|
||||
log::info("Registered new pattern language pragma: {}", name);
|
||||
|
||||
getPragmas()[name] = handler;
|
||||
}
|
||||
|
||||
void addFunction(const pl::api::Namespace &ns, const std::string &name, pl::api::FunctionParameterCount parameterCount, const pl::api::FunctionCallback &func) {
|
||||
log::info("Registered new pattern language function: {}", getFunctionName(ns, name));
|
||||
|
||||
getFunctions()[getFunctionName(ns, name)] = Function { parameterCount, { }, func, false };
|
||||
getFunctions().push_back({
|
||||
ns, name,
|
||||
parameterCount, func,
|
||||
false
|
||||
});
|
||||
}
|
||||
|
||||
void addDangerousFunction(const Namespace &ns, const std::string &name, ParameterCount parameterCount, const Callback &func) {
|
||||
void addDangerousFunction(const pl::api::Namespace &ns, const std::string &name, pl::api::FunctionParameterCount parameterCount, const pl::api::FunctionCallback &func) {
|
||||
log::info("Registered new dangerous pattern language function: {}", getFunctionName(ns, name));
|
||||
|
||||
getFunctions()[getFunctionName(ns, name)] = Function { parameterCount, { }, func, true };
|
||||
getFunctions().push_back({
|
||||
ns, name,
|
||||
parameterCount, func,
|
||||
true
|
||||
});
|
||||
}
|
||||
|
||||
std::map<std::string, Function> &getFunctions() {
|
||||
static std::map<std::string, Function> functions;
|
||||
std::map<std::string, pl::api::PragmaHandler> &getPragmas() {
|
||||
static std::map<std::string, pl::api::PragmaHandler> pragmas;
|
||||
|
||||
return pragmas;
|
||||
}
|
||||
|
||||
std::vector<impl::FunctionDefinition> &getFunctions() {
|
||||
static std::vector<impl::FunctionDefinition> functions;
|
||||
|
||||
return functions;
|
||||
}
|
||||
|
||||
|
||||
static std::vector<impl::ColorPalette> s_colorPalettes;
|
||||
static u32 s_colorIndex;
|
||||
static u32 s_selectedColorPalette;
|
||||
|
||||
std::vector<impl::ColorPalette> &getPalettes() {
|
||||
return s_colorPalettes;
|
||||
}
|
||||
|
||||
void addColorPalette(const std::string &unlocalizedName, const std::vector<u32> &colors) {
|
||||
s_colorPalettes.push_back({ unlocalizedName,
|
||||
colors });
|
||||
}
|
||||
|
||||
void setSelectedPalette(u32 index) {
|
||||
if (index < s_colorPalettes.size())
|
||||
s_selectedColorPalette = index;
|
||||
|
||||
resetPalette();
|
||||
}
|
||||
|
||||
u32 getNextColor() {
|
||||
if (s_colorPalettes.empty())
|
||||
return 0x00;
|
||||
|
||||
auto &currColors = s_colorPalettes[s_selectedColorPalette].colors;
|
||||
|
||||
u32 color = currColors[s_colorIndex];
|
||||
|
||||
s_colorIndex++;
|
||||
s_colorIndex %= currColors.size();
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
void resetPalette() {
|
||||
s_colorIndex = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,254 +0,0 @@
|
||||
#include <hex/pattern_language/evaluator.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern.hpp>
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_type_decl.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_variable_decl.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_function_call.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_function_definition.hpp>
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern_unsigned.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_signed.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_float.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_boolean.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_character.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_string.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_enum.hpp>
|
||||
|
||||
#include <hex/helpers/logger.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
void Evaluator::createParameterPack(const std::string &name, const std::vector<Token::Literal> &values) {
|
||||
this->getScope(0).parameterPack = ParameterPack {
|
||||
name,
|
||||
values
|
||||
};
|
||||
}
|
||||
|
||||
void Evaluator::createVariable(const std::string &name, ASTNode *type, const std::optional<Token::Literal> &value, bool outVariable) {
|
||||
auto &variables = *this->getScope(0).scope;
|
||||
for (auto &variable : variables) {
|
||||
if (variable->getVariableName() == name) {
|
||||
LogConsole::abortEvaluation(hex::format("variable with name '{}' already exists", name));
|
||||
}
|
||||
}
|
||||
|
||||
auto startOffset = this->dataOffset();
|
||||
|
||||
std::unique_ptr<Pattern> pattern;
|
||||
|
||||
bool referenceType = false;
|
||||
|
||||
auto typePattern = type->createPatterns(this);
|
||||
|
||||
this->dataOffset() = startOffset;
|
||||
|
||||
if (typePattern.empty()) {
|
||||
// Handle auto variables
|
||||
if (!value.has_value())
|
||||
LogConsole::abortEvaluation("cannot determine type of auto variable", type);
|
||||
|
||||
if (std::get_if<u128>(&value.value()) != nullptr)
|
||||
pattern = std::unique_ptr<Pattern>(new PatternUnsigned(this, 0, sizeof(u128)));
|
||||
else if (std::get_if<i128>(&value.value()) != nullptr)
|
||||
pattern = std::unique_ptr<Pattern>(new PatternSigned(this, 0, sizeof(i128)));
|
||||
else if (std::get_if<double>(&value.value()) != nullptr)
|
||||
pattern = std::unique_ptr<Pattern>(new PatternFloat(this, 0, sizeof(double)));
|
||||
else if (std::get_if<bool>(&value.value()) != nullptr)
|
||||
pattern = std::unique_ptr<Pattern>(new PatternBoolean(this, 0));
|
||||
else if (std::get_if<char>(&value.value()) != nullptr)
|
||||
pattern = std::unique_ptr<Pattern>(new PatternCharacter(this, 0));
|
||||
else if (std::get_if<std::string>(&value.value()) != nullptr)
|
||||
pattern = std::unique_ptr<Pattern>(new PatternString(this, 0, 1));
|
||||
else if (auto patternValue = std::get_if<Pattern *>(&value.value()); patternValue != nullptr) {
|
||||
pattern = (*patternValue)->clone();
|
||||
referenceType = true;
|
||||
} else
|
||||
LogConsole::abortEvaluation("cannot determine type of auto variable", type);
|
||||
} else {
|
||||
pattern = std::move(typePattern.front());
|
||||
}
|
||||
|
||||
pattern->setVariableName(name);
|
||||
|
||||
if (!referenceType) {
|
||||
pattern->setOffset(this->getStack().size());
|
||||
pattern->setLocal(true);
|
||||
this->getStack().emplace_back();
|
||||
}
|
||||
|
||||
if (outVariable)
|
||||
this->m_outVariables[name] = pattern->getOffset();
|
||||
|
||||
variables.push_back(std::move(pattern));
|
||||
}
|
||||
|
||||
void Evaluator::setVariable(const std::string &name, const Token::Literal &value) {
|
||||
std::unique_ptr<Pattern> pattern = nullptr;
|
||||
|
||||
{
|
||||
auto &variables = *this->getScope(0).scope;
|
||||
for (auto &variable : variables) {
|
||||
if (variable->getVariableName() == name) {
|
||||
pattern = variable->clone();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pattern == nullptr) {
|
||||
auto &variables = *this->getGlobalScope().scope;
|
||||
for (auto &variable : variables) {
|
||||
if (variable->getVariableName() == name) {
|
||||
if (!variable->isLocal())
|
||||
LogConsole::abortEvaluation(hex::format("cannot modify global variable '{}' which has been placed in memory", name));
|
||||
|
||||
pattern = variable->clone();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pattern == nullptr)
|
||||
LogConsole::abortEvaluation(hex::format("no variable with name '{}' found", name));
|
||||
|
||||
if (!pattern->isLocal()) return;
|
||||
|
||||
Token::Literal castedLiteral = std::visit(overloaded {
|
||||
[&](double &value) -> Token::Literal {
|
||||
if (dynamic_cast<PatternUnsigned *>(pattern.get()))
|
||||
return u128(value) & bitmask(pattern->getSize() * 8);
|
||||
else if (dynamic_cast<PatternSigned *>(pattern.get()))
|
||||
return i128(value) & bitmask(pattern->getSize() * 8);
|
||||
else if (dynamic_cast<PatternFloat *>(pattern.get()))
|
||||
return pattern->getSize() == sizeof(float) ? double(float(value)) : value;
|
||||
else
|
||||
LogConsole::abortEvaluation(hex::format("cannot cast type 'double' to type '{}'", pattern->getTypeName()));
|
||||
},
|
||||
[&](const std::string &value) -> Token::Literal {
|
||||
if (dynamic_cast<PatternString *>(pattern.get()))
|
||||
return value;
|
||||
else
|
||||
LogConsole::abortEvaluation(hex::format("cannot cast type 'string' to type '{}'", pattern->getTypeName()));
|
||||
},
|
||||
[&](Pattern *value) -> Token::Literal {
|
||||
if (value->getTypeName() == pattern->getTypeName())
|
||||
return value;
|
||||
else
|
||||
LogConsole::abortEvaluation(hex::format("cannot cast type '{}' to type '{}'", value->getTypeName(), pattern->getTypeName()));
|
||||
},
|
||||
[&](auto &&value) -> Token::Literal {
|
||||
if (dynamic_cast<PatternUnsigned *>(pattern.get()) || dynamic_cast<PatternEnum *>(pattern.get()))
|
||||
return u128(value) & bitmask(pattern->getSize() * 8);
|
||||
else if (dynamic_cast<PatternSigned *>(pattern.get()))
|
||||
return i128(value) & bitmask(pattern->getSize() * 8);
|
||||
else if (dynamic_cast<PatternCharacter *>(pattern.get()))
|
||||
return char(value);
|
||||
else if (dynamic_cast<PatternBoolean *>(pattern.get()))
|
||||
return bool(value);
|
||||
else if (dynamic_cast<PatternFloat *>(pattern.get()))
|
||||
return pattern->getSize() == sizeof(float) ? double(float(value)) : value;
|
||||
else
|
||||
LogConsole::abortEvaluation(hex::format("cannot cast integer literal to type '{}'", pattern->getTypeName()));
|
||||
} },
|
||||
value);
|
||||
|
||||
this->getStack()[pattern->getOffset()] = castedLiteral;
|
||||
}
|
||||
|
||||
std::optional<std::vector<std::shared_ptr<Pattern>>> Evaluator::evaluate(const std::vector<std::shared_ptr<ASTNode>> &ast) {
|
||||
this->m_stack.clear();
|
||||
this->m_customFunctions.clear();
|
||||
this->m_scopes.clear();
|
||||
this->m_mainResult.reset();
|
||||
this->m_aborted = false;
|
||||
|
||||
if (this->m_allowDangerousFunctions == DangerousFunctionPermission::Deny)
|
||||
this->m_allowDangerousFunctions = DangerousFunctionPermission::Ask;
|
||||
|
||||
this->m_dangerousFunctionCalled = false;
|
||||
|
||||
ON_SCOPE_EXIT {
|
||||
this->m_envVariables.clear();
|
||||
};
|
||||
|
||||
this->dataOffset() = 0x00;
|
||||
this->m_currPatternCount = 0;
|
||||
|
||||
this->m_customFunctionDefinitions.clear();
|
||||
|
||||
std::vector<std::shared_ptr<Pattern>> patterns;
|
||||
|
||||
try {
|
||||
this->setCurrentControlFlowStatement(ControlFlowStatement::None);
|
||||
pushScope(nullptr, patterns);
|
||||
ON_SCOPE_EXIT {
|
||||
popScope();
|
||||
};
|
||||
|
||||
for (auto &node : ast) {
|
||||
if (dynamic_cast<ASTNodeTypeDecl *>(node.get())) {
|
||||
; // Don't create patterns from type declarations
|
||||
} else if (dynamic_cast<ASTNodeFunctionCall *>(node.get())) {
|
||||
(void)node->evaluate(this);
|
||||
} else if (dynamic_cast<ASTNodeFunctionDefinition *>(node.get())) {
|
||||
this->m_customFunctionDefinitions.push_back(node->evaluate(this));
|
||||
} else if (auto varDeclNode = dynamic_cast<ASTNodeVariableDecl *>(node.get())) {
|
||||
for (auto &pattern : node->createPatterns(this)) {
|
||||
if (varDeclNode->getPlacementOffset() == nullptr) {
|
||||
auto type = varDeclNode->getType()->evaluate(this);
|
||||
|
||||
auto &name = pattern->getVariableName();
|
||||
this->createVariable(name, type.get(), std::nullopt, varDeclNode->isOutVariable());
|
||||
|
||||
if (varDeclNode->isInVariable() && this->m_inVariables.contains(name))
|
||||
this->setVariable(name, this->m_inVariables[name]);
|
||||
} else {
|
||||
patterns.push_back(std::move(pattern));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
auto newPatterns = node->createPatterns(this);
|
||||
std::move(newPatterns.begin(), newPatterns.end(), std::back_inserter(patterns));
|
||||
}
|
||||
}
|
||||
|
||||
if (this->m_customFunctions.contains("main")) {
|
||||
auto mainFunction = this->m_customFunctions["main"];
|
||||
|
||||
if (mainFunction.parameterCount.max > 0)
|
||||
LogConsole::abortEvaluation("main function may not accept any arguments");
|
||||
|
||||
this->m_mainResult = mainFunction.func(this, {});
|
||||
}
|
||||
} catch (PatternLanguageError &error) {
|
||||
if (error.getLineNumber() != 0)
|
||||
this->m_console.setHardError(error);
|
||||
|
||||
patterns.clear();
|
||||
|
||||
this->m_currPatternCount = 0;
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Remove global local variables
|
||||
std::erase_if(patterns, [](const std::shared_ptr<Pattern> &pattern) {
|
||||
return pattern->isLocal();
|
||||
});
|
||||
|
||||
return patterns;
|
||||
}
|
||||
|
||||
void Evaluator::patternCreated() {
|
||||
if (this->m_currPatternCount > this->m_patternLimit)
|
||||
LogConsole::abortEvaluation(hex::format("exceeded maximum number of patterns: {}", this->m_patternLimit));
|
||||
this->m_currPatternCount++;
|
||||
}
|
||||
|
||||
void Evaluator::patternDestroyed() {
|
||||
this->m_currPatternCount--;
|
||||
}
|
||||
|
||||
}
|
@ -1,534 +0,0 @@
|
||||
#include <hex/pattern_language/lexer.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <charconv>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
#define TOKEN(type, value) Token::Type::type, Token::type::value, lineNumber
|
||||
#define VALUE_TOKEN(type, value) Token::Type::type, value, lineNumber
|
||||
|
||||
std::string matchTillInvalid(const char *characters, const std::function<bool(char)> &predicate) {
|
||||
std::string ret;
|
||||
|
||||
while (*characters != 0x00) {
|
||||
ret += *characters;
|
||||
characters++;
|
||||
|
||||
if (!predicate(*characters))
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool isIdentifierCharacter(char c) {
|
||||
return std::isalnum(c) || c == '_';
|
||||
}
|
||||
|
||||
size_t getIntegerLiteralLength(std::string_view string) {
|
||||
auto count = string.find_first_not_of("0123456789ABCDEFabcdef'xXoOpP.uU");
|
||||
if (count == std::string_view::npos)
|
||||
return string.size();
|
||||
else
|
||||
return count;
|
||||
}
|
||||
|
||||
std::optional<Token::Literal> lexIntegerLiteral(std::string_view string) {
|
||||
bool hasFloatSuffix = string.ends_with('D') || string.ends_with('F') || string.ends_with('d') || string.ends_with('f');
|
||||
bool isFloat = std::count(string.begin(), string.end(), '.') == 1 || (!string.starts_with("0x") && hasFloatSuffix);
|
||||
|
||||
if (isFloat) {
|
||||
// Parse double
|
||||
char suffix = 0x00;
|
||||
if (hasFloatSuffix) {
|
||||
suffix = string.back();
|
||||
string = string.substr(0, string.length() - 1);
|
||||
}
|
||||
|
||||
char *end = nullptr;
|
||||
double value = std::strtod(string.begin(), &end);
|
||||
|
||||
if (end == string.end()) {
|
||||
switch (suffix) {
|
||||
case 'd':
|
||||
case 'D':
|
||||
return double(value);
|
||||
case 'f':
|
||||
case 'F':
|
||||
return float(value);
|
||||
default:
|
||||
return value;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
bool isUnsigned = false;
|
||||
if (string.ends_with('U') || string.ends_with('u')) {
|
||||
isUnsigned = true;
|
||||
string = string.substr(0, string.length() - 1);
|
||||
}
|
||||
|
||||
u8 prefixOffset = 0;
|
||||
u8 base = 10;
|
||||
|
||||
if (string.starts_with("0x") || string.starts_with("0X")) {
|
||||
// Parse hexadecimal
|
||||
prefixOffset = 2;
|
||||
base = 16;
|
||||
} else if (string.starts_with("0o") || string.starts_with("0O")) {
|
||||
// Parse octal
|
||||
prefixOffset = 2;
|
||||
base = 8;
|
||||
} else if (string.starts_with("0b") || string.starts_with("0B")) {
|
||||
// Parse binary
|
||||
prefixOffset = 2;
|
||||
base = 2;
|
||||
} else {
|
||||
// Parse decimal
|
||||
prefixOffset = 0;
|
||||
base = 10;
|
||||
}
|
||||
|
||||
u128 value = 0x00;
|
||||
for (char c : string.substr(prefixOffset)) {
|
||||
value *= base;
|
||||
value += [&] {
|
||||
if (c >= '0' && c <= '9') return c - '0';
|
||||
else if (c >= 'A' && c <= 'F') return 0xA + (c - 'A');
|
||||
else if (c >= 'a' && c <= 'f') return 0xA + (c - 'a');
|
||||
else return 0x00;
|
||||
}();
|
||||
}
|
||||
|
||||
if (isUnsigned)
|
||||
return value;
|
||||
else
|
||||
return i128(value);
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<Token::Literal> lexIntegerLiteralWithSeparator(std::string_view string) {
|
||||
|
||||
if (string.starts_with('\'') || string.ends_with('\''))
|
||||
return std::nullopt;
|
||||
else if (string.find('\'') == std::string_view::npos)
|
||||
return lexIntegerLiteral(string);
|
||||
else {
|
||||
auto preprocessedString = std::string(string);
|
||||
preprocessedString.erase(std::remove(preprocessedString.begin(), preprocessedString.end(), '\''), preprocessedString.end());
|
||||
return lexIntegerLiteral(preprocessedString);
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::pair<char, size_t>> getCharacter(const std::string &string) {
|
||||
|
||||
if (string.length() < 1)
|
||||
return std::nullopt;
|
||||
|
||||
// Escape sequences
|
||||
if (string[0] == '\\') {
|
||||
|
||||
if (string.length() < 2)
|
||||
return std::nullopt;
|
||||
|
||||
// Handle simple escape sequences
|
||||
switch (string[1]) {
|
||||
case 'a':
|
||||
return {
|
||||
{'\a', 2}
|
||||
};
|
||||
case 'b':
|
||||
return {
|
||||
{'\b', 2}
|
||||
};
|
||||
case 'f':
|
||||
return {
|
||||
{'\f', 2}
|
||||
};
|
||||
case 'n':
|
||||
return {
|
||||
{'\n', 2}
|
||||
};
|
||||
case 'r':
|
||||
return {
|
||||
{'\r', 2}
|
||||
};
|
||||
case 't':
|
||||
return {
|
||||
{'\t', 2}
|
||||
};
|
||||
case 'v':
|
||||
return {
|
||||
{'\v', 2}
|
||||
};
|
||||
case '\\':
|
||||
return {
|
||||
{'\\', 2}
|
||||
};
|
||||
case '\'':
|
||||
return {
|
||||
{'\'', 2}
|
||||
};
|
||||
case '\"':
|
||||
return {
|
||||
{'\"', 2}
|
||||
};
|
||||
}
|
||||
|
||||
// Hexadecimal number
|
||||
if (string[1] == 'x') {
|
||||
if (string.length() != 4)
|
||||
return std::nullopt;
|
||||
|
||||
if (!isxdigit(string[2]) || !isxdigit(string[3]))
|
||||
return std::nullopt;
|
||||
|
||||
return {
|
||||
{std::strtoul(&string[2], nullptr, 16), 4}
|
||||
};
|
||||
}
|
||||
|
||||
// Octal number
|
||||
if (string[1] == 'o') {
|
||||
if (string.length() != 5)
|
||||
return {};
|
||||
|
||||
if (string[2] < '0' || string[2] > '7' || string[3] < '0' || string[3] > '7' || string[4] < '0' || string[4] > '7')
|
||||
return {};
|
||||
|
||||
return {
|
||||
{std::strtoul(&string[2], nullptr, 8), 5}
|
||||
};
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
} else return {
|
||||
{string[0], 1}
|
||||
};
|
||||
}
|
||||
|
||||
std::optional<std::pair<std::string, size_t>> getStringLiteral(const std::string &string) {
|
||||
if (!string.starts_with('\"'))
|
||||
return {};
|
||||
|
||||
size_t size = 1;
|
||||
|
||||
std::string result;
|
||||
while (string[size] != '\"') {
|
||||
auto character = getCharacter(string.substr(size));
|
||||
|
||||
if (!character.has_value())
|
||||
return {};
|
||||
|
||||
auto &[c, charSize] = character.value();
|
||||
|
||||
result += c;
|
||||
size += charSize;
|
||||
|
||||
if (size >= string.length())
|
||||
return {};
|
||||
}
|
||||
|
||||
return {
|
||||
{result, size + 1}
|
||||
};
|
||||
}
|
||||
|
||||
std::optional<std::pair<char, size_t>> getCharacterLiteral(const std::string &string) {
|
||||
if (string.empty())
|
||||
return {};
|
||||
|
||||
if (string[0] != '\'')
|
||||
return {};
|
||||
|
||||
|
||||
auto character = getCharacter(string.substr(1));
|
||||
|
||||
if (!character.has_value())
|
||||
return {};
|
||||
|
||||
auto &[c, charSize] = character.value();
|
||||
|
||||
if (string.length() >= charSize + 2 && string[charSize + 1] != '\'')
|
||||
return {};
|
||||
|
||||
return {
|
||||
{c, charSize + 2}
|
||||
};
|
||||
}
|
||||
|
||||
std::optional<std::vector<Token>> Lexer::lex(const std::string &code) {
|
||||
std::vector<Token> tokens;
|
||||
u32 offset = 0;
|
||||
|
||||
u32 lineNumber = 1;
|
||||
|
||||
try {
|
||||
|
||||
while (offset < code.length()) {
|
||||
const char &c = code[offset];
|
||||
|
||||
if (c == 0x00)
|
||||
break;
|
||||
|
||||
if (std::isblank(c) || std::isspace(c)) {
|
||||
if (code[offset] == '\n') lineNumber++;
|
||||
offset += 1;
|
||||
} else if (c == ';') {
|
||||
tokens.emplace_back(TOKEN(Separator, EndOfExpression));
|
||||
offset += 1;
|
||||
} else if (c == '(') {
|
||||
tokens.emplace_back(TOKEN(Separator, RoundBracketOpen));
|
||||
offset += 1;
|
||||
} else if (c == ')') {
|
||||
tokens.emplace_back(TOKEN(Separator, RoundBracketClose));
|
||||
offset += 1;
|
||||
} else if (c == '{') {
|
||||
tokens.emplace_back(TOKEN(Separator, CurlyBracketOpen));
|
||||
offset += 1;
|
||||
} else if (c == '}') {
|
||||
tokens.emplace_back(TOKEN(Separator, CurlyBracketClose));
|
||||
offset += 1;
|
||||
} else if (c == '[') {
|
||||
tokens.emplace_back(TOKEN(Separator, SquareBracketOpen));
|
||||
offset += 1;
|
||||
} else if (c == ']') {
|
||||
tokens.emplace_back(TOKEN(Separator, SquareBracketClose));
|
||||
offset += 1;
|
||||
} else if (c == ',') {
|
||||
tokens.emplace_back(TOKEN(Separator, Comma));
|
||||
offset += 1;
|
||||
} else if (c == '.') {
|
||||
tokens.emplace_back(TOKEN(Separator, Dot));
|
||||
offset += 1;
|
||||
} else if (code.substr(offset, 2) == "::") {
|
||||
tokens.emplace_back(TOKEN(Operator, ScopeResolution));
|
||||
offset += 2;
|
||||
} else if (c == '@') {
|
||||
tokens.emplace_back(TOKEN(Operator, AtDeclaration));
|
||||
offset += 1;
|
||||
} else if (code.substr(offset, 2) == "==") {
|
||||
tokens.emplace_back(TOKEN(Operator, BoolEquals));
|
||||
offset += 2;
|
||||
} else if (code.substr(offset, 2) == "!=") {
|
||||
tokens.emplace_back(TOKEN(Operator, BoolNotEquals));
|
||||
offset += 2;
|
||||
} else if (code.substr(offset, 2) == ">=") {
|
||||
tokens.emplace_back(TOKEN(Operator, BoolGreaterThanOrEquals));
|
||||
offset += 2;
|
||||
} else if (code.substr(offset, 2) == "<=") {
|
||||
tokens.emplace_back(TOKEN(Operator, BoolLessThanOrEquals));
|
||||
offset += 2;
|
||||
} else if (code.substr(offset, 2) == "&&") {
|
||||
tokens.emplace_back(TOKEN(Operator, BoolAnd));
|
||||
offset += 2;
|
||||
} else if (code.substr(offset, 2) == "||") {
|
||||
tokens.emplace_back(TOKEN(Operator, BoolOr));
|
||||
offset += 2;
|
||||
} else if (code.substr(offset, 2) == "^^") {
|
||||
tokens.emplace_back(TOKEN(Operator, BoolXor));
|
||||
offset += 2;
|
||||
} else if (c == '=') {
|
||||
tokens.emplace_back(TOKEN(Operator, Assignment));
|
||||
offset += 1;
|
||||
} else if (c == ':') {
|
||||
tokens.emplace_back(TOKEN(Operator, Inherit));
|
||||
offset += 1;
|
||||
} else if (c == '+') {
|
||||
tokens.emplace_back(TOKEN(Operator, Plus));
|
||||
offset += 1;
|
||||
} else if (c == '-') {
|
||||
tokens.emplace_back(TOKEN(Operator, Minus));
|
||||
offset += 1;
|
||||
} else if (c == '*') {
|
||||
tokens.emplace_back(TOKEN(Operator, Star));
|
||||
offset += 1;
|
||||
} else if (c == '/') {
|
||||
tokens.emplace_back(TOKEN(Operator, Slash));
|
||||
offset += 1;
|
||||
} else if (c == '%') {
|
||||
tokens.emplace_back(TOKEN(Operator, Percent));
|
||||
offset += 1;
|
||||
} else if (code.substr(offset, 2) == "<<") {
|
||||
tokens.emplace_back(TOKEN(Operator, ShiftLeft));
|
||||
offset += 2;
|
||||
} else if (code.substr(offset, 2) == ">>") {
|
||||
tokens.emplace_back(TOKEN(Operator, ShiftRight));
|
||||
offset += 2;
|
||||
} else if (c == '>') {
|
||||
tokens.emplace_back(TOKEN(Operator, BoolGreaterThan));
|
||||
offset += 1;
|
||||
} else if (c == '<') {
|
||||
tokens.emplace_back(TOKEN(Operator, BoolLessThan));
|
||||
offset += 1;
|
||||
} else if (c == '!') {
|
||||
tokens.emplace_back(TOKEN(Operator, BoolNot));
|
||||
offset += 1;
|
||||
} else if (c == '|') {
|
||||
tokens.emplace_back(TOKEN(Operator, BitOr));
|
||||
offset += 1;
|
||||
} else if (c == '&') {
|
||||
tokens.emplace_back(TOKEN(Operator, BitAnd));
|
||||
offset += 1;
|
||||
} else if (c == '^') {
|
||||
tokens.emplace_back(TOKEN(Operator, BitXor));
|
||||
offset += 1;
|
||||
} else if (c == '~') {
|
||||
tokens.emplace_back(TOKEN(Operator, BitNot));
|
||||
offset += 1;
|
||||
} else if (c == '?') {
|
||||
tokens.emplace_back(TOKEN(Operator, TernaryConditional));
|
||||
offset += 1;
|
||||
} else if (c == '$') {
|
||||
tokens.emplace_back(TOKEN(Operator, Dollar));
|
||||
offset += 1;
|
||||
} else if (code.substr(offset, 9) == "addressof" && !isIdentifierCharacter(code[offset + 9])) {
|
||||
tokens.emplace_back(TOKEN(Operator, AddressOf));
|
||||
offset += 9;
|
||||
} else if (code.substr(offset, 6) == "sizeof" && !isIdentifierCharacter(code[offset + 6])) {
|
||||
tokens.emplace_back(TOKEN(Operator, SizeOf));
|
||||
offset += 6;
|
||||
} else if (c == '\'') {
|
||||
auto lexedCharacter = getCharacterLiteral(code.substr(offset));
|
||||
|
||||
if (!lexedCharacter.has_value())
|
||||
throwLexerError("invalid character literal", lineNumber);
|
||||
|
||||
auto [character, charSize] = lexedCharacter.value();
|
||||
|
||||
tokens.emplace_back(VALUE_TOKEN(Integer, Token::Literal(character)));
|
||||
offset += charSize;
|
||||
} else if (c == '\"') {
|
||||
auto string = getStringLiteral(code.substr(offset));
|
||||
|
||||
if (!string.has_value())
|
||||
throwLexerError("invalid string literal", lineNumber);
|
||||
|
||||
auto [s, stringSize] = string.value();
|
||||
|
||||
tokens.emplace_back(VALUE_TOKEN(String, Token::Literal(s)));
|
||||
offset += stringSize;
|
||||
} else if (isIdentifierCharacter(c) && !std::isdigit(c)) {
|
||||
std::string identifier = matchTillInvalid(&code[offset], isIdentifierCharacter);
|
||||
|
||||
// Check for reserved keywords
|
||||
|
||||
if (identifier == "struct")
|
||||
tokens.emplace_back(TOKEN(Keyword, Struct));
|
||||
else if (identifier == "union")
|
||||
tokens.emplace_back(TOKEN(Keyword, Union));
|
||||
else if (identifier == "using")
|
||||
tokens.emplace_back(TOKEN(Keyword, Using));
|
||||
else if (identifier == "enum")
|
||||
tokens.emplace_back(TOKEN(Keyword, Enum));
|
||||
else if (identifier == "bitfield")
|
||||
tokens.emplace_back(TOKEN(Keyword, Bitfield));
|
||||
else if (identifier == "be")
|
||||
tokens.emplace_back(TOKEN(Keyword, BigEndian));
|
||||
else if (identifier == "le")
|
||||
tokens.emplace_back(TOKEN(Keyword, LittleEndian));
|
||||
else if (identifier == "if")
|
||||
tokens.emplace_back(TOKEN(Keyword, If));
|
||||
else if (identifier == "else")
|
||||
tokens.emplace_back(TOKEN(Keyword, Else));
|
||||
else if (identifier == "false")
|
||||
tokens.emplace_back(VALUE_TOKEN(Integer, Token::Literal(false)));
|
||||
else if (identifier == "true")
|
||||
tokens.emplace_back(VALUE_TOKEN(Integer, Token::Literal(true)));
|
||||
else if (identifier == "parent")
|
||||
tokens.emplace_back(TOKEN(Keyword, Parent));
|
||||
else if (identifier == "this")
|
||||
tokens.emplace_back(TOKEN(Keyword, This));
|
||||
else if (identifier == "while")
|
||||
tokens.emplace_back(TOKEN(Keyword, While));
|
||||
else if (identifier == "for")
|
||||
tokens.emplace_back(TOKEN(Keyword, For));
|
||||
else if (identifier == "fn")
|
||||
tokens.emplace_back(TOKEN(Keyword, Function));
|
||||
else if (identifier == "return")
|
||||
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));
|
||||
else if (identifier == "break")
|
||||
tokens.emplace_back(TOKEN(Keyword, Break));
|
||||
else if (identifier == "continue")
|
||||
tokens.emplace_back(TOKEN(Keyword, Continue));
|
||||
|
||||
// Check for built-in types
|
||||
else if (identifier == "u8")
|
||||
tokens.emplace_back(TOKEN(ValueType, Unsigned8Bit));
|
||||
else if (identifier == "s8")
|
||||
tokens.emplace_back(TOKEN(ValueType, Signed8Bit));
|
||||
else if (identifier == "u16")
|
||||
tokens.emplace_back(TOKEN(ValueType, Unsigned16Bit));
|
||||
else if (identifier == "s16")
|
||||
tokens.emplace_back(TOKEN(ValueType, Signed16Bit));
|
||||
else if (identifier == "u32")
|
||||
tokens.emplace_back(TOKEN(ValueType, Unsigned32Bit));
|
||||
else if (identifier == "s32")
|
||||
tokens.emplace_back(TOKEN(ValueType, Signed32Bit));
|
||||
else if (identifier == "u64")
|
||||
tokens.emplace_back(TOKEN(ValueType, Unsigned64Bit));
|
||||
else if (identifier == "s64")
|
||||
tokens.emplace_back(TOKEN(ValueType, Signed64Bit));
|
||||
else if (identifier == "u128")
|
||||
tokens.emplace_back(TOKEN(ValueType, Unsigned128Bit));
|
||||
else if (identifier == "s128")
|
||||
tokens.emplace_back(TOKEN(ValueType, Signed128Bit));
|
||||
else if (identifier == "float")
|
||||
tokens.emplace_back(TOKEN(ValueType, Float));
|
||||
else if (identifier == "double")
|
||||
tokens.emplace_back(TOKEN(ValueType, Double));
|
||||
else if (identifier == "char")
|
||||
tokens.emplace_back(TOKEN(ValueType, Character));
|
||||
else if (identifier == "char16")
|
||||
tokens.emplace_back(TOKEN(ValueType, Character16));
|
||||
else if (identifier == "bool")
|
||||
tokens.emplace_back(TOKEN(ValueType, Boolean));
|
||||
else if (identifier == "str")
|
||||
tokens.emplace_back(TOKEN(ValueType, String));
|
||||
else if (identifier == "padding")
|
||||
tokens.emplace_back(TOKEN(ValueType, Padding));
|
||||
else if (identifier == "auto")
|
||||
tokens.emplace_back(TOKEN(ValueType, Auto));
|
||||
|
||||
// If it's not a keyword and a builtin type, it has to be an identifier
|
||||
|
||||
else
|
||||
tokens.emplace_back(VALUE_TOKEN(Identifier, Token::Identifier(identifier)));
|
||||
|
||||
offset += identifier.length();
|
||||
} else if (std::isdigit(c)) {
|
||||
auto integerLength = getIntegerLiteralLength(&code[offset]);
|
||||
auto integer = lexIntegerLiteralWithSeparator(std::string_view(&code[offset], integerLength));
|
||||
|
||||
if (!integer.has_value())
|
||||
throwLexerError("invalid integer literal", lineNumber);
|
||||
|
||||
|
||||
tokens.emplace_back(VALUE_TOKEN(Integer, Token::Literal(integer.value())));
|
||||
offset += integerLength;
|
||||
} else
|
||||
throwLexerError("unknown token", lineNumber);
|
||||
}
|
||||
|
||||
tokens.emplace_back(TOKEN(Separator, EndOfProgram));
|
||||
} catch (PatternLanguageError &e) {
|
||||
this->m_error = e;
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
|
||||
return tokens;
|
||||
}
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
#include <hex/pattern_language/log_console.hpp>
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
[[noreturn]] void LogConsole::abortEvaluation(const std::string &message, const ASTNode *node) {
|
||||
if (node == nullptr)
|
||||
throw PatternLanguageError(0, "Evaluator: " + message);
|
||||
else
|
||||
throw PatternLanguageError(node->getLineNumber(), "Evaluator: " + message);
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,255 +0,0 @@
|
||||
#include <hex/pattern_language/pattern_language.hpp>
|
||||
|
||||
#include <hex/helpers/file.hpp>
|
||||
#include <hex/providers/provider.hpp>
|
||||
#include <hex/helpers/logger.hpp>
|
||||
|
||||
#include <hex/pattern_language/preprocessor.hpp>
|
||||
#include <hex/pattern_language/lexer.hpp>
|
||||
#include <hex/pattern_language/parser.hpp>
|
||||
#include <hex/pattern_language/validator.hpp>
|
||||
#include <hex/pattern_language/evaluator.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class Pattern;
|
||||
|
||||
PatternLanguage::PatternLanguage() {
|
||||
this->m_preprocessor = new Preprocessor();
|
||||
this->m_lexer = new Lexer();
|
||||
this->m_parser = new Parser();
|
||||
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);
|
||||
return true;
|
||||
} else if (value == "little") {
|
||||
this->m_evaluator->setDefaultEndian(std::endian::little);
|
||||
return true;
|
||||
} else if (value == "native") {
|
||||
this->m_evaluator->setDefaultEndian(std::endian::native);
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
});
|
||||
|
||||
this->m_preprocessor->addPragmaHandler("eval_depth", [this](std::string value) {
|
||||
auto limit = strtol(value.c_str(), nullptr, 0);
|
||||
|
||||
if (limit <= 0)
|
||||
return false;
|
||||
|
||||
this->m_evaluator->setEvaluationDepth(limit);
|
||||
return true;
|
||||
});
|
||||
|
||||
this->m_preprocessor->addPragmaHandler("array_limit", [this](const std::string &value) {
|
||||
auto limit = strtol(value.c_str(), nullptr, 0);
|
||||
|
||||
if (limit <= 0)
|
||||
return false;
|
||||
|
||||
this->m_evaluator->setArrayLimit(limit);
|
||||
return true;
|
||||
});
|
||||
|
||||
this->m_preprocessor->addPragmaHandler("pattern_limit", [this](const std::string &value) {
|
||||
auto limit = strtol(value.c_str(), nullptr, 0);
|
||||
|
||||
if (limit <= 0)
|
||||
return false;
|
||||
|
||||
this->m_evaluator->setPatternLimit(limit);
|
||||
return true;
|
||||
});
|
||||
|
||||
this->m_preprocessor->addPragmaHandler("loop_limit", [this](const std::string &value) {
|
||||
auto limit = strtol(value.c_str(), nullptr, 0);
|
||||
|
||||
if (limit <= 0)
|
||||
return false;
|
||||
|
||||
this->m_evaluator->setLoopLimit(limit);
|
||||
return true;
|
||||
});
|
||||
|
||||
this->m_preprocessor->addPragmaHandler("base_address", [](const std::string &value) {
|
||||
auto baseAddress = strtoull(value.c_str(), nullptr, 0);
|
||||
|
||||
ImHexApi::Provider::get()->setBaseAddress(baseAddress);
|
||||
return true;
|
||||
});
|
||||
|
||||
this->m_preprocessor->addPragmaHandler("bitfield_order", [this](const std::string &value) {
|
||||
if (value == "left_to_right") {
|
||||
this->m_evaluator->setBitfieldOrder(BitfieldOrder::LeftToRight);
|
||||
return true;
|
||||
} else if (value == "right_to_left") {
|
||||
this->m_evaluator->setBitfieldOrder(BitfieldOrder::RightToLeft);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
PatternLanguage::~PatternLanguage() {
|
||||
delete this->m_preprocessor;
|
||||
delete this->m_lexer;
|
||||
delete this->m_parser;
|
||||
delete this->m_validator;
|
||||
}
|
||||
|
||||
std::optional<std::vector<std::shared_ptr<ASTNode>>> 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 std::nullopt;
|
||||
}
|
||||
|
||||
auto tokens = this->m_lexer->lex(preprocessedCode.value());
|
||||
if (!tokens.has_value()) {
|
||||
this->m_currError = this->m_lexer->getError();
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto ast = this->m_parser->parse(tokens.value());
|
||||
if (!ast.has_value()) {
|
||||
this->m_currError = this->m_parser->getError();
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (!this->m_validator->validate(*ast)) {
|
||||
this->m_currError = this->m_validator->getError();
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return ast;
|
||||
}
|
||||
|
||||
bool PatternLanguage::executeString(prv::Provider *provider, const std::string &code, const std::map<std::string, Token::Literal> &envVars, const std::map<std::string, Token::Literal> &inVariables, bool checkResult) {
|
||||
this->m_running = true;
|
||||
ON_SCOPE_EXIT { this->m_running = false; };
|
||||
|
||||
ON_SCOPE_EXIT {
|
||||
if (this->m_currError.has_value()) {
|
||||
const auto &error = this->m_currError.value();
|
||||
|
||||
if (error.getLineNumber() > 0)
|
||||
this->m_evaluator->getConsole().log(LogConsole::Level::Error, hex::format("{}: {}", error.getLineNumber(), error.what()));
|
||||
else
|
||||
this->m_evaluator->getConsole().log(LogConsole::Level::Error, error.what());
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
this->m_currAST.clear();
|
||||
|
||||
{
|
||||
auto ast = this->parseString(code);
|
||||
if (!ast)
|
||||
return false;
|
||||
|
||||
this->m_currAST = std::move(ast.value());
|
||||
}
|
||||
|
||||
|
||||
auto patterns = this->m_evaluator->evaluate(this->m_currAST);
|
||||
if (!patterns.has_value()) {
|
||||
this->m_currError = this->m_evaluator->getConsole().getLastHardError();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (auto mainResult = this->m_evaluator->getMainResult(); checkResult && mainResult.has_value()) {
|
||||
auto returnCode = Token::literalToSigned(*mainResult);
|
||||
|
||||
if (returnCode != 0) {
|
||||
this->m_currError = PatternLanguageError(0, hex::format("non-success value returned from main: {}", returnCode));
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
this->m_patterns = std::move(patterns.value());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PatternLanguage::executeFile(prv::Provider *provider, const std::fs::path &path, const std::map<std::string, Token::Literal> &envVars, const std::map<std::string, Token::Literal> &inVariables) {
|
||||
fs::File file(path, fs::File::Mode::Read);
|
||||
|
||||
return this->executeString(provider, file.readString(), envVars, inVariables, true);
|
||||
}
|
||||
|
||||
std::pair<bool, std::optional<Token::Literal>> PatternLanguage::executeFunction(prv::Provider *provider, const std::string &code) {
|
||||
|
||||
auto functionContent = hex::format("fn main() {{ {0} }};", code);
|
||||
|
||||
auto success = this->executeString(provider, functionContent, {}, {}, false);
|
||||
auto result = this->m_evaluator->getMainResult();
|
||||
|
||||
return { success, std::move(result) };
|
||||
}
|
||||
|
||||
void PatternLanguage::abort() {
|
||||
this->m_evaluator->abort();
|
||||
}
|
||||
|
||||
const std::vector<std::shared_ptr<ASTNode>> &PatternLanguage::getCurrentAST() const {
|
||||
return this->m_currAST;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::map<std::string, Token::Literal> PatternLanguage::getOutVariables() const {
|
||||
return this->m_evaluator->getOutVariables();
|
||||
}
|
||||
|
||||
|
||||
const std::vector<std::pair<LogConsole::Level, std::string>> &PatternLanguage::getConsoleLog() {
|
||||
return this->m_evaluator->getConsole().getLog();
|
||||
}
|
||||
|
||||
const std::optional<PatternLanguageError> &PatternLanguage::getError() {
|
||||
return this->m_currError;
|
||||
}
|
||||
|
||||
u32 PatternLanguage::getCreatedPatternCount() {
|
||||
return this->m_evaluator->getPatternCount();
|
||||
}
|
||||
|
||||
u32 PatternLanguage::getMaximumPatternCount() {
|
||||
return this->m_evaluator->getPatternLimit();
|
||||
}
|
||||
|
||||
|
||||
void PatternLanguage::allowDangerousFunctions(bool allow) {
|
||||
this->m_evaluator->allowDangerousFunctions(allow);
|
||||
}
|
||||
|
||||
bool PatternLanguage::hasDangerousFunctionBeenCalled() const {
|
||||
return this->m_evaluator->hasDangerousFunctionBeenCalled();
|
||||
}
|
||||
|
||||
void PatternLanguage::reset() {
|
||||
this->m_patterns.clear();
|
||||
|
||||
this->m_currAST.clear();
|
||||
}
|
||||
|
||||
}
|
@ -1,276 +0,0 @@
|
||||
#include <hex/pattern_language/preprocessor.hpp>
|
||||
|
||||
#include <hex/helpers/fmt.hpp>
|
||||
#include <hex/helpers/fs.hpp>
|
||||
#include <hex/helpers/file.hpp>
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
std::optional<std::string> Preprocessor::preprocess(std::string code, bool initialRun) {
|
||||
u32 offset = 0;
|
||||
u32 lineNumber = 1;
|
||||
bool isInString = false;
|
||||
|
||||
if (initialRun) {
|
||||
this->m_defines.clear();
|
||||
this->m_pragmas.clear();
|
||||
}
|
||||
|
||||
std::string output;
|
||||
output.reserve(code.length());
|
||||
|
||||
try {
|
||||
while (offset < code.length()) {
|
||||
if (code.substr(offset, 2) == "//") {
|
||||
while (code[offset] != '\n' && offset < code.length())
|
||||
offset += 1;
|
||||
} else if (code.substr(offset, 2) == "/*") {
|
||||
while (code.substr(offset, 2) != "*/" && offset < code.length()) {
|
||||
if (code[offset] == '\n') {
|
||||
output += '\n';
|
||||
lineNumber++;
|
||||
}
|
||||
|
||||
offset += 1;
|
||||
}
|
||||
|
||||
offset += 2;
|
||||
if (offset >= code.length())
|
||||
throwPreprocessorError("unterminated comment", lineNumber - 1);
|
||||
} else {
|
||||
output += code[offset];
|
||||
offset++;
|
||||
}
|
||||
}
|
||||
|
||||
offset = 0;
|
||||
code = output;
|
||||
output.clear();
|
||||
output.reserve(code.size());
|
||||
|
||||
bool startOfLine = true;
|
||||
while (offset < code.length()) {
|
||||
if (offset > 0 && code[offset - 1] != '\\' && code[offset] == '\"')
|
||||
isInString = !isInString;
|
||||
else if (isInString) {
|
||||
output += code[offset];
|
||||
offset += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (code[offset] == '#' && startOfLine) {
|
||||
offset += 1;
|
||||
|
||||
if (code.substr(offset, 7) == "include") {
|
||||
offset += 7;
|
||||
|
||||
while (std::isblank(code[offset]) || std::isspace(code[offset]))
|
||||
offset += 1;
|
||||
|
||||
if (code[offset] != '<' && code[offset] != '"')
|
||||
throwPreprocessorError("expected '<' or '\"' before file name", lineNumber);
|
||||
|
||||
char endChar = code[offset];
|
||||
if (endChar == '<') endChar = '>';
|
||||
|
||||
offset += 1;
|
||||
|
||||
std::string includeFile;
|
||||
while (code[offset] != endChar && code[offset] != '\n') {
|
||||
includeFile += code[offset];
|
||||
offset += 1;
|
||||
|
||||
if (offset >= code.length())
|
||||
throwPreprocessorError(hex::format("missing terminating '{0}' character", endChar), lineNumber);
|
||||
}
|
||||
offset += 1;
|
||||
|
||||
std::fs::path includePath = includeFile;
|
||||
|
||||
if (includeFile[0] != '/') {
|
||||
for (const auto &dir : fs::getDefaultPaths(fs::ImHexPath::PatternsInclude)) {
|
||||
std::fs::path tempPath = dir / includePath;
|
||||
if (fs::isRegularFile(tempPath)) {
|
||||
includePath = tempPath;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!fs::isRegularFile(includePath)) {
|
||||
if (includePath.parent_path().filename().string() == "std")
|
||||
throwPreprocessorError(hex::format("{0}: No such file.\n\nThis file might be part of the standard library.\nYou can install the standard library though\nthe Content Store found under Help -> Content Store.", includeFile.c_str()), lineNumber);
|
||||
else
|
||||
throwPreprocessorError(hex::format("{0}: No such file", includeFile.c_str()), lineNumber);
|
||||
}
|
||||
|
||||
fs::File file(includePath, fs::File::Mode::Read);
|
||||
if (!file.isValid()) {
|
||||
throwPreprocessorError(hex::format("{0}: Failed to open file", includeFile.c_str()), lineNumber);
|
||||
}
|
||||
|
||||
Preprocessor preprocessor;
|
||||
preprocessor.addDefaultPragmaHandlers();
|
||||
preprocessor.m_defines = this->m_defines;
|
||||
preprocessor.m_onceIncludedFiles = this->m_onceIncludedFiles;
|
||||
|
||||
auto preprocessedInclude = preprocessor.preprocess(file.readString(), /*initialRun =*/false);
|
||||
|
||||
if (!preprocessedInclude.has_value()) {
|
||||
auto error = *preprocessor.m_error;
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (preprocessor.shouldOnlyIncludeOnce()) {
|
||||
auto [iter, added] = this->m_onceIncludedFiles.insert(includePath);
|
||||
if (added) {
|
||||
auto content = preprocessedInclude.value();
|
||||
|
||||
std::replace(content.begin(), content.end(), '\n', ' ');
|
||||
std::replace(content.begin(), content.end(), '\r', ' ');
|
||||
|
||||
output += content;
|
||||
}
|
||||
}
|
||||
|
||||
this->m_defines = preprocessor.m_defines;
|
||||
this->m_onceIncludedFiles = preprocessor.m_onceIncludedFiles;
|
||||
} else if (code.substr(offset, 6) == "define") {
|
||||
offset += 6;
|
||||
|
||||
while (std::isblank(code[offset])) {
|
||||
offset += 1;
|
||||
}
|
||||
|
||||
std::string defineName;
|
||||
while (!std::isblank(code[offset])) {
|
||||
defineName += code[offset];
|
||||
|
||||
if (offset >= code.length() || code[offset] == '\n' || code[offset] == '\r')
|
||||
throwPreprocessorError("no value given in #define directive", lineNumber);
|
||||
offset += 1;
|
||||
}
|
||||
|
||||
while (std::isblank(code[offset])) {
|
||||
offset += 1;
|
||||
if (offset >= code.length())
|
||||
throwPreprocessorError("no value given in #define directive", lineNumber);
|
||||
}
|
||||
|
||||
std::string replaceValue;
|
||||
while (code[offset] != '\n' && code[offset] != '\r') {
|
||||
if (offset >= code.length())
|
||||
throwPreprocessorError("missing new line after #define directive", lineNumber);
|
||||
|
||||
replaceValue += code[offset];
|
||||
offset += 1;
|
||||
}
|
||||
|
||||
if (replaceValue.empty())
|
||||
throwPreprocessorError("no value given in #define directive", lineNumber);
|
||||
|
||||
this->m_defines.emplace(defineName, replaceValue, lineNumber);
|
||||
} else if (code.substr(offset, 6) == "pragma") {
|
||||
offset += 6;
|
||||
|
||||
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]) && code[offset] != '\n' && code[offset] != '\r') {
|
||||
pragmaKey += code[offset];
|
||||
|
||||
if (offset >= code.length())
|
||||
throwPreprocessorError("no instruction given in #pragma directive", lineNumber);
|
||||
|
||||
offset += 1;
|
||||
}
|
||||
|
||||
while (std::isblank(code[offset]))
|
||||
offset += 1;
|
||||
|
||||
std::string pragmaValue;
|
||||
while (code[offset] != '\n' && code[offset] != '\r') {
|
||||
if (offset >= code.length())
|
||||
throwPreprocessorError("missing new line after #pragma directive", lineNumber);
|
||||
|
||||
pragmaValue += code[offset];
|
||||
offset += 1;
|
||||
}
|
||||
|
||||
this->m_pragmas.emplace(pragmaKey, pragmaValue, lineNumber);
|
||||
} else
|
||||
throwPreprocessorError("unknown preprocessor directive", lineNumber);
|
||||
}
|
||||
|
||||
if (code[offset] == '\n') {
|
||||
lineNumber++;
|
||||
startOfLine = true;
|
||||
} else if (!std::isspace(code[offset]))
|
||||
startOfLine = false;
|
||||
|
||||
output += code[offset];
|
||||
offset += 1;
|
||||
}
|
||||
|
||||
// Apply defines
|
||||
std::vector<std::tuple<std::string, std::string, u32>> sortedDefines;
|
||||
std::copy(this->m_defines.begin(), this->m_defines.end(), std::back_inserter(sortedDefines));
|
||||
std::sort(sortedDefines.begin(), sortedDefines.end(), [](const auto &left, const auto &right) {
|
||||
return std::get<0>(left).size() > std::get<0>(right).size();
|
||||
});
|
||||
|
||||
for (const auto &[define, value, defineLine] : sortedDefines) {
|
||||
size_t index = 0;
|
||||
while ((index = output.find(define, index)) != std::string::npos) {
|
||||
output.replace(index, define.length(), value);
|
||||
index += value.length();
|
||||
}
|
||||
}
|
||||
|
||||
// Handle pragmas
|
||||
for (const auto &[type, value, pragmaLine] : this->m_pragmas) {
|
||||
if (this->m_pragmaHandlers.contains(type)) {
|
||||
if (!this->m_pragmaHandlers[type](value))
|
||||
throwPreprocessorError(hex::format("invalid value provided to '{0}' #pragma directive", type.c_str()), pragmaLine);
|
||||
} else
|
||||
throwPreprocessorError(hex::format("no #pragma handler registered for type {0}", type.c_str()), pragmaLine);
|
||||
}
|
||||
} catch (PatternLanguageError &e) {
|
||||
this->m_error = e;
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
void Preprocessor::addPragmaHandler(const std::string &pragmaType, const std::function<bool(const std::string &)> &function) {
|
||||
this->m_pragmaHandlers[pragmaType] = function;
|
||||
}
|
||||
|
||||
void Preprocessor::removePragmaHandler(const std::string &pragmaType) {
|
||||
this->m_pragmaHandlers.erase(pragmaType);
|
||||
}
|
||||
|
||||
void Preprocessor::addDefaultPragmaHandlers() {
|
||||
this->addPragmaHandler("MIME", [](const std::string &value) {
|
||||
return !std::all_of(value.begin(), value.end(), isspace) && !value.ends_with('\n') && !value.ends_with('\r');
|
||||
});
|
||||
this->addPragmaHandler("endian", [](const std::string &value) {
|
||||
return value == "big" || value == "little" || value == "native";
|
||||
});
|
||||
this->addPragmaHandler("once", [this](const std::string &value) {
|
||||
this->m_onlyIncludeOnce = true;
|
||||
|
||||
return value.empty();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
#include <hex/pattern_language/validator.hpp>
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_variable_decl.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_type_decl.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_struct.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_union.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_enum.hpp>
|
||||
|
||||
#include <hex/helpers/fmt.hpp>
|
||||
|
||||
#include <unordered_set>
|
||||
#include <string>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
bool Validator::validate(const std::vector<std::shared_ptr<ASTNode>> &ast) {
|
||||
std::unordered_set<std::string> identifiers;
|
||||
std::unordered_set<std::string> types;
|
||||
|
||||
try {
|
||||
|
||||
for (const auto &node : ast) {
|
||||
if (node == nullptr)
|
||||
throwValidatorError("nullptr in AST. This is a bug!", 1);
|
||||
|
||||
if (auto variableDeclNode = dynamic_cast<ASTNodeVariableDecl *>(node.get()); variableDeclNode != nullptr) {
|
||||
if (!identifiers.insert(variableDeclNode->getName().data()).second)
|
||||
throwValidatorError(hex::format("redefinition of identifier '{0}'", variableDeclNode->getName().data()), variableDeclNode->getLineNumber());
|
||||
|
||||
this->validate(hex::moveToVector<std::shared_ptr<ASTNode>>(variableDeclNode->getType()->clone()));
|
||||
} else if (auto typeDeclNode = dynamic_cast<ASTNodeTypeDecl *>(node.get()); typeDeclNode != nullptr) {
|
||||
if (!types.insert(typeDeclNode->getName().data()).second)
|
||||
throwValidatorError(hex::format("redefinition of type '{0}'", typeDeclNode->getName().data()), typeDeclNode->getLineNumber());
|
||||
|
||||
if (!typeDeclNode->isForwardDeclared())
|
||||
this->validate(hex::moveToVector<std::shared_ptr<ASTNode>>(typeDeclNode->getType()->clone()));
|
||||
} else if (auto structNode = dynamic_cast<ASTNodeStruct *>(node.get()); structNode != nullptr) {
|
||||
this->validate(structNode->getMembers());
|
||||
} else if (auto unionNode = dynamic_cast<ASTNodeUnion *>(node.get()); unionNode != nullptr) {
|
||||
this->validate(unionNode->getMembers());
|
||||
} else if (auto enumNode = dynamic_cast<ASTNodeEnum *>(node.get()); enumNode != nullptr) {
|
||||
std::unordered_set<std::string> enumIdentifiers;
|
||||
for (auto &[name, value] : enumNode->getEntries()) {
|
||||
if (!enumIdentifiers.insert(name).second)
|
||||
throwValidatorError(hex::format("redefinition of enum constant '{0}'", name.c_str()), value->getLineNumber());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (PatternLanguageError &e) {
|
||||
this->m_error = e;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@ -1,10 +1,8 @@
|
||||
#include <hex/providers/provider.hpp>
|
||||
|
||||
#include <hex.hpp>
|
||||
#include <hex/api/content_registry.hpp>
|
||||
#include <hex/api/event.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern.hpp>
|
||||
#include <hex/pattern_language/pattern_language.hpp>
|
||||
|
||||
#include <hex/ui/view.hpp>
|
||||
|
||||
#include <cmath>
|
||||
@ -13,11 +11,13 @@
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include <pl/pattern_language.hpp>
|
||||
|
||||
namespace hex::prv {
|
||||
|
||||
Provider::Provider() {
|
||||
this->m_patches.emplace_back();
|
||||
this->m_patternLanguageRuntime = std::make_unique<pl::PatternLanguage>();
|
||||
this->m_patternLanguageRuntime = ContentRegistry::PatternLanguage::createDefaultRuntime(this);
|
||||
|
||||
if (this->hasLoadInterface())
|
||||
EventManager::post<RequestOpenPopup>(View::toWindowName("hex.builtin.view.provider_settings.load_popup"));
|
||||
@ -44,7 +44,7 @@ namespace hex::prv {
|
||||
}
|
||||
|
||||
void Provider::resize(size_t newSize) {
|
||||
hex::unused(newSize);
|
||||
this->m_patternLanguageRuntime->setDataSize(newSize);
|
||||
}
|
||||
|
||||
void Provider::insert(u64 offset, size_t size) {
|
||||
@ -129,6 +129,7 @@ namespace hex::prv {
|
||||
|
||||
void Provider::setBaseAddress(u64 address) {
|
||||
this->m_baseAddress = address;
|
||||
this->m_patternLanguageRuntime->setDataBaseAddress(address);
|
||||
}
|
||||
|
||||
u64 Provider::getBaseAddress() const {
|
||||
|
@ -4,7 +4,6 @@
|
||||
#include <imgui_freetype.h>
|
||||
|
||||
#include <hex/api/content_registry.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern.hpp>
|
||||
#include <hex/ui/view.hpp>
|
||||
#include <hex/helpers/net.hpp>
|
||||
#include <hex/helpers/fs.hpp>
|
||||
@ -185,7 +184,6 @@ namespace hex::init {
|
||||
|
||||
ContentRegistry::CommandPaletteCommands::getEntries().clear();
|
||||
ContentRegistry::PatternLanguage::getFunctions().clear();
|
||||
ContentRegistry::PatternLanguage::getPalettes().clear();
|
||||
|
||||
for (auto &[name, view] : ContentRegistry::Views::getEntries())
|
||||
delete view;
|
||||
|
@ -9,6 +9,7 @@ add_library(${PROJECT_NAME} SHARED
|
||||
source/content/command_palette_commands.cpp
|
||||
source/content/data_inspector.cpp
|
||||
source/content/pl_builtin_functions.cpp
|
||||
source/content/pl_pragmas.cpp
|
||||
source/content/settings_entries.cpp
|
||||
source/content/tools_entries.cpp
|
||||
source/content/data_processor_nodes.cpp
|
||||
|
@ -22,7 +22,7 @@ namespace hex::plugin::builtin {
|
||||
|
||||
private:
|
||||
std::map<prv::Provider *, std::vector<std::shared_ptr<pl::Pattern>>> m_sortedPatterns;
|
||||
hex::PatternDrawer m_drawer;
|
||||
hex::PatternDrawer m_patternDrawer;
|
||||
};
|
||||
|
||||
}
|
@ -1,8 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/ui/view.hpp>
|
||||
#include <hex/pattern_language/pattern_language.hpp>
|
||||
#include <hex/pattern_language/log_console.hpp>
|
||||
#include <pl/pattern_language.hpp>
|
||||
#include <hex/providers/provider.hpp>
|
||||
|
||||
#include <cstring>
|
||||
@ -79,6 +78,15 @@ namespace hex::plugin::builtin {
|
||||
u64 m_envVarIdCounter;
|
||||
std::list<EnvVar> m_envVarEntries;
|
||||
|
||||
enum class DangerousFunctionPerms : u8 {
|
||||
Ask,
|
||||
Allow,
|
||||
Deny
|
||||
};
|
||||
|
||||
std::atomic<bool> m_dangerousFunctionCalled = false;
|
||||
std::atomic<DangerousFunctionPerms> m_dangerousFunctionsAllowed = DangerousFunctionPerms::Ask;
|
||||
|
||||
void drawConsole(ImVec2 size);
|
||||
void drawEnvVars(ImVec2 size);
|
||||
void drawVariableSettings(ImVec2 size);
|
||||
|
@ -1,22 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern.hpp>
|
||||
#include <hex/pattern_language/pattern_visitor.hpp>
|
||||
#include <pl/patterns/pattern.hpp>
|
||||
#include <pl/pattern_visitor.hpp>
|
||||
#include <hex/providers/provider.hpp>
|
||||
|
||||
namespace hex {
|
||||
|
||||
template<typename T>
|
||||
concept ArrayPattern = requires(T pattern, std::function<void(int, pl::Pattern&)> fn) {
|
||||
{ pattern.forEachArrayEntry(fn) } -> std::same_as<void>;
|
||||
{ pattern.forEachArrayEntry(fn) } -> std::same_as<void>;
|
||||
};
|
||||
|
||||
class PatternDrawer : public pl::PatternVisitor
|
||||
{
|
||||
class PatternDrawer : public pl::PatternVisitor {
|
||||
public:
|
||||
PatternDrawer();
|
||||
|
||||
void setProvider(prv::Provider *provider);
|
||||
PatternDrawer() = default;
|
||||
|
||||
void visit(pl::PatternArrayDynamic& pattern) override;
|
||||
void visit(pl::PatternArrayStatic& pattern) override;
|
||||
@ -73,7 +70,6 @@ namespace hex {
|
||||
u64& getDisplayEnd(const pl::Pattern& pattern);
|
||||
|
||||
private:
|
||||
prv::Provider *m_provider;
|
||||
std::map<const pl::Pattern*, u64> m_displayEnd;
|
||||
};
|
||||
};
|
@ -3,11 +3,12 @@
|
||||
#include <hex/helpers/fmt.hpp>
|
||||
#include <hex/helpers/net.hpp>
|
||||
#include <hex/helpers/file.hpp>
|
||||
#include <hex/helpers/utils.hpp>
|
||||
|
||||
#include <hex/pattern_language/token.hpp>
|
||||
#include <hex/pattern_language/log_console.hpp>
|
||||
#include <hex/pattern_language/evaluator.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern.hpp>
|
||||
#include <pl/token.hpp>
|
||||
#include <pl/log_console.hpp>
|
||||
#include <pl/evaluator.hpp>
|
||||
#include <pl/patterns/pattern.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
@ -15,7 +16,7 @@
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
std::string format(pl::Evaluator *ctx, const auto ¶ms) {
|
||||
std::string format(const auto ¶ms) {
|
||||
auto format = pl::Token::literalToString(params[0], true);
|
||||
std::string message;
|
||||
|
||||
@ -26,7 +27,7 @@ namespace hex::plugin::builtin {
|
||||
|
||||
std::visit(overloaded {
|
||||
[&](pl::Pattern *value) {
|
||||
formatArgs.push_back(value->toString(ctx->getProvider()));
|
||||
formatArgs.push_back(value->toString());
|
||||
},
|
||||
[&](auto &&value) {
|
||||
formatArgs.push_back(value);
|
||||
@ -37,32 +38,30 @@ namespace hex::plugin::builtin {
|
||||
try {
|
||||
return fmt::vformat(format, formatArgs);
|
||||
} catch (fmt::format_error &error) {
|
||||
hex::pl::LogConsole::abortEvaluation(hex::format("format error: {}", error.what()));
|
||||
pl::LogConsole::abortEvaluation(hex::format("format error: {}", error.what()));
|
||||
}
|
||||
}
|
||||
|
||||
void registerPatternLanguageFunctions() {
|
||||
using namespace hex::pl;
|
||||
using ParameterCount = ContentRegistry::PatternLanguage::ParameterCount;
|
||||
using namespace pl;
|
||||
using FunctionParameterCount = pl::api::FunctionParameterCount;
|
||||
|
||||
ContentRegistry::PatternLanguage::addColorPalette("hex.builtin.palette.pastel", { 0x70B4771F, 0x700E7FFF, 0x702CA02C, 0x702827D6, 0x70BD6794, 0x704B568C, 0x70C277E3, 0x707F7F7F, 0x7022BDBC, 0x70CFBE17 });
|
||||
|
||||
ContentRegistry::PatternLanguage::Namespace nsStd = { "builtin", "std" };
|
||||
pl::api::Namespace nsStd = { "builtin", "std" };
|
||||
{
|
||||
/* print(format, args...) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStd, "print", ParameterCount::moreThan(0), [](Evaluator *ctx, auto params) -> std::optional<Token::Literal> {
|
||||
ctx->getConsole().log(LogConsole::Level::Info, format(ctx, params));
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStd, "print", FunctionParameterCount::moreThan(0), [](Evaluator *ctx, auto params) -> std::optional<Token::Literal> {
|
||||
ctx->getConsole().log(LogConsole::Level::Info, format(params));
|
||||
|
||||
return std::nullopt;
|
||||
});
|
||||
|
||||
/* format(format, args...) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStd, "format", ParameterCount::moreThan(0), [](Evaluator *ctx, auto params) -> std::optional<Token::Literal> {
|
||||
return format(ctx, params);
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStd, "format", FunctionParameterCount::moreThan(0), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
return format(params);
|
||||
});
|
||||
|
||||
/* env(name) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStd, "env", ParameterCount::exactly(1), [](Evaluator *ctx, auto params) -> std::optional<Token::Literal> {
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStd, "env", FunctionParameterCount::exactly(1), [](Evaluator *ctx, auto params) -> std::optional<Token::Literal> {
|
||||
auto name = Token::literalToString(params[0], false);
|
||||
|
||||
auto env = ctx->getEnvVariable(name);
|
||||
@ -75,44 +74,44 @@ namespace hex::plugin::builtin {
|
||||
});
|
||||
|
||||
/* pack_size(...) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStd, "sizeof_pack", ParameterCount::atLeast(0), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStd, "sizeof_pack", FunctionParameterCount::atLeast(0), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
return u128(params.size());
|
||||
});
|
||||
|
||||
/* error(message) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStd, "error", ParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStd, "error", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
LogConsole::abortEvaluation(Token::literalToString(params[0], true));
|
||||
|
||||
return std::nullopt;
|
||||
});
|
||||
|
||||
/* warning(message) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStd, "warning", ParameterCount::exactly(1), [](Evaluator *ctx, auto params) -> std::optional<Token::Literal> {
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStd, "warning", FunctionParameterCount::exactly(1), [](Evaluator *ctx, auto params) -> std::optional<Token::Literal> {
|
||||
ctx->getConsole().log(LogConsole::Level::Warning, Token::literalToString(params[0], true));
|
||||
|
||||
return std::nullopt;
|
||||
});
|
||||
}
|
||||
|
||||
ContentRegistry::PatternLanguage::Namespace nsStdMem = { "builtin", "std", "mem" };
|
||||
api::Namespace nsStdMem = { "builtin", "std", "mem" };
|
||||
{
|
||||
|
||||
/* base_address() */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMem, "base_address", ParameterCount::none(), [](Evaluator *ctx, auto params) -> std::optional<Token::Literal> {
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMem, "base_address", FunctionParameterCount::none(), [](Evaluator *ctx, auto params) -> std::optional<Token::Literal> {
|
||||
hex::unused(params);
|
||||
|
||||
return u128(ctx->getProvider()->getBaseAddress());
|
||||
return u128(ctx->getDataBaseAddress());
|
||||
});
|
||||
|
||||
/* size() */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMem, "size", ParameterCount::none(), [](Evaluator *ctx, auto params) -> std::optional<Token::Literal> {
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMem, "size", FunctionParameterCount::none(), [](Evaluator *ctx, auto params) -> std::optional<Token::Literal> {
|
||||
hex::unused(params);
|
||||
|
||||
return u128(ctx->getProvider()->getActualSize());
|
||||
return u128(ctx->getDataSize());
|
||||
});
|
||||
|
||||
/* find_sequence_in_range(occurrence_index, start_offset, end_offset, bytes...) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMem, "find_sequence_in_range", ParameterCount::moreThan(3), [](Evaluator *ctx, auto params) -> std::optional<Token::Literal> {
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMem, "find_sequence_in_range", FunctionParameterCount::moreThan(3), [](Evaluator *ctx, auto params) -> std::optional<Token::Literal> {
|
||||
auto occurrenceIndex = Token::literalToUnsigned(params[0]);
|
||||
auto offsetFrom = Token::literalToUnsigned(params[1]);
|
||||
auto offsetTo = Token::literalToUnsigned(params[2]);
|
||||
@ -129,10 +128,10 @@ namespace hex::plugin::builtin {
|
||||
|
||||
std::vector<u8> bytes(sequence.size(), 0x00);
|
||||
u32 occurrences = 0;
|
||||
const u64 bufferSize = ctx->getProvider()->getSize();
|
||||
const u64 bufferSize = ctx->getDataSize();
|
||||
const u64 endOffset = offsetTo <= offsetFrom ? bufferSize : std::min(bufferSize, u64(offsetTo));
|
||||
for (u64 offset = offsetFrom; offset < endOffset - sequence.size(); offset++) {
|
||||
ctx->getProvider()->read(offset, bytes.data(), bytes.size());
|
||||
ctx->readData(offset, bytes.data(), bytes.size());
|
||||
|
||||
if (bytes == sequence) {
|
||||
if (occurrences < occurrenceIndex) {
|
||||
@ -148,7 +147,7 @@ namespace hex::plugin::builtin {
|
||||
});
|
||||
|
||||
/* read_unsigned(address, size) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMem, "read_unsigned", ParameterCount::exactly(2), [](Evaluator *ctx, auto params) -> std::optional<Token::Literal> {
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMem, "read_unsigned", FunctionParameterCount::exactly(2), [](Evaluator *ctx, auto params) -> std::optional<Token::Literal> {
|
||||
auto address = Token::literalToUnsigned(params[0]);
|
||||
auto size = Token::literalToUnsigned(params[1]);
|
||||
|
||||
@ -156,13 +155,13 @@ namespace hex::plugin::builtin {
|
||||
LogConsole::abortEvaluation("read size out of range");
|
||||
|
||||
u128 result = 0;
|
||||
ctx->getProvider()->read(address, &result, size);
|
||||
ctx->readData(address, &result, size);
|
||||
|
||||
return result;
|
||||
});
|
||||
|
||||
/* read_signed(address, size) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMem, "read_signed", ParameterCount::exactly(2), [](Evaluator *ctx, auto params) -> std::optional<Token::Literal> {
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMem, "read_signed", FunctionParameterCount::exactly(2), [](Evaluator *ctx, auto params) -> std::optional<Token::Literal> {
|
||||
auto address = Token::literalToUnsigned(params[0]);
|
||||
auto size = Token::literalToUnsigned(params[1]);
|
||||
|
||||
@ -170,33 +169,33 @@ namespace hex::plugin::builtin {
|
||||
LogConsole::abortEvaluation("read size out of range");
|
||||
|
||||
i128 value;
|
||||
ctx->getProvider()->read(address, &value, size);
|
||||
ctx->readData(address, &value, size);
|
||||
return hex::signExtend(size * 8, value);
|
||||
});
|
||||
|
||||
/* read_string(address, size) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMem, "read_string", ParameterCount::exactly(2), [](Evaluator *ctx, auto params) -> std::optional<Token::Literal> {
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMem, "read_string", FunctionParameterCount::exactly(2), [](Evaluator *ctx, auto params) -> std::optional<Token::Literal> {
|
||||
auto address = Token::literalToUnsigned(params[0]);
|
||||
auto size = Token::literalToUnsigned(params[1]);
|
||||
|
||||
std::string result(size, '\x00');
|
||||
ctx->getProvider()->read(address, result.data(), size);
|
||||
ctx->readData(address, result.data(), size);
|
||||
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
ContentRegistry::PatternLanguage::Namespace nsStdString = { "builtin", "std", "string" };
|
||||
api::Namespace nsStdString = { "builtin", "std", "string" };
|
||||
{
|
||||
/* length(string) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdString, "length", ParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdString, "length", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
auto string = Token::literalToString(params[0], false);
|
||||
|
||||
return u128(string.length());
|
||||
});
|
||||
|
||||
/* at(string, index) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdString, "at", ParameterCount::exactly(2), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdString, "at", FunctionParameterCount::exactly(2), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
auto string = Token::literalToString(params[0], false);
|
||||
auto index = Token::literalToSigned(params[1]);
|
||||
|
||||
@ -217,7 +216,7 @@ namespace hex::plugin::builtin {
|
||||
});
|
||||
|
||||
/* substr(string, pos, count) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdString, "substr", ParameterCount::exactly(3), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdString, "substr", FunctionParameterCount::exactly(3), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
auto string = Token::literalToString(params[0], false);
|
||||
auto pos = Token::literalToUnsigned(params[1]);
|
||||
auto size = Token::literalToUnsigned(params[2]);
|
||||
@ -229,7 +228,7 @@ namespace hex::plugin::builtin {
|
||||
});
|
||||
|
||||
/* parse_int(string, base) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdString, "parse_int", ParameterCount::exactly(2), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdString, "parse_int", FunctionParameterCount::exactly(2), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
auto string = Token::literalToString(params[0], false);
|
||||
auto base = Token::literalToUnsigned(params[1]);
|
||||
|
||||
@ -237,17 +236,17 @@ namespace hex::plugin::builtin {
|
||||
});
|
||||
|
||||
/* parse_float(string) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdString, "parse_float", ParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdString, "parse_float", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
auto string = Token::literalToString(params[0], false);
|
||||
|
||||
return double(std::strtod(string.c_str(), nullptr));
|
||||
});
|
||||
}
|
||||
|
||||
ContentRegistry::PatternLanguage::Namespace nsStdHttp = { "builtin", "std", "http" };
|
||||
api::Namespace nsStdHttp = { "builtin", "std", "http" };
|
||||
{
|
||||
/* get(url) */
|
||||
ContentRegistry::PatternLanguage::addDangerousFunction(nsStdHttp, "get", ParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
ContentRegistry::PatternLanguage::addDangerousFunction(nsStdHttp, "get", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
const auto url = Token::literalToString(params[0], false);
|
||||
|
||||
hex::Net net;
|
||||
@ -256,13 +255,13 @@ namespace hex::plugin::builtin {
|
||||
}
|
||||
|
||||
|
||||
ContentRegistry::PatternLanguage::Namespace nsStdFile = { "builtin", "std", "file" };
|
||||
api::Namespace nsStdFile = { "builtin", "std", "file" };
|
||||
{
|
||||
static u32 fileCounter = 0;
|
||||
static std::map<u32, fs::File> openFiles;
|
||||
|
||||
/* open(path, mode) */
|
||||
ContentRegistry::PatternLanguage::addDangerousFunction(nsStdFile, "open", ParameterCount::exactly(2), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
ContentRegistry::PatternLanguage::addDangerousFunction(nsStdFile, "open", FunctionParameterCount::exactly(2), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
const auto path = Token::literalToString(params[0], false);
|
||||
const auto modeEnum = Token::literalToUnsigned(params[1]);
|
||||
|
||||
@ -293,7 +292,7 @@ namespace hex::plugin::builtin {
|
||||
});
|
||||
|
||||
/* close(file) */
|
||||
ContentRegistry::PatternLanguage::addDangerousFunction(nsStdFile, "close", ParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
ContentRegistry::PatternLanguage::addDangerousFunction(nsStdFile, "close", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
const auto file = Token::literalToUnsigned(params[0]);
|
||||
|
||||
if (!openFiles.contains(file))
|
||||
@ -305,7 +304,7 @@ namespace hex::plugin::builtin {
|
||||
});
|
||||
|
||||
/* read(file, size) */
|
||||
ContentRegistry::PatternLanguage::addDangerousFunction(nsStdFile, "read", ParameterCount::exactly(2), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
ContentRegistry::PatternLanguage::addDangerousFunction(nsStdFile, "read", FunctionParameterCount::exactly(2), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
const auto file = Token::literalToUnsigned(params[0]);
|
||||
const auto size = Token::literalToUnsigned(params[1]);
|
||||
|
||||
@ -316,7 +315,7 @@ namespace hex::plugin::builtin {
|
||||
});
|
||||
|
||||
/* write(file, data) */
|
||||
ContentRegistry::PatternLanguage::addDangerousFunction(nsStdFile, "write", ParameterCount::exactly(2), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
ContentRegistry::PatternLanguage::addDangerousFunction(nsStdFile, "write", FunctionParameterCount::exactly(2), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
const auto file = Token::literalToUnsigned(params[0]);
|
||||
const auto data = Token::literalToString(params[1], true);
|
||||
|
||||
@ -329,7 +328,7 @@ namespace hex::plugin::builtin {
|
||||
});
|
||||
|
||||
/* seek(file, offset) */
|
||||
ContentRegistry::PatternLanguage::addDangerousFunction(nsStdFile, "seek", ParameterCount::exactly(2), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
ContentRegistry::PatternLanguage::addDangerousFunction(nsStdFile, "seek", FunctionParameterCount::exactly(2), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
const auto file = Token::literalToUnsigned(params[0]);
|
||||
const auto offset = Token::literalToUnsigned(params[1]);
|
||||
|
||||
@ -342,7 +341,7 @@ namespace hex::plugin::builtin {
|
||||
});
|
||||
|
||||
/* size(file) */
|
||||
ContentRegistry::PatternLanguage::addDangerousFunction(nsStdFile, "size", ParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
ContentRegistry::PatternLanguage::addDangerousFunction(nsStdFile, "size", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
const auto file = Token::literalToUnsigned(params[0]);
|
||||
|
||||
if (!openFiles.contains(file))
|
||||
@ -352,7 +351,7 @@ namespace hex::plugin::builtin {
|
||||
});
|
||||
|
||||
/* resize(file, size) */
|
||||
ContentRegistry::PatternLanguage::addDangerousFunction(nsStdFile, "resize", ParameterCount::exactly(2), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
ContentRegistry::PatternLanguage::addDangerousFunction(nsStdFile, "resize", FunctionParameterCount::exactly(2), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
const auto file = Token::literalToUnsigned(params[0]);
|
||||
const auto size = Token::literalToUnsigned(params[1]);
|
||||
|
||||
@ -365,7 +364,7 @@ namespace hex::plugin::builtin {
|
||||
});
|
||||
|
||||
/* flush(file) */
|
||||
ContentRegistry::PatternLanguage::addDangerousFunction(nsStdFile, "flush", ParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
ContentRegistry::PatternLanguage::addDangerousFunction(nsStdFile, "flush", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
const auto file = Token::literalToUnsigned(params[0]);
|
||||
|
||||
if (!openFiles.contains(file))
|
||||
@ -377,7 +376,7 @@ namespace hex::plugin::builtin {
|
||||
});
|
||||
|
||||
/* remove(file) */
|
||||
ContentRegistry::PatternLanguage::addDangerousFunction(nsStdFile, "remove", ParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
ContentRegistry::PatternLanguage::addDangerousFunction(nsStdFile, "remove", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
const auto file = Token::literalToUnsigned(params[0]);
|
||||
|
||||
if (!openFiles.contains(file))
|
||||
@ -390,129 +389,129 @@ namespace hex::plugin::builtin {
|
||||
}
|
||||
|
||||
|
||||
ContentRegistry::PatternLanguage::Namespace nsStdMath = { "builtin", "std", "math" };
|
||||
api::Namespace nsStdMath = { "builtin", "std", "math" };
|
||||
{
|
||||
/* floor(value) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "floor", ParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "floor", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
return std::floor(Token::literalToFloatingPoint(params[0]));
|
||||
});
|
||||
|
||||
/* ceil(value) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "ceil", ParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "ceil", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
return std::ceil(Token::literalToFloatingPoint(params[0]));
|
||||
});
|
||||
|
||||
/* round(value) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "round", ParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "round", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
return std::round(Token::literalToFloatingPoint(params[0]));
|
||||
});
|
||||
|
||||
/* trunc(value) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "trunc", ParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "trunc", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
return std::trunc(Token::literalToFloatingPoint(params[0]));
|
||||
});
|
||||
|
||||
|
||||
/* log10(value) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "log10", ParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "log10", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
return std::log10(Token::literalToFloatingPoint(params[0]));
|
||||
});
|
||||
|
||||
/* log2(value) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "log2", ParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "log2", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
return std::log2(Token::literalToFloatingPoint(params[0]));
|
||||
});
|
||||
|
||||
/* ln(value) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "ln", ParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "ln", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
return std::log(Token::literalToFloatingPoint(params[0]));
|
||||
});
|
||||
|
||||
|
||||
/* fmod(x, y) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "fmod", ParameterCount::exactly(2), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "fmod", FunctionParameterCount::exactly(2), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
return std::fmod(Token::literalToFloatingPoint(params[0]), Token::literalToFloatingPoint(params[1]));
|
||||
});
|
||||
|
||||
/* pow(base, exp) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "pow", ParameterCount::exactly(2), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "pow", FunctionParameterCount::exactly(2), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
return std::pow(Token::literalToFloatingPoint(params[0]), Token::literalToFloatingPoint(params[1]));
|
||||
});
|
||||
|
||||
/* sqrt(value) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "sqrt", ParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "sqrt", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
return std::sqrt(Token::literalToFloatingPoint(params[0]));
|
||||
});
|
||||
|
||||
/* cbrt(value) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "cbrt", ParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "cbrt", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
return std::cbrt(Token::literalToFloatingPoint(params[0]));
|
||||
});
|
||||
|
||||
|
||||
/* sin(value) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "sin", ParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "sin", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
return std::sin(Token::literalToFloatingPoint(params[0]));
|
||||
});
|
||||
|
||||
/* cos(value) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "cos", ParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "cos", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
return std::cos(Token::literalToFloatingPoint(params[0]));
|
||||
});
|
||||
|
||||
/* tan(value) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "tan", ParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "tan", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
return std::tan(Token::literalToFloatingPoint(params[0]));
|
||||
});
|
||||
|
||||
/* asin(value) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "asin", ParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "asin", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
return std::asin(Token::literalToFloatingPoint(params[0]));
|
||||
});
|
||||
|
||||
/* acos(value) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "acos", ParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "acos", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
return std::acos(Token::literalToFloatingPoint(params[0]));
|
||||
});
|
||||
|
||||
/* atan(value) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "atan", ParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "atan", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
return std::atan(Token::literalToFloatingPoint(params[0]));
|
||||
});
|
||||
|
||||
/* atan2(y, x) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "atan", ParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "atan", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
return std::atan2(Token::literalToFloatingPoint(params[0]), Token::literalToFloatingPoint(params[1]));
|
||||
});
|
||||
|
||||
|
||||
/* sinh(value) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "sinh", ParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "sinh", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
return std::sinh(Token::literalToFloatingPoint(params[0]));
|
||||
});
|
||||
|
||||
/* cosh(value) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "cosh", ParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "cosh", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
return std::cosh(Token::literalToFloatingPoint(params[0]));
|
||||
});
|
||||
|
||||
/* tanh(value) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "tanh", ParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "tanh", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
return std::tanh(Token::literalToFloatingPoint(params[0]));
|
||||
});
|
||||
|
||||
/* asinh(value) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "asinh", ParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "asinh", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
return std::asinh(Token::literalToFloatingPoint(params[0]));
|
||||
});
|
||||
|
||||
/* acosh(value) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "acosh", ParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "acosh", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
return std::acosh(Token::literalToFloatingPoint(params[0]));
|
||||
});
|
||||
|
||||
/* atanh(value) */
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "atanh", ParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
ContentRegistry::PatternLanguage::addFunction(nsStdMath, "atanh", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {
|
||||
return std::atanh(Token::literalToFloatingPoint(params[0]));
|
||||
});
|
||||
}
|
||||
|
85
plugins/builtin/source/content/pl_pragmas.cpp
Normal file
85
plugins/builtin/source/content/pl_pragmas.cpp
Normal file
@ -0,0 +1,85 @@
|
||||
#include <hex/api/content_registry.hpp>
|
||||
#include <hex/providers/provider.hpp>
|
||||
|
||||
#include <pl/evaluator.hpp>
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
void registerPatternLanguagePragmas() {
|
||||
ContentRegistry::PatternLanguage::addPragma("endian", [](pl::PatternLanguage &runtime, const std::string &value) {
|
||||
if (value == "big") {
|
||||
runtime.getInternals().evaluator->setDefaultEndian(std::endian::big);
|
||||
return true;
|
||||
} else if (value == "little") {
|
||||
runtime.getInternals().evaluator->setDefaultEndian(std::endian::little);
|
||||
return true;
|
||||
} else if (value == "native") {
|
||||
runtime.getInternals().evaluator->setDefaultEndian(std::endian::native);
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
});
|
||||
|
||||
ContentRegistry::PatternLanguage::addPragma("eval_depth", [](pl::PatternLanguage &runtime, const std::string &value) {
|
||||
auto limit = strtol(value.c_str(), nullptr, 0);
|
||||
|
||||
if (limit <= 0)
|
||||
return false;
|
||||
|
||||
runtime.getInternals().evaluator->setEvaluationDepth(limit);
|
||||
return true;
|
||||
});
|
||||
|
||||
ContentRegistry::PatternLanguage::addPragma("array_limit", [](pl::PatternLanguage &runtime, const std::string &value) {
|
||||
auto limit = strtol(value.c_str(), nullptr, 0);
|
||||
|
||||
if (limit <= 0)
|
||||
return false;
|
||||
|
||||
runtime.getInternals().evaluator->setArrayLimit(limit);
|
||||
return true;
|
||||
});
|
||||
|
||||
ContentRegistry::PatternLanguage::addPragma("pattern_limit", [](pl::PatternLanguage &runtime, const std::string &value) {
|
||||
auto limit = strtol(value.c_str(), nullptr, 0);
|
||||
|
||||
if (limit <= 0)
|
||||
return false;
|
||||
|
||||
runtime.getInternals().evaluator->setPatternLimit(limit);
|
||||
return true;
|
||||
});
|
||||
|
||||
ContentRegistry::PatternLanguage::addPragma("loop_limit", [](pl::PatternLanguage &runtime, const std::string &value) {
|
||||
auto limit = strtol(value.c_str(), nullptr, 0);
|
||||
|
||||
if (limit <= 0)
|
||||
return false;
|
||||
|
||||
runtime.getInternals().evaluator->setLoopLimit(limit);
|
||||
return true;
|
||||
});
|
||||
|
||||
ContentRegistry::PatternLanguage::addPragma("base_address", [](pl::PatternLanguage &runtime, const std::string &value) {
|
||||
hex::unused(runtime);
|
||||
|
||||
auto baseAddress = strtoull(value.c_str(), nullptr, 0);
|
||||
|
||||
ImHexApi::Provider::get()->setBaseAddress(baseAddress);
|
||||
return true;
|
||||
});
|
||||
|
||||
ContentRegistry::PatternLanguage::addPragma("bitfield_order", [](pl::PatternLanguage &runtime, const std::string &value) {
|
||||
if (value == "left_to_right") {
|
||||
runtime.getInternals().evaluator->setBitfieldOrder(pl::BitfieldOrder::LeftToRight);
|
||||
return true;
|
||||
} else if (value == "right_to_left") {
|
||||
runtime.getInternals().evaluator->setBitfieldOrder(pl::BitfieldOrder::RightToLeft);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -133,6 +133,8 @@ namespace hex::plugin::builtin::prv {
|
||||
|
||||
#endif
|
||||
|
||||
Provider::resize(this->getActualSize());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -264,6 +264,8 @@ namespace hex::plugin::builtin::prv {
|
||||
}
|
||||
#endif
|
||||
|
||||
Provider::resize(this->getActualSize());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -278,6 +278,8 @@ namespace hex::plugin::builtin::prv {
|
||||
}
|
||||
});
|
||||
|
||||
Provider::resize(this->getActualSize());
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include <imgui.h>
|
||||
#include <hex/ui/imgui_imhex_extensions.h>
|
||||
#include <fonts/codicons_font.h>
|
||||
#include <hex/helpers/utils.hpp>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
|
@ -2,8 +2,8 @@
|
||||
|
||||
#include <hex/providers/provider.hpp>
|
||||
|
||||
#include <hex/pattern_language/pattern_language.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern.hpp>
|
||||
#include <pl/pattern_language.hpp>
|
||||
#include <pl/patterns/pattern.hpp>
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
@ -20,6 +20,53 @@ namespace hex::plugin::builtin {
|
||||
EventManager::unsubscribe<EventHighlightingChanged>(this);
|
||||
}
|
||||
|
||||
static bool sortPatterns(prv::Provider *provider, const ImGuiTableSortSpecs* sortSpecs, const pl::Pattern * left, const pl::Pattern * right) {
|
||||
if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("name")) {
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return left->getDisplayName() > right->getDisplayName();
|
||||
else
|
||||
return left->getDisplayName() < right->getDisplayName();
|
||||
} else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("offset")) {
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return left->getOffset() > right->getOffset();
|
||||
else
|
||||
return left->getOffset() < right->getOffset();
|
||||
} else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("size")) {
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return left->getSize() > right->getSize();
|
||||
else
|
||||
return left->getSize() < right->getSize();
|
||||
} else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("value")) {
|
||||
size_t biggerSize = std::max(left->getSize(), right->getSize());
|
||||
std::vector<u8> leftBuffer(biggerSize, 0x00), rightBuffer(biggerSize, 0x00);
|
||||
|
||||
provider->read(left->getOffset(), leftBuffer.data(), left->getSize());
|
||||
provider->read(right->getOffset(), rightBuffer.data(), right->getSize());
|
||||
|
||||
if (left->getEndian() != std::endian::native)
|
||||
std::reverse(leftBuffer.begin(), leftBuffer.end());
|
||||
if (right->getEndian() != std::endian::native)
|
||||
std::reverse(rightBuffer.begin(), rightBuffer.end());
|
||||
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return leftBuffer > rightBuffer;
|
||||
else
|
||||
return leftBuffer < rightBuffer;
|
||||
} else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("type")) {
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return left->getTypeName() > right->getTypeName();
|
||||
else
|
||||
return left->getTypeName() < right->getTypeName();
|
||||
} else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("color")) {
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return left->getColor() > right->getColor();
|
||||
else
|
||||
return left->getColor() < right->getColor();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool beginPatternTable(prv::Provider *&provider, const std::vector<std::shared_ptr<pl::Pattern>> &patterns, std::vector<std::shared_ptr<pl::Pattern>> &sortedPatterns) {
|
||||
if (ImGui::BeginTable("##Patterntable", 6, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Sortable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY)) {
|
||||
ImGui::TableSetupScrollFreeze(0, 1);
|
||||
@ -36,11 +83,13 @@ namespace hex::plugin::builtin {
|
||||
sortedPatterns = patterns;
|
||||
|
||||
std::sort(sortedPatterns.begin(), sortedPatterns.end(), [&sortSpecs, &provider](const std::shared_ptr<pl::Pattern> &left, const std::shared_ptr<pl::Pattern> &right) -> bool {
|
||||
return pl::Pattern::sortPatternTable(sortSpecs, provider, left.get(), right.get());
|
||||
return sortPatterns(provider, sortSpecs, left.get(), right.get());
|
||||
});
|
||||
|
||||
for (auto &pattern : sortedPatterns)
|
||||
pattern->sort(sortSpecs, provider);
|
||||
pattern->sort([&sortSpecs, &provider](const pl::Pattern *left, const pl::Pattern *right){
|
||||
return sortPatterns(provider, sortSpecs, left, right);
|
||||
});
|
||||
|
||||
sortSpecs->SpecsDirty = false;
|
||||
}
|
||||
@ -61,9 +110,8 @@ namespace hex::plugin::builtin {
|
||||
ImGui::TableHeadersRow();
|
||||
if (!sortedPatterns.empty()) {
|
||||
|
||||
m_drawer.setProvider(provider);
|
||||
for (auto &patterns : sortedPatterns)
|
||||
patterns->accept(m_drawer);
|
||||
patterns->accept(this->m_patternDrawer);
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
|
@ -1,12 +1,12 @@
|
||||
#include "content/views/view_pattern_editor.hpp"
|
||||
|
||||
#include <pl/preprocessor.hpp>
|
||||
#include <pl/patterns/pattern.hpp>
|
||||
#include <pl/ast/ast_node_variable_decl.hpp>
|
||||
#include <pl/ast/ast_node_type_decl.hpp>
|
||||
#include <pl/ast/ast_node_builtin_type.hpp>
|
||||
|
||||
#include <hex/pattern_language/preprocessor.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_variable_decl.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_type_decl.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_builtin_type.hpp>
|
||||
|
||||
#include <hex/api/content_registry.hpp>
|
||||
#include <hex/helpers/fs.hpp>
|
||||
#include <hex/helpers/utils.hpp>
|
||||
#include <hex/helpers/file.hpp>
|
||||
@ -105,16 +105,18 @@ namespace hex::plugin::builtin {
|
||||
if (!ContentRegistry::Settings::read("hex.builtin.setting.general", "hex.builtin.setting.general.auto_load_patterns", 1))
|
||||
return;
|
||||
|
||||
pl::Preprocessor preprocessor;
|
||||
preprocessor.addDefaultPragmaHandlers();
|
||||
|
||||
if (!ImHexApi::Provider::isValid())
|
||||
return;
|
||||
|
||||
std::string mimeType = magic::getMIMEType(ImHexApi::Provider::get());
|
||||
auto provider = ImHexApi::Provider::get();
|
||||
auto runtime = provider->getPatternLanguageRuntime();
|
||||
|
||||
auto mimeType = magic::getMIMEType(ImHexApi::Provider::get());
|
||||
|
||||
bool foundCorrectType = false;
|
||||
preprocessor.addPragmaHandler("MIME", [&mimeType, &foundCorrectType](std::string value) {
|
||||
runtime.addPragma("MIME", [&mimeType, &foundCorrectType](pl::PatternLanguage &runtime, const std::string &value) {
|
||||
hex::unused(runtime);
|
||||
|
||||
if (value == mimeType) {
|
||||
foundCorrectType = true;
|
||||
return true;
|
||||
@ -135,13 +137,14 @@ namespace hex::plugin::builtin {
|
||||
if (!file.isValid())
|
||||
continue;
|
||||
|
||||
preprocessor.preprocess(file.readString());
|
||||
runtime.getInternals().preprocessor->preprocess(runtime, file.readString());
|
||||
|
||||
if (foundCorrectType)
|
||||
this->m_possiblePatternFiles.push_back(entry.path());
|
||||
}
|
||||
}
|
||||
|
||||
runtime.removePragma("MIME");
|
||||
|
||||
if (!this->m_possiblePatternFiles.empty()) {
|
||||
this->m_selectedPatternFile = 0;
|
||||
@ -329,7 +332,7 @@ namespace hex::plugin::builtin {
|
||||
}
|
||||
}
|
||||
|
||||
if (provider->getPatternLanguageRuntime().hasDangerousFunctionBeenCalled() && !ImGui::IsPopupOpen(View::toWindowName("hex.builtin.view.pattern_editor.dangerous_function.name").c_str())) {
|
||||
if (this->m_dangerousFunctionCalled && !ImGui::IsPopupOpen(View::toWindowName("hex.builtin.view.pattern_editor.dangerous_function.name").c_str())) {
|
||||
ImGui::OpenPopup(View::toWindowName("hex.builtin.view.pattern_editor.dangerous_function.name").c_str());
|
||||
}
|
||||
|
||||
@ -339,11 +342,14 @@ namespace hex::plugin::builtin {
|
||||
ImGui::NewLine();
|
||||
|
||||
View::confirmButtons(
|
||||
"hex.builtin.common.yes"_lang, "hex.builtin.common.no"_lang, [] {
|
||||
ImHexApi::Provider::get()->getPatternLanguageRuntime().allowDangerousFunctions(true);
|
||||
ImGui::CloseCurrentPopup(); }, [] {
|
||||
ImHexApi::Provider::get()->getPatternLanguageRuntime().allowDangerousFunctions(false);
|
||||
ImGui::CloseCurrentPopup(); });
|
||||
"hex.builtin.common.yes"_lang, "hex.builtin.common.no"_lang,
|
||||
[this] {
|
||||
this->m_dangerousFunctionsAllowed = DangerousFunctionPerms::Allow;
|
||||
ImGui::CloseCurrentPopup();
|
||||
}, [this] {
|
||||
this->m_dangerousFunctionsAllowed = DangerousFunctionPerms::Deny;
|
||||
ImGui::CloseCurrentPopup();
|
||||
});
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
@ -616,7 +622,6 @@ namespace hex::plugin::builtin {
|
||||
if (!ImHexApi::Provider::isValid()) return;
|
||||
|
||||
ImHexApi::Provider::get()->getPatternLanguageRuntime().reset();
|
||||
ContentRegistry::PatternLanguage::resetPalette();
|
||||
}
|
||||
|
||||
|
||||
@ -679,7 +684,17 @@ namespace hex::plugin::builtin {
|
||||
auto provider = ImHexApi::Provider::get();
|
||||
auto &runtime = provider->getPatternLanguageRuntime();
|
||||
|
||||
this->m_lastEvaluationResult = runtime.executeString(provider, code, envVars, inVariables);
|
||||
runtime.setDangerousFunctionCallHandler([this]{
|
||||
this->m_dangerousFunctionCalled = true;
|
||||
|
||||
while (this->m_dangerousFunctionsAllowed == DangerousFunctionPerms::Ask) {
|
||||
std::this_thread::yield();
|
||||
}
|
||||
|
||||
return this->m_dangerousFunctionsAllowed == DangerousFunctionPerms::Allow;
|
||||
});
|
||||
|
||||
this->m_lastEvaluationResult = runtime.executeString(code, envVars, inVariables);
|
||||
if (!this->m_lastEvaluationResult) {
|
||||
this->m_lastEvaluationError = runtime.getError();
|
||||
}
|
||||
|
@ -1,24 +1,30 @@
|
||||
#include "pattern_drawer.hpp"
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern_array_dynamic.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_array_static.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_bitfield.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_boolean.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_character.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_enum.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_float.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_padding.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_pointer.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_signed.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_string.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_struct.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_union.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_unsigned.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_wide_character.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_wide_string.hpp>
|
||||
#include <pl/patterns/pattern_array_dynamic.hpp>
|
||||
#include <pl/patterns/pattern_array_static.hpp>
|
||||
#include <pl/patterns/pattern_bitfield.hpp>
|
||||
#include <pl/patterns/pattern_boolean.hpp>
|
||||
#include <pl/patterns/pattern_character.hpp>
|
||||
#include <pl/patterns/pattern_enum.hpp>
|
||||
#include <pl/patterns/pattern_float.hpp>
|
||||
#include <pl/patterns/pattern_padding.hpp>
|
||||
#include <pl/patterns/pattern_pointer.hpp>
|
||||
#include <pl/patterns/pattern_signed.hpp>
|
||||
#include <pl/patterns/pattern_string.hpp>
|
||||
#include <pl/patterns/pattern_struct.hpp>
|
||||
#include <pl/patterns/pattern_union.hpp>
|
||||
#include <pl/patterns/pattern_unsigned.hpp>
|
||||
#include <pl/patterns/pattern_wide_character.hpp>
|
||||
#include <pl/patterns/pattern_wide_string.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <hex/api/imhex_api.hpp>
|
||||
#include <hex/helpers/utils.hpp>
|
||||
|
||||
#include <imgui.h>
|
||||
#include <hex/ui/imgui_imhex_extensions.h>
|
||||
|
||||
namespace {
|
||||
constexpr static auto DisplayEndDefault = 50u;
|
||||
constexpr static auto DisplayEndStep = 50u;
|
||||
@ -28,14 +34,6 @@ namespace {
|
||||
|
||||
namespace hex {
|
||||
|
||||
PatternDrawer::PatternDrawer()
|
||||
: m_provider{nullptr}
|
||||
{ }
|
||||
|
||||
void PatternDrawer::setProvider(prv::Provider *provider) {
|
||||
m_provider = provider;
|
||||
}
|
||||
|
||||
void PatternDrawer::visit(pl::PatternArrayDynamic& pattern) {
|
||||
this->drawArray(pattern);
|
||||
}
|
||||
@ -67,12 +65,12 @@ namespace hex {
|
||||
ImGui::TextFormattedColored(ImColor(0xFF9BC64D), "bits");
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
u64 extractedValue = pattern.getValue(m_provider);
|
||||
u64 extractedValue = pattern.getValue();
|
||||
ImGui::TextFormatted("{}", pattern.formatDisplayValue(hex::format("{0} (0x{1:X})", extractedValue, extractedValue), &pattern));
|
||||
}
|
||||
|
||||
void PatternDrawer::visit(pl::PatternBitfield& pattern) {
|
||||
std::vector<u8> value = pattern.getValue(m_provider);
|
||||
std::vector<u8> value = pattern.getValue();
|
||||
|
||||
bool open = true;
|
||||
if (!pattern.isInlined()) {
|
||||
@ -108,7 +106,7 @@ namespace hex {
|
||||
}
|
||||
|
||||
void PatternDrawer::visit(pl::PatternBoolean& pattern) {
|
||||
u8 boolean = pattern.getValue(m_provider);
|
||||
u8 boolean = pattern.getValue();
|
||||
|
||||
if (boolean == 0)
|
||||
this->createDefaultEntry(pattern, "false", false);
|
||||
@ -119,12 +117,12 @@ namespace hex {
|
||||
}
|
||||
|
||||
void PatternDrawer::visit(pl::PatternCharacter& pattern) {
|
||||
char character = pattern.getValue(m_provider);
|
||||
char character = pattern.getValue();
|
||||
this->createDefaultEntry(pattern, hex::format("'{0}'", character), character);
|
||||
}
|
||||
|
||||
void PatternDrawer::visit(pl::PatternEnum& pattern) {
|
||||
u64 value = pattern.getValue(m_provider);
|
||||
u64 value = pattern.getValue();
|
||||
|
||||
std::string valueString = pattern.getTypeName() + "::";
|
||||
|
||||
@ -168,12 +166,12 @@ namespace hex {
|
||||
|
||||
void PatternDrawer::visit(pl::PatternFloat& pattern) {
|
||||
if (pattern.getSize() == 4) {
|
||||
float f32 = static_cast<float>(pattern.getValue(m_provider));
|
||||
float f32 = static_cast<float>(pattern.getValue());
|
||||
u32 integerResult = 0;
|
||||
std::memcpy(&integerResult, &f32, sizeof(float));
|
||||
this->createDefaultEntry(pattern, hex::format("{:e} (0x{:0{}X})", f32, integerResult, pattern.getSize() * 2), f32);
|
||||
} else if (pattern.getSize() == 8) {
|
||||
double f64 = pattern.getValue(m_provider);
|
||||
double f64 = pattern.getValue();
|
||||
u64 integerResult = 0;
|
||||
std::memcpy(&integerResult, &f64, sizeof(double));
|
||||
this->createDefaultEntry(pattern, hex::format("{:e} (0x{:0{}X})", f64, integerResult, pattern.getSize() * 2), f64);
|
||||
@ -186,7 +184,7 @@ namespace hex {
|
||||
}
|
||||
|
||||
void PatternDrawer::visit(pl::PatternPointer& pattern) {
|
||||
u64 data = pattern.getValue(m_provider);
|
||||
u64 data = pattern.getValue();
|
||||
|
||||
bool open = true;
|
||||
|
||||
@ -217,7 +215,7 @@ namespace hex {
|
||||
}
|
||||
|
||||
void PatternDrawer::visit(pl::PatternSigned& pattern) {
|
||||
i128 data = pattern.getValue(m_provider);
|
||||
i128 data = pattern.getValue();
|
||||
this->createDefaultEntry(pattern, hex::format("{:d} (0x{:0{}X})", data, data, 1 * 2), data);
|
||||
}
|
||||
|
||||
@ -227,7 +225,7 @@ namespace hex {
|
||||
if (size == 0)
|
||||
return;
|
||||
|
||||
std::string displayString = pattern.getValue(m_provider, size);
|
||||
std::string displayString = pattern.getValue(size);
|
||||
this->createDefaultEntry(pattern, hex::format("\"{0}\" {1}", displayString, size > pattern.getSize() ? "(truncated)" : ""), displayString);
|
||||
}
|
||||
|
||||
@ -290,12 +288,12 @@ namespace hex {
|
||||
}
|
||||
|
||||
void PatternDrawer::visit(pl::PatternUnsigned& pattern) {
|
||||
u128 data = pattern.getValue(m_provider);
|
||||
u128 data = pattern.getValue();
|
||||
this->createDefaultEntry(pattern, hex::format("{:d} (0x{:0{}X})", data, data, pattern.getSize() * 2), data);
|
||||
}
|
||||
|
||||
void PatternDrawer::visit(pl::PatternWideCharacter& pattern) {
|
||||
char16_t character = pattern.getValue(m_provider);
|
||||
char16_t character = pattern.getValue();
|
||||
u128 literal = character;
|
||||
auto str = std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> {}.to_bytes(character);
|
||||
this->createDefaultEntry(pattern, hex::format("'{0}'", str), literal);
|
||||
@ -307,7 +305,7 @@ namespace hex {
|
||||
if (size == 0)
|
||||
return;
|
||||
|
||||
std::string utf8String = pattern.getValue(m_provider, size);
|
||||
std::string utf8String = pattern.getValue(size);
|
||||
|
||||
this->createDefaultEntry(pattern, hex::format("\"{0}\" {1}", utf8String, size > pattern.getSize() ? "(truncated)" : ""), utf8String);
|
||||
}
|
||||
|
@ -1,13 +1,12 @@
|
||||
#include <hex/plugin.hpp>
|
||||
|
||||
#include <hex/api/imhex_api.hpp>
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
void registerViews();
|
||||
void registerDataInspectorEntries();
|
||||
void registerToolEntries();
|
||||
void registerPatternLanguageFunctions();
|
||||
void registerPatternLanguagePragmas();
|
||||
void registerCommandPaletteCommands();
|
||||
void registerSettings();
|
||||
void registerDataProcessorNodes();
|
||||
@ -42,6 +41,7 @@ IMHEX_PLUGIN_SETUP("Built-in", "WerWolv", "Default ImHex functionality") {
|
||||
registerDataInspectorEntries();
|
||||
registerToolEntries();
|
||||
registerPatternLanguageFunctions();
|
||||
registerPatternLanguagePragmas();
|
||||
registerCommandPaletteCommands();
|
||||
registerSettings();
|
||||
registerDataProcessorNodes();
|
||||
|
@ -20,7 +20,7 @@ namespace hex::test {
|
||||
second->setEntries(create<PatternUnsigned>("u8", "", 0x4, sizeof(u8)), 4);
|
||||
|
||||
auto testStruct = create<PatternStruct>("Signature", "sign", 0x0, sizeof(u8[8]));
|
||||
std::vector<std::shared_ptr<hex::pl::Pattern>> structMembers;
|
||||
std::vector<std::shared_ptr<pl::Pattern>> structMembers;
|
||||
{
|
||||
structMembers.push_back(std::move(first));
|
||||
structMembers.push_back(std::move(second));
|
||||
|
@ -12,7 +12,7 @@ namespace hex::test {
|
||||
auto testBitfield = create<PatternBitfield>("TestBitfield", "testBitfield", 0x12, (4 * 4) / 8);
|
||||
testBitfield->setEndian(std::endian::big);
|
||||
|
||||
std::vector<std::shared_ptr<hex::pl::Pattern>> bitfieldFields;
|
||||
std::vector<std::shared_ptr<pl::Pattern>> bitfieldFields;
|
||||
{
|
||||
bitfieldFields.push_back(create<PatternBitfieldField>("", "a", 0x12, 0, 4, testBitfield.get()));
|
||||
bitfieldFields.push_back(create<PatternBitfieldField>("", "b", 0x12, 4, 4, testBitfield.get()));
|
||||
|
@ -22,7 +22,7 @@ namespace hex::test {
|
||||
{
|
||||
auto hdr = create<PatternStruct>("Header", "hdr", HEADER_START, HEADER_SIZE);
|
||||
{
|
||||
std::vector<std::shared_ptr<hex::pl::Pattern>> hdrMembers {
|
||||
std::vector<std::shared_ptr<pl::Pattern>> hdrMembers {
|
||||
std::shared_ptr(create<PatternUnsigned>("u8", "len", HEADER_START, sizeof(u8)))
|
||||
};
|
||||
hdr->setMembers(std::move(hdrMembers));
|
||||
@ -32,13 +32,13 @@ namespace hex::test {
|
||||
{
|
||||
auto bodyArray = create<PatternArrayStatic>("u8", "arr", BODY_START, BODY_SIZE);
|
||||
bodyArray->setEntries(create<PatternUnsigned>("u8", "", BODY_START, sizeof(u8)), BODY_SIZE);
|
||||
std::vector<std::shared_ptr<hex::pl::Pattern>> bodyMembers {
|
||||
std::vector<std::shared_ptr<pl::Pattern>> bodyMembers {
|
||||
std::shared_ptr(std::move(bodyArray))
|
||||
};
|
||||
body->setMembers(std::move(bodyMembers));
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<hex::pl::Pattern>> dataMembers {
|
||||
std::vector<std::shared_ptr<pl::Pattern>> dataMembers {
|
||||
std::shared_ptr(std::move(hdr)),
|
||||
std::shared_ptr(std::move(body))
|
||||
};
|
||||
|
@ -20,7 +20,7 @@ namespace hex::test {
|
||||
auto array = create<PatternArrayStatic>("u8", "array", 0x100 + sizeof(i32) + 20, sizeof(u8[0x10]));
|
||||
array->setEntries(create<PatternUnsigned>("u8", "", 0x100 + sizeof(i32) + 20, sizeof(u8)), 0x10);
|
||||
|
||||
std::vector<std::shared_ptr<hex::pl::Pattern>> structMembers;
|
||||
std::vector<std::shared_ptr<pl::Pattern>> structMembers;
|
||||
{
|
||||
structMembers.push_back(std::move(variable));
|
||||
structMembers.push_back(std::move(padding));
|
||||
|
@ -18,7 +18,7 @@ namespace hex::test {
|
||||
auto array = create<PatternArrayStatic>("u8", "array", 0x100 + sizeof(i32), sizeof(u8[0x10]));
|
||||
array->setEntries(create<PatternUnsigned>("u8", "", 0x100 + sizeof(i32), sizeof(u8)), 0x10);
|
||||
|
||||
std::vector<std::shared_ptr<hex::pl::Pattern>> structMembers;
|
||||
std::vector<std::shared_ptr<pl::Pattern>> structMembers;
|
||||
{
|
||||
structMembers.push_back(std::move(variable));
|
||||
structMembers.push_back(std::move(array));
|
||||
|
@ -18,7 +18,7 @@ namespace hex::test {
|
||||
array->setEntries(create<PatternSigned>("s32", "", 0x200, sizeof(i32)), 2);
|
||||
auto variable = create<PatternUnsigned>("u128", "variable", 0x200, sizeof(u128));
|
||||
|
||||
std::vector<std::shared_ptr<hex::pl::Pattern>> unionMembers;
|
||||
std::vector<std::shared_ptr<pl::Pattern>> unionMembers;
|
||||
{
|
||||
unionMembers.push_back(std::move(array));
|
||||
unionMembers.push_back(std::move(variable));
|
||||
|
@ -18,8 +18,8 @@
|
||||
|
||||
using namespace hex::test;
|
||||
|
||||
static std::string format(hex::pl::Evaluator *ctx, const auto ¶ms) {
|
||||
auto format = hex::pl::Token::literalToString(params[0], true);
|
||||
static std::string format(pl::Evaluator *ctx, const auto ¶ms) {
|
||||
auto format = pl::Token::literalToString(params[0], true);
|
||||
std::string message;
|
||||
|
||||
fmt::dynamic_format_arg_store<fmt::format_context> formatArgs;
|
||||
@ -28,7 +28,7 @@ static std::string format(hex::pl::Evaluator *ctx, const auto ¶ms) {
|
||||
auto ¶m = params[i];
|
||||
|
||||
std::visit(hex::overloaded {
|
||||
[&](hex::pl::Pattern *value) {
|
||||
[&](pl::Pattern *value) {
|
||||
formatArgs.push_back(value->toString(ctx->getProvider()));
|
||||
},
|
||||
[&](auto &&value) {
|
||||
@ -40,7 +40,7 @@ static std::string format(hex::pl::Evaluator *ctx, const auto ¶ms) {
|
||||
try {
|
||||
return fmt::vformat(format, formatArgs);
|
||||
} catch (fmt::format_error &error) {
|
||||
hex::pl::LogConsole::abortEvaluation(hex::format("format error: {}", error.what()));
|
||||
pl::LogConsole::abortEvaluation(hex::format("format error: {}", error.what()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,7 +91,7 @@ int test(int argc, char **argv) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
hex::pl::PatternLanguage language;
|
||||
pl::PatternLanguage language;
|
||||
|
||||
// Check if compilation succeeded
|
||||
auto result = language.executeString(provider, testPatterns[testName]->getSourceCode());
|
||||
|
Loading…
Reference in New Issue
Block a user