1
0
mirror of synced 2024-11-27 17:10:51 +01:00

patterns: Huge refactor of Pattern Language runtime to use smart pointers (#458)

* patterns: Initial work to refactor pattern language to use smart pointers

* patterns: Fixed remaining issues, moved patterns to unique files

* sys: Added missing includes for macOS
This commit is contained in:
WerWolv 2022-02-27 23:25:39 +01:00 committed by GitHub
parent b28eaf2dbf
commit 66d1b3fd2f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
85 changed files with 4990 additions and 5146 deletions

2
.gitignore vendored
View File

@ -12,3 +12,5 @@ imgui.ini
plugins/.rustc_info.json
**/target
plugins/example_rust/Cargo.lock

View File

@ -31,6 +31,7 @@ add_subdirectory(main)
add_custom_target(imhex_all ALL DEPENDS main)
# Add unit tests
enable_testing()
add_subdirectory(tests EXCLUDE_FROM_ALL)
# Configure packaging

View File

@ -97,7 +97,7 @@ namespace hex {
};
namespace pl {
class PatternData;
class Pattern;
}
/* Default Events */

View File

@ -3,6 +3,7 @@
#include <hex.hpp>
#include <type_traits>
#include <memory>
namespace hex {
@ -166,7 +167,7 @@ namespace hex {
template<typename T>
class Cloneable {
public:
[[nodiscard]] virtual T *clone() const = 0;
[[nodiscard]] virtual std::unique_ptr<T> clone() const = 0;
};
}

View File

@ -193,6 +193,22 @@ namespace hex {
return T(1) << bit_width(T(x - 1));
}
template<typename T, typename... Args>
void moveToVector(std::vector<T> &buffer, T &&first, Args &&...rest) {
buffer.push_back(std::move(first));
if constexpr (sizeof...(rest) > 0)
moveToVector(buffer, std::move(rest)...);
}
template<typename T, typename... Args>
std::vector<T> moveToVector(T &&first, Args &&...rest) {
std::vector<T> result;
moveToVector(result, T(std::move(first)), std::move(rest)...);
return result;
}
std::vector<std::string> splitString(const std::string &string, const std::string &delimiter);
std::string combineStrings(const std::vector<std::string> &strings, const std::string &delimiter = "");

View File

@ -0,0 +1,36 @@
#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 { return this->clone(); }
[[nodiscard]] virtual std::vector<std::unique_ptr<Pattern>> createPatterns(Evaluator *evaluator) const { return {}; }
using FunctionResult = std::optional<Token::Literal>;
virtual FunctionResult execute(Evaluator *evaluator) const { LogConsole::abortEvaluation("cannot execute non-function statement", this); }
private:
u32 m_lineNumber = 1;
};
}

View File

@ -0,0 +1,309 @@
#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::unique_ptr<ASTNode> &&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->clone();
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](const std::shared_ptr<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::unique_ptr<ASTNode> 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](const std::shared_ptr<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](const std::shared_ptr<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();
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();
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();
if (ctrlFlow == ControlFlowStatement::Break)
break;
else if (ctrlFlow == ControlFlowStatement::Continue) {
discardEntries(1);
continue;
}
if (reachedEnd) break;
}
}
arrayPattern->setEntries(std::move(entries));
if (auto &arrayEntries = arrayPattern->getEntries(); !entries.empty())
arrayPattern->setTypeName(arrayEntries.front()->getTypeName());
arrayPattern->setSize(size);
return std::move(arrayPattern);
}
};
}

View File

@ -0,0 +1,53 @@
#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;
};
}

View File

@ -0,0 +1,216 @@
#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));
}
}
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);
}
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 != 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 != 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 != 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 != 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();
}
}
}

View File

@ -0,0 +1,85 @@
#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;
bool isLeftToRight = false;
if (this->hasAttribute("left_to_right", false))
isLeftToRight = true;
else if (this->hasAttribute("right_to_left", false))
isLeftToRight = false;
std::vector<std::pair<std::string, ASTNode *>> entries;
for (const auto &[name, entry] : this->m_entries)
entries.push_back({ name, entry.get() });
if (isLeftToRight)
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](const std::shared_ptr<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;
};
}

View File

@ -0,0 +1,64 @@
#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;
};
}

View File

@ -0,0 +1,92 @@
#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 startOffset = evaluator->dataOffset();
auto typePatterns = this->m_type->createPatterns(evaluator);
auto &typePattern = typePatterns.front();
return std::unique_ptr<ASTNode>(std::visit(overloaded {
[&, this](const std::shared_ptr<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;
};
}

View File

@ -0,0 +1,80 @@
#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;
};
}

View File

@ -0,0 +1,92 @@
#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;
};
}

View File

@ -0,0 +1,54 @@
#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]] const std::unique_ptr<ASTNode> &getReturnValue() 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 {
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;
};
}

View File

@ -0,0 +1,59 @@
#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;
};
}

View File

@ -0,0 +1,126 @@
#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>> &&params)
: 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 &param : 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 {
std::vector<Token::Literal> evaluatedParams;
for (auto &param : 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);
}
auto function = functions[this->m_functionName];
if (function.parameterCount == ContentRegistry::PatternLanguage::UnlimitedParameters) {
; // Don't check parameter count
} else if (function.parameterCount & ContentRegistry::PatternLanguage::LessParametersThan) {
if (evaluatedParams.size() >= (function.parameterCount & ~ContentRegistry::PatternLanguage::LessParametersThan))
LogConsole::abortEvaluation(hex::format("too many parameters for function '{0}'. Expected less than {1}", this->m_functionName, function.parameterCount & ~ContentRegistry::PatternLanguage::LessParametersThan), this);
} else if (function.parameterCount & ContentRegistry::PatternLanguage::MoreParametersThan) {
if (evaluatedParams.size() <= (function.parameterCount & ~ContentRegistry::PatternLanguage::MoreParametersThan))
LogConsole::abortEvaluation(hex::format("too few parameters for function '{0}'. Expected more than {1}", this->m_functionName, function.parameterCount & ~ContentRegistry::PatternLanguage::MoreParametersThan), this);
} else if (function.parameterCount & ContentRegistry::PatternLanguage::ExactlyOrMoreParametersThan) {
if (evaluatedParams.size() < (function.parameterCount & ~ContentRegistry::PatternLanguage::ExactlyOrMoreParametersThan))
LogConsole::abortEvaluation(hex::format("too few parameters for function '{0}'. Expected more than {1}", this->m_functionName, (function.parameterCount - 1) & ~ContentRegistry::PatternLanguage::ExactlyOrMoreParametersThan), this);
} else if (function.parameterCount != evaluatedParams.size()) {
LogConsole::abortEvaluation(hex::format("invalid number of parameters for function '{0}'. Expected {1}", this->m_functionName, function.parameterCount), 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;
};
}

View File

@ -0,0 +1,103 @@
#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>>> &&params, std::vector<std::unique_ptr<ASTNode>> &&body, std::optional<std::string> parameterPack)
: m_name(std::move(name)), m_params(std::move(params)), m_body(std::move(body)), m_parameterPack(std::move(parameterPack)) {
}
ASTNodeFunctionDefinition(const ASTNodeFunctionDefinition &other) : ASTNode(other) {
this->m_name = other.m_name;
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());
}
}
[[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 {
size_t paramCount = this->m_params.size();
if (this->m_parameterPack.has_value())
paramCount |= ContentRegistry::PatternLanguage::ExactlyOrMoreParametersThan;
evaluator->addCustomFunction(this->m_name, paramCount, [this](Evaluator *ctx, const std::vector<Token::Literal> &params) -> std::optional<Token::Literal> {
std::vector<std::shared_ptr<Pattern>> variables;
ctx->pushScope(nullptr, variables);
ON_SCOPE_EXIT {
ctx->popScope();
};
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++) {
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;
};
}

View File

@ -0,0 +1,25 @@
#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;
};
}

View File

@ -0,0 +1,208 @@
#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 { \
LogConsole::abortEvaluation("invalid floating point operation", this); \
return 0; \
} \
auto name(auto left, hex::floating_point auto right) const { \
LogConsole::abortEvaluation("invalid floating point operation", this); \
return 0; \
} \
auto name(hex::floating_point auto left, hex::floating_point auto right) const { \
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) {
return ~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 left, const std::shared_ptr<Pattern> &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
[this](i128 left, const std::shared_ptr<Pattern> &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
[this](double left, const std::shared_ptr<Pattern> &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
[this](char left, const std::shared_ptr<Pattern> &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
[this](bool left, const std::shared_ptr<Pattern> &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
[this](const std::string &left, const std::shared_ptr<Pattern> &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
[this](const std::shared_ptr<Pattern> &left, u128 right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
[this](const std::shared_ptr<Pattern> &left, i128 right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
[this](const std::shared_ptr<Pattern> &left, double right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
[this](const std::shared_ptr<Pattern> &left, char right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
[this](const std::shared_ptr<Pattern> &left, bool right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
[this](const std::shared_ptr<Pattern> &left, const std::string &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
[this](const std::shared_ptr<Pattern> &left, const std::shared_ptr<Pattern> &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
[this](auto &&left, const std::string &right) -> 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:
{
std::string result;
for (auto i = 0; i < 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 == right));
case Token::Operator::BoolNotEquals:
return new ASTNodeLiteral(bool(left != right));
case Token::Operator::BoolGreaterThan:
return new ASTNodeLiteral(bool(left > right));
case Token::Operator::BoolLessThan:
return new ASTNodeLiteral(bool(left < right));
case Token::Operator::BoolGreaterThanOrEquals:
return new ASTNodeLiteral(bool(left >= right));
case Token::Operator::BoolLessThanOrEquals:
return new ASTNodeLiteral(bool(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
}

View File

@ -0,0 +1,52 @@
#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;
};
}

View File

@ -0,0 +1,23 @@
#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;
};
}

View File

@ -0,0 +1,95 @@
#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<ASTNode> &&type, std::shared_ptr<ASTNode> &&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->clone();
this->m_sizeType = other.m_sizeType->clone();
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<ASTNode> &getType() const { return this->m_type; }
[[nodiscard]] constexpr const std::shared_ptr<ASTNode> &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<ASTNode> m_type;
std::shared_ptr<ASTNode> m_sizeType;
std::unique_ptr<ASTNode> m_placementOffset;
};
}

View File

@ -0,0 +1,293 @@
#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 &&assignmentValue) { 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->clone();
}
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 (-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](const std::shared_ptr<Pattern> &) { LogConsole::abortEvaluation("cannot use custom type to index array", this); },
[&, this](auto &&index) {
if (auto dynamicArrayPattern = dynamic_cast<PatternArrayDynamic *>(currPattern.get())) {
if (index >= searchScope.size() || index < 0)
LogConsole::abortEvaluation("array index out of bounds", this);
currPattern = searchScope[index]->clone();
} else if (auto staticArrayPattern = dynamic_cast<PatternArrayStatic *>(currPattern.get())) {
if (index >= staticArrayPattern->getEntryCount() || 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();
}
std::shared_ptr<Pattern> indexPattern;
if (currPattern->isLocal()) {
auto stackLiteral = evaluator->getStack()[currPattern->getOffset()];
if (auto stackPattern = std::get_if<std::shared_ptr<Pattern>>(&stackLiteral); stackPattern != nullptr)
indexPattern = *stackPattern;
else
return hex::moveToVector<std::unique_ptr<Pattern>>(std::move(currPattern));
} else
indexPattern = currPattern->clone();
if (auto structPattern = dynamic_cast<PatternStruct *>(indexPattern.get()))
searchScope = structPattern->getMembers();
else if (auto unionPattern = dynamic_cast<PatternUnion *>(indexPattern.get()))
searchScope = unionPattern->getMembers();
else if (auto bitfieldPattern = dynamic_cast<PatternBitfield *>(indexPattern.get()))
searchScope = bitfieldPattern->getFields();
else if (auto dynamicArrayPattern = dynamic_cast<PatternArrayDynamic *>(indexPattern.get()))
searchScope = dynamicArrayPattern->getEntries();
else if (auto staticArrayPattern = dynamic_cast<PatternArrayStatic *>(indexPattern.get()))
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;
},
[&](std::shared_ptr<Pattern> &assignmentValue) { readVariable(evaluator, value, assignmentValue.get()); },
[&](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());
}
};
}

View File

@ -0,0 +1,40 @@
#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;
};
}

View File

@ -0,0 +1,73 @@
#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;
};
}

View File

@ -0,0 +1,58 @@
#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 &&second, auto &&third) -> 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;
};
}

View File

@ -0,0 +1,75 @@
#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:
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->clone();
this->m_endian = other.m_endian;
}
[[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() { 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->m_type->evaluate(evaluator);
if (auto attributable = dynamic_cast<Attributable *>(type.get())) {
for (auto &attribute : this->getAttributes()) {
if (auto node = dynamic_cast<ASTNodeAttribute *>(attribute.get()))
attributable->addAttribute(std::unique_ptr<ASTNodeAttribute>(node));
}
}
return type;
}
[[nodiscard]] std::vector<std::unique_ptr<Pattern>> createPatterns(Evaluator *evaluator) const override {
auto patterns = this->m_type->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->m_type.get()); attributable != nullptr) {
attributable->addAttribute(std::unique_ptr<ASTNodeAttribute>(static_cast<ASTNodeAttribute *>(attribute->clone().release())));
}
Attributable::addAttribute(std::move(attribute));
}
private:
std::string m_name;
std::shared_ptr<ASTNode> m_type;
std::optional<std::endian> m_endian;
};
}

View File

@ -0,0 +1,49 @@
#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;
};
}

View File

@ -0,0 +1,61 @@
#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; }
void addMember(std::shared_ptr<ASTNode> &&node) { this->m_members.push_back(std::move(node)); }
private:
std::vector<std::shared_ptr<ASTNode>> m_members;
};
}

View File

@ -0,0 +1,80 @@
#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>
namespace hex::pl {
class ASTNodeVariableDecl : public ASTNode,
public Attributable {
public:
ASTNodeVariableDecl(std::string name, std::unique_ptr<ASTNode> &&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->clone();
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::unique_ptr<ASTNode> &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](const std::shared_ptr<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::unique_ptr<ASTNode> m_type;
std::unique_ptr<ASTNode> m_placementOffset;
bool m_inVariable = false, m_outVariable = false;
};
}

View File

@ -0,0 +1,107 @@
#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;
};
}

File diff suppressed because it is too large Load Diff

View File

@ -34,7 +34,7 @@ namespace hex::pl {
Return
};
class PatternData;
class Pattern;
class PatternCreationLimiter;
class ASTNode;
@ -42,7 +42,7 @@ namespace hex::pl {
public:
Evaluator() = default;
std::optional<std::vector<PatternData *>> evaluate(const std::vector<ASTNode *> &ast);
std::optional<std::vector<std::shared_ptr<Pattern>>> evaluate(const std::vector<std::shared_ptr<ASTNode>> &ast);
[[nodiscard]] LogConsole &getConsole() {
return this->m_console;
@ -54,11 +54,11 @@ namespace hex::pl {
};
struct Scope {
PatternData *parent;
std::vector<PatternData *> *scope;
Pattern *parent;
std::vector<std::shared_ptr<Pattern>> *scope;
std::optional<ParameterPack> parameterPack;
};
void pushScope(PatternData *parent, std::vector<PatternData *> &scope) {
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()));
@ -197,9 +197,10 @@ namespace hex::pl {
}
[[nodiscard]] std::optional<Token::Literal> getEnvVariable(const std::string &name) const {
if (this->m_envVariables.contains(name))
if (this->m_envVariables.contains(name)) {
auto value = this->m_envVariables.at(name);
return this->m_envVariables.at(name);
else
} else
return std::nullopt;
}
@ -232,7 +233,7 @@ namespace hex::pl {
return this->m_currControlFlowStatement;
}
[[nodiscard]] std::optional<Token::Literal> getMainResult() {
[[nodiscard]] const std::optional<Token::Literal> &getMainResult() {
return this->m_mainResult;
}
@ -257,7 +258,7 @@ namespace hex::pl {
std::vector<Scope> m_scopes;
std::map<std::string, ContentRegistry::PatternLanguage::Function> m_customFunctions;
std::vector<ASTNode *> m_customFunctionDefinitions;
std::vector<std::unique_ptr<ASTNode>> m_customFunctionDefinitions;
std::vector<Token::Literal> m_stack;
std::optional<Token::Literal> m_mainResult;

View File

@ -8,6 +8,8 @@
#include <utility>
#include <vector>
#include <hex/helpers/concepts.hpp>
#include <hex/pattern_language/error.hpp>
namespace hex::pl {
@ -26,11 +28,25 @@ namespace hex::pl {
[[nodiscard]] const auto &getLog() const { return this->m_consoleLog; }
void log(Level level, const std::string &message);
void log(Level level, const std::string &message) {
this->m_consoleLog.emplace_back(level, message);
}
[[noreturn]] static void abortEvaluation(const std::string &message, const ASTNode *node = nullptr);
[[noreturn]] static void abortEvaluation(const std::string &message) {
abortEvaluation(message, nullptr);
}
void clear();
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; }

View File

@ -5,7 +5,11 @@
#include <hex/pattern_language/error.hpp>
#include <hex/pattern_language/token.hpp>
#include <hex/pattern_language/ast_node.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>
@ -21,7 +25,7 @@ namespace hex::pl {
Parser() = default;
~Parser() = default;
std::optional<std::vector<ASTNode *>> parse(const std::vector<Token> &tokens);
std::optional<std::vector<std::shared_ptr<ASTNode>>> parse(const std::vector<Token> &tokens);
const std::optional<PatternLanguageError> &getError() { return this->m_error; }
private:
@ -29,7 +33,7 @@ namespace hex::pl {
TokenIter m_curr;
TokenIter m_originalPosition, m_partOriginalPosition;
std::unordered_map<std::string, ASTNode *> m_types;
std::unordered_map<std::string, std::shared_ptr<ASTNode>> m_types;
std::vector<TokenIter> m_matchedOptionals;
std::vector<std::vector<std::string>> m_currNamespace;
@ -37,9 +41,10 @@ namespace hex::pl {
return this->m_curr[index].lineNumber;
}
auto *create(auto *node) {
template<typename T>
std::unique_ptr<T> create(T *node) {
node->setLineNumber(this->getLineNumber(-1));
return node;
return std::unique_ptr<T>(node);
}
template<typename T>
@ -67,79 +72,73 @@ namespace hex::pl {
return result;
}
ASTNode *parseFunctionCall();
ASTNode *parseStringLiteral();
std::unique_ptr<ASTNode> parseFunctionCall();
std::unique_ptr<ASTNode> parseStringLiteral();
std::string parseNamespaceResolution();
ASTNode *parseScopeResolution();
ASTNode *parseRValue();
ASTNode *parseRValue(ASTNodeRValue::Path &path);
ASTNode *parseFactor();
ASTNode *parseCastExpression();
ASTNode *parseUnaryExpression();
ASTNode *parseMultiplicativeExpression();
ASTNode *parseAdditiveExpression();
ASTNode *parseShiftExpression();
ASTNode *parseBinaryAndExpression();
ASTNode *parseBinaryXorExpression();
ASTNode *parseBinaryOrExpression();
ASTNode *parseBooleanAnd();
ASTNode *parseBooleanXor();
ASTNode *parseBooleanOr();
ASTNode *parseRelationExpression();
ASTNode *parseEqualityExpression();
ASTNode *parseTernaryConditional();
ASTNode *parseMathematicalExpression();
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();
ASTNode *parseFunctionDefinition();
ASTNode *parseFunctionVariableDecl();
ASTNode *parseFunctionStatement();
ASTNode *parseFunctionVariableAssignment(const std::string &lvalue);
ASTNode *parseFunctionVariableCompoundAssignment(const std::string &lvalue);
ASTNode *parseFunctionControlFlowStatement();
std::vector<ASTNode *> parseStatementBody();
ASTNode *parseFunctionConditional();
ASTNode *parseFunctionWhileLoop();
ASTNode *parseFunctionForLoop();
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);
ASTNode *parseConditional();
ASTNode *parseWhileStatement();
ASTNodeTypeDecl *parseType(bool allowFunctionTypes = false);
ASTNode *parseUsingDeclaration();
ASTNode *parsePadding();
ASTNode *parseMemberVariable(ASTNodeTypeDecl *type);
ASTNode *parseMemberArrayVariable(ASTNodeTypeDecl *type);
ASTNode *parseMemberPointerVariable(ASTNodeTypeDecl *type);
ASTNode *parseMember();
ASTNode *parseStruct();
ASTNode *parseUnion();
ASTNode *parseEnum();
ASTNode *parseBitfield();
ASTNode *parseVariablePlacement(ASTNodeTypeDecl *type);
ASTNode *parseArrayVariablePlacement(ASTNodeTypeDecl *type);
ASTNode *parsePointerVariablePlacement(ASTNodeTypeDecl *type);
ASTNode *parsePlacement();
std::vector<ASTNode *> parseNamespace();
std::vector<ASTNode *> parseStatements();
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(std::unique_ptr<ASTNodeTypeDecl> &&type);
std::unique_ptr<ASTNode> parseMemberArrayVariable(std::unique_ptr<ASTNodeTypeDecl> &&type);
std::unique_ptr<ASTNode> parseMemberPointerVariable(std::unique_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();
std::unique_ptr<ASTNode> parseVariablePlacement(std::unique_ptr<ASTNodeTypeDecl> &&type);
std::unique_ptr<ASTNode> parseArrayVariablePlacement(std::unique_ptr<ASTNodeTypeDecl> &&type);
std::unique_ptr<ASTNode> parsePointerVariablePlacement(std::unique_ptr<ASTNodeTypeDecl> &&type);
std::unique_ptr<ASTNode> parsePlacement();
std::vector<std::shared_ptr<ASTNode>> parseNamespace();
std::vector<std::shared_ptr<ASTNode>> parseStatements();
ASTNodeTypeDecl *addType(const std::string &name, ASTNode *node, std::optional<std::endian> endian = std::nullopt);
std::shared_ptr<ASTNodeTypeDecl> addType(const std::string &name, std::unique_ptr<ASTNode> &&node, std::optional<std::endian> endian = std::nullopt);
std::vector<ASTNode *> parseTillToken(Token::Type endTokenType, const auto value) {
std::vector<ASTNode *> program;
auto guard = SCOPE_GUARD {
for (auto &node : program)
delete node;
};
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(statement);
for (auto &statement : parseStatements())
program.push_back(std::move(statement));
}
this->m_curr++;
guard.release();
return program;
}

File diff suppressed because it is too large Load Diff

View File

@ -11,6 +11,7 @@
#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;
@ -23,7 +24,7 @@ namespace hex::pl {
class Parser;
class Validator;
class Evaluator;
class PatternData;
class Pattern;
class ASTNode;
@ -32,11 +33,11 @@ namespace hex::pl {
PatternLanguage();
~PatternLanguage();
[[nodiscard]] std::optional<std::vector<ASTNode *>> parseString(const std::string &code);
[[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 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<ASTNode *> &getCurrentAST() const;
[[nodiscard]] const std::vector<std::shared_ptr<ASTNode>> &getCurrentAST() const;
void abort();
@ -50,8 +51,8 @@ namespace hex::pl {
[[nodiscard]] bool hasDangerousFunctionBeenCalled() const;
void allowDangerousFunctions(bool allow);
[[nodiscard]] const std::vector<PatternData *> &getPatterns() {
const static std::vector<PatternData *> empty;
[[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;
@ -67,11 +68,11 @@ namespace hex::pl {
Validator *m_validator;
Evaluator *m_evaluator;
std::vector<ASTNode *> m_currAST;
std::vector<std::shared_ptr<ASTNode>> m_currAST;
std::optional<PatternLanguageError> m_currError;
std::vector<PatternData *> m_patterns;
std::vector<std::shared_ptr<Pattern>> m_patterns;
bool m_running = false;
};

View File

@ -0,0 +1,293 @@
#pragma once
#include <hex.hpp>
#include <imgui.h>
#include <hex/ui/imgui_imhex_extensions.h>
#include <hex/pattern_language/evaluator.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]] const 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;
}
[[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; }
virtual void createEntry(prv::Provider *&provider) = 0;
[[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) { }
[[nodiscard]] virtual std::string toString(prv::Provider *provider) const {
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 draw(prv::Provider *provider) {
if (isHidden()) return;
this->createEntry(provider);
}
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 formatDisplayValue(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();
}
}
}
protected:
void createDefaultEntry(const std::string &value, const Token::Literal &literal) const {
ImGui::TableNextRow();
ImGui::TreeNodeEx(this->getDisplayName().c_str(), ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_AllowItemOverlap);
ImGui::TableNextColumn();
ImGui::PushID(this->getOffset());
ImGui::PushID(this->getVariableName().c_str());
if (ImGui::Selectable("##PatternLine", false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowItemOverlap)) {
ImHexApi::HexEditor::setSelection(this->getOffset(), this->getSize());
}
ImGui::PopID();
ImGui::PopID();
this->drawCommentTooltip();
ImGui::SameLine();
ImGui::TextUnformatted(this->getDisplayName().c_str());
ImGui::TableNextColumn();
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight()));
ImGui::TableNextColumn();
ImGui::TextFormatted("0x{0:08X} : 0x{1:08X}", this->getOffset(), this->getOffset() + this->getSize() - 1);
ImGui::TableNextColumn();
ImGui::TextFormatted("0x{0:04X}", this->getSize());
ImGui::TableNextColumn();
ImGui::TextFormattedColored(ImColor(0xFF9BC64D), "{}", this->getFormattedName());
ImGui::TableNextColumn();
ImGui::TextFormatted("{}", formatDisplayValue(value, literal));
}
void drawCommentTooltip() const {
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) && this->getComment().has_value()) {
ImGui::BeginTooltip();
ImGui::TextUnformatted(this->getComment()->c_str());
ImGui::EndTooltip();
}
}
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;
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;
};
}

View File

@ -0,0 +1,159 @@
#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 createEntry(prv::Provider *&provider) override {
if (this->m_entries.empty())
return;
bool open = true;
if (!this->isInlined()) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
open = ImGui::TreeNodeEx(this->getDisplayName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
ImGui::TableNextColumn();
if (ImGui::Selectable(("##PatternLine"s + std::to_string(u64(this))).c_str(), false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowItemOverlap)) {
ImHexApi::HexEditor::setSelection(this->getOffset(), this->getSize());
}
this->drawCommentTooltip();
ImGui::TableNextColumn();
ImGui::TextFormatted("0x{0:08X} : 0x{1:08X}", this->getOffset(), this->getOffset() + this->getSize() - 1);
ImGui::TableNextColumn();
ImGui::TextFormatted("0x{0:04X}", this->getSize());
ImGui::TableNextColumn();
ImGui::TextFormattedColored(ImColor(0xFF9BC64D), "{0}", this->m_entries[0]->getTypeName());
ImGui::SameLine(0, 0);
ImGui::TextUnformatted("[");
ImGui::SameLine(0, 0);
ImGui::TextFormattedColored(ImColor(0xFF00FF00), "{0}", this->m_entries.size());
ImGui::SameLine(0, 0);
ImGui::TextUnformatted("]");
ImGui::TableNextColumn();
ImGui::TextFormatted("{}", this->formatDisplayValue("{ ... }", this->clone()));
}
if (open) {
for (u64 i = 0; i < this->m_entries.size(); i++) {
this->m_entries[i]->draw(provider);
if (i >= (this->m_displayEnd - 1)) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Selectable("... (Double-click to see more items)", false, ImGuiSelectableFlags_SpanAllColumns);
if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
this->m_displayEnd += 50;
break;
}
}
if (!this->isInlined())
ImGui::TreePop();
} else {
this->m_displayEnd = 50;
}
}
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()) + "]";
}
void setOffset(u64 offset) override {
for (auto &entry : this->m_entries)
entry->setOffset(entry->getOffset() - this->getOffset() + offset);
Pattern::setOffset(offset);
}
[[nodiscard]] const auto &getEntries() {
return this->m_entries;
}
void setEntries(std::vector<std::shared_ptr<Pattern>> &&entries) {
this->m_entries = std::move(entries);
if (this->hasOverriddenColor()) {
for (auto &entry : this->m_entries) {
entry->setColor(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);
}
private:
std::vector<std::shared_ptr<Pattern>> m_entries;
u64 m_displayEnd = 50;
};
}

View File

@ -0,0 +1,162 @@
#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 createEntry(prv::Provider *&provider) override {
if (this->getEntryCount() == 0)
return;
bool open = true;
if (!this->isInlined()) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
open = ImGui::TreeNodeEx(this->getDisplayName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
ImGui::TableNextColumn();
if (ImGui::Selectable(("##PatternLine"s + std::to_string(u64(this))).c_str(), false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowItemOverlap)) {
ImHexApi::HexEditor::setSelection(this->getOffset(), this->getSize());
}
this->drawCommentTooltip();
ImGui::TableNextColumn();
ImGui::TextFormatted("0x{0:08X} : 0x{1:08X}", this->getOffset(), this->getOffset() + this->getSize() - 1);
ImGui::TableNextColumn();
ImGui::TextFormatted("0x{0:04X}", this->getSize());
ImGui::TableNextColumn();
ImGui::TextFormattedColored(ImColor(0xFF9BC64D), "{0}", this->m_template->getTypeName().c_str());
ImGui::SameLine(0, 0);
ImGui::TextUnformatted("[");
ImGui::SameLine(0, 0);
ImGui::TextFormattedColored(ImColor(0xFF00FF00), "{0}", this->m_entryCount);
ImGui::SameLine(0, 0);
ImGui::TextUnformatted("]");
ImGui::TableNextColumn();
ImGui::TextFormatted("{}", this->formatDisplayValue("{ ... }", this->clone()));
}
if (open) {
auto entry = this->m_template->clone();
for (u64 index = 0; index < this->m_entryCount; index++) {
entry->setVariableName(hex::format("[{0}]", index));
entry->setOffset(this->getOffset() + index * this->m_template->getSize());
entry->draw(provider);
if (index >= (this->m_displayEnd - 1)) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Selectable("... (Double-click to see more items)", false, ImGuiSelectableFlags_SpanAllColumns);
if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
this->m_displayEnd += 50;
break;
}
}
if (!this->isInlined())
ImGui::TreePop();
} else {
this->m_displayEnd = 50;
}
}
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]] 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;
if (this->hasOverriddenColor())
this->setColor(this->m_template->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->setColor(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);
}
private:
std::shared_ptr<Pattern> m_template = nullptr;
mutable std::unique_ptr<Pattern> m_highlightTemplate = nullptr;
size_t m_entryCount = 0;
u64 m_displayEnd = 50;
};
}

View File

@ -0,0 +1,186 @@
#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));
}
void createEntry(prv::Provider *&provider) override {
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());
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextUnformatted(this->getDisplayName().c_str());
ImGui::TableNextColumn();
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight()));
ImGui::TableNextColumn();
if (this->m_bitSize == 1)
ImGui::TextFormatted("0x{0:08X} bit {1}", this->getOffset() + this->m_bitOffset / 8, (this->m_bitOffset + (this->m_bitSize - 1)) % 8);
else
ImGui::TextFormatted("0x{0:08X} bits {1} - {2}", this->getOffset() + this->m_bitOffset / 8, this->m_bitOffset % 8, this->m_bitOffset % 8 + (this->m_bitSize - 1) % 8);
ImGui::TableNextColumn();
if (this->m_bitSize == 1)
ImGui::TextFormatted("{0} bit", this->m_bitSize);
else
ImGui::TextFormatted("{0} bits", this->m_bitSize);
ImGui::TableNextColumn();
ImGui::TextFormattedColored(ImColor(0xFF9BC64D), "bits");
ImGui::TableNextColumn();
{
u8 numBytes = (this->m_bitSize / 8) + 1;
u64 extractedValue = hex::extract(this->m_bitOffset + (this->m_bitSize - 1), this->m_bitOffset, value);
ImGui::TextFormatted("{}", this->formatDisplayValue(hex::format("{0} (0x{1:X})", extractedValue, extractedValue), this->clone()));
}
}
[[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;
}
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));
}
void createEntry(prv::Provider *&provider) override {
std::vector<u8> value(this->getSize(), 0);
provider->read(this->getOffset(), &value[0], value.size());
if (this->m_endian == std::endian::little)
std::reverse(value.begin(), value.end());
bool open = true;
if (!this->isInlined()) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
open = ImGui::TreeNodeEx(this->getDisplayName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
ImGui::TableNextColumn();
if (ImGui::Selectable(("##PatternLine"s + std::to_string(u64(this))).c_str(), false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowItemOverlap)) {
ImHexApi::HexEditor::setSelection(this->getOffset(), this->getSize());
}
this->drawCommentTooltip();
ImGui::TableNextColumn();
ImGui::TextFormatted("0x{0:08X} : 0x{1:08X}", this->getOffset(), this->getOffset() + this->getSize() - 1);
ImGui::TableNextColumn();
ImGui::TextFormatted("0x{0:04X}", this->getSize());
ImGui::TableNextColumn();
ImGui::TextFormattedColored(ImColor(0xFFD69C56), "bitfield");
ImGui::SameLine();
ImGui::TextUnformatted(Pattern::getTypeName().c_str());
ImGui::TableNextColumn();
std::string valueString = "{ ";
for (auto i : value)
valueString += hex::format("{0:02X} ", i);
valueString += "}";
ImGui::TextFormatted("{}", this->formatDisplayValue(valueString, this->clone()));
}
if (open) {
for (auto &field : this->m_fields)
field->draw(provider);
if (!this->isInlined())
ImGui::TreePop();
}
}
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;
}
private:
std::vector<std::shared_ptr<Pattern>> m_fields;
};
}

View File

@ -0,0 +1,35 @@
#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));
}
void createEntry(prv::Provider *&provider) override {
u8 boolean;
provider->read(this->getOffset(), &boolean, 1);
if (boolean == 0)
this->createDefaultEntry("false", false);
else if (boolean == 1)
this->createDefaultEntry("true", true);
else
this->createDefaultEntry("true*", true);
}
[[nodiscard]] std::string getFormattedName() const override {
return "bool";
}
[[nodiscard]] bool operator==(const Pattern &other) const override { return areCommonPropertiesEqual<decltype(*this)>(other); }
};
}

View File

@ -0,0 +1,30 @@
#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));
}
void createEntry(prv::Provider *&provider) override {
char character;
provider->read(this->getOffset(), &character, 1);
this->createDefaultEntry(hex::format("'{0}'", character), character);
}
[[nodiscard]] std::string getFormattedName() const override {
return "char";
}
[[nodiscard]] bool operator==(const Pattern &other) const override { return areCommonPropertiesEqual<decltype(*this)>(other); }
};
}

View File

@ -0,0 +1,101 @@
#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));
}
void createEntry(prv::Provider *&provider) override {
u64 value = 0;
provider->read(this->getOffset(), &value, this->getSize());
value = hex::changeEndianess(value, this->getSize(), this->getEndian());
std::string valueString = Pattern::getTypeName() + "::";
bool foundValue = false;
for (auto &[entryValueLiteral, entryName] : this->m_enumValues) {
bool matches = std::visit(overloaded {
[&, name = entryName](auto &&entryValue) {
if (value == entryValue) {
valueString += name;
foundValue = true;
return true;
}
return false;
},
[](std::string &) { return false; },
[](std::shared_ptr<Pattern> &) { return false; } },
entryValueLiteral);
if (matches)
break;
}
if (!foundValue)
valueString += "???";
ImGui::TableNextRow();
ImGui::TreeNodeEx(this->getDisplayName().c_str(), ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_AllowItemOverlap);
this->drawCommentTooltip();
ImGui::TableNextColumn();
if (ImGui::Selectable(("##PatternLine"s + std::to_string(u64(this))).c_str(), false, ImGuiSelectableFlags_SpanAllColumns)) {
ImHexApi::HexEditor::setSelection(this->getOffset(), this->getSize());
}
ImGui::SameLine();
ImGui::TextUnformatted(this->getDisplayName().c_str());
ImGui::TableNextColumn();
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight()));
ImGui::TableNextColumn();
ImGui::TextFormatted("0x{0:08X} : 0x{1:08X}", this->getOffset(), this->getOffset() + this->getSize() - 1);
ImGui::TableNextColumn();
ImGui::TextFormatted("0x{0:04X}", this->getSize());
ImGui::TableNextColumn();
ImGui::TextFormattedColored(ImColor(0xFFD69C56), "enum");
ImGui::SameLine();
ImGui::TextUnformatted(Pattern::getTypeName().c_str());
ImGui::TableNextColumn();
ImGui::TextFormatted("{}", this->formatDisplayValue(hex::format("{} (0x{:0{}X})", valueString.c_str(), value, this->getSize() * 2), this->clone()));
}
[[nodiscard]] std::string getFormattedName() const override {
return "enum " + Pattern::getTypeName();
}
[[nodiscard]] const auto &getEnumValues() const {
return this->m_enumValues;
}
void setEnumValues(const std::vector<std::pair<Token::Literal, std::string>> &enumValues) {
this->m_enumValues = 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;
}
private:
std::vector<std::pair<Token::Literal, std::string>> m_enumValues;
};
}

View File

@ -0,0 +1,46 @@
#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));
}
void createEntry(prv::Provider *&provider) override {
if (this->getSize() == 4) {
u32 data = 0;
provider->read(this->getOffset(), &data, 4);
data = hex::changeEndianess(data, 4, this->getEndian());
this->createDefaultEntry(hex::format("{:e} (0x{:0{}X})", *reinterpret_cast<float *>(&data), data, this->getSize() * 2), *reinterpret_cast<float *>(&data));
} else if (this->getSize() == 8) {
u64 data = 0;
provider->read(this->getOffset(), &data, 8);
data = hex::changeEndianess(data, 8, this->getEndian());
this->createDefaultEntry(hex::format("{:e} (0x{:0{}X})", *reinterpret_cast<double *>(&data), data, this->getSize() * 2), *reinterpret_cast<double *>(&data));
}
}
[[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); }
};
}

View File

@ -0,0 +1,25 @@
#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));
}
void createEntry(prv::Provider *&provider) override {
}
[[nodiscard]] std::string getFormattedName() const override {
return "";
}
[[nodiscard]] bool operator==(const Pattern &other) const override { return areCommonPropertiesEqual<decltype(*this)>(other); }
};
}

View File

@ -0,0 +1,143 @@
#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));
}
void createEntry(prv::Provider *&provider) override {
u64 data = 0;
provider->read(this->getOffset(), &data, this->getSize());
data = hex::changeEndianess(data, this->getSize(), this->getEndian());
bool open = true;
if (!this->isInlined()) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
open = ImGui::TreeNodeEx(this->getDisplayName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
ImGui::TableNextColumn();
if (ImGui::Selectable(("##PatternLine"s + std::to_string(u64(this))).c_str(), false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowItemOverlap)) {
ImHexApi::HexEditor::setSelection(this->getOffset(), this->getSize());
}
this->drawCommentTooltip();
ImGui::SameLine(0, 0);
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight()));
ImGui::TableNextColumn();
ImGui::TextFormatted("0x{0:08X} : 0x{1:08X}", this->getOffset(), this->getOffset() + this->getSize() - 1);
ImGui::TableNextColumn();
ImGui::TextFormatted("0x{0:04X}", this->getSize());
ImGui::TableNextColumn();
ImGui::TextFormattedColored(ImColor(0xFF9BC64D), "{}", this->getFormattedName());
ImGui::TableNextColumn();
ImGui::TextFormatted("{}", formatDisplayValue(hex::format("*(0x{0:X})", data), u128(data)));
}
if (open) {
this->m_pointedAt->createEntry(provider);
if (!this->isInlined())
ImGui::TreePop();
}
}
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->m_pointedAt->getFormattedName() + "* : ";
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);
}
private:
std::unique_ptr<Pattern> m_pointedAt;
u64 m_pointedAtAddress = 0;
u64 m_pointerBase = 0;
};
}

View File

@ -0,0 +1,45 @@
#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));
}
void createEntry(prv::Provider *&provider) override {
i128 data = 0;
provider->read(this->getOffset(), &data, this->getSize());
data = hex::changeEndianess(data, this->getSize(), this->getEndian());
data = hex::signExtend(this->getSize() * 8, data);
this->createDefaultEntry(hex::format("{:d} (0x{:0{}X})", data, data, 1 * 2), 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); }
};
}

View File

@ -0,0 +1,47 @@
#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));
}
void createEntry(prv::Provider *&provider) override {
auto size = std::min<size_t>(this->getSize(), 0x7F);
if (size == 0)
return;
std::vector<u8> buffer(size, 0x00);
provider->read(this->getOffset(), buffer.data(), size);
auto displayString = hex::encodeByteString(buffer);
this->createDefaultEntry(hex::format("\"{0}\" {1}", displayString, size > this->getSize() ? "(truncated)" : ""), displayString);
}
[[nodiscard]] std::string getFormattedName() const override {
return "String";
}
[[nodiscard]] std::string toString(prv::Provider *provider) const override {
std::string buffer(this->getSize(), 0x00);
provider->read(this->getOffset(), buffer.data(), buffer.size());
std::erase_if(buffer, [](auto c) {
return c == 0x00;
});
return buffer;
}
[[nodiscard]] bool operator==(const Pattern &other) const override { return areCommonPropertiesEqual<decltype(*this)>(other); }
};
}

View File

@ -0,0 +1,156 @@
#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 createEntry(prv::Provider *&provider) override {
bool open = true;
if (!this->isInlined()) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
open = ImGui::TreeNodeEx(this->getDisplayName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
ImGui::TableNextColumn();
if (ImGui::Selectable(("##PatternLine"s + std::to_string(u64(this))).c_str(), false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowItemOverlap)) {
ImHexApi::HexEditor::setSelection(this->getOffset(), this->getSize());
}
this->drawCommentTooltip();
ImGui::TableNextColumn();
ImGui::TextFormatted("0x{0:08X} : 0x{1:08X}", this->getOffset(), this->getOffset() + this->getSize() - (this->getSize() == 0 ? 0 : 1));
ImGui::TableNextColumn();
ImGui::TextFormatted("0x{0:04X}", this->getSize());
ImGui::TableNextColumn();
ImGui::TextFormattedColored(ImColor(0xFFD69C56), "struct");
ImGui::SameLine();
ImGui::TextUnformatted(this->getTypeName().c_str());
ImGui::TableNextColumn();
ImGui::TextFormatted("{}", this->formatDisplayValue("{ ... }", this->clone()));
}
if (open) {
for (auto &member : this->m_sortedMembers)
member->draw(provider);
if (!this->isInlined())
ImGui::TreePop();
}
}
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 "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;
auto iter = std::find_if(this->m_members.begin(), this->m_members.end(), [offset](const std::shared_ptr<Pattern> &pattern) {
return offset >= pattern->getOffset() && offset < (pattern->getOffset() + pattern->getSize());
});
if (iter == this->m_members.end())
return nullptr;
else
return (*iter)->getPattern(offset);
}
void setEndian(std::endian endian) override {
for (auto &member : this->m_members) {
if (!member->hasOverriddenEndian())
member->setEndian(endian);
}
Pattern::setEndian(endian);
}
private:
std::vector<std::shared_ptr<Pattern>> m_members;
std::vector<Pattern *> m_sortedMembers;
};
}

View File

@ -0,0 +1,158 @@
#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 createEntry(prv::Provider *&provider) override {
bool open = true;
if (!this->isInlined()) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
open = ImGui::TreeNodeEx(this->getDisplayName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
ImGui::TableNextColumn();
if (ImGui::Selectable(("##PatternLine"s + std::to_string(u64(this))).c_str(), false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowItemOverlap)) {
ImHexApi::HexEditor::setSelection(this->getOffset(), this->getSize());
}
this->drawCommentTooltip();
ImGui::TableNextColumn();
ImGui::TextFormatted("0x{0:08X} : 0x{1:08X}", this->getOffset(), std::max(this->getOffset() + this->getSize() - (this->getSize() == 0 ? 0 : 1), u64(0)));
ImGui::TableNextColumn();
ImGui::TextFormatted("0x{0:04X}", this->getSize());
ImGui::TableNextColumn();
ImGui::TextFormattedColored(ImColor(0xFFD69C56), "union");
ImGui::SameLine();
ImGui::TextUnformatted(Pattern::getTypeName().c_str());
ImGui::TableNextColumn();
ImGui::TextFormatted("{}", this->formatDisplayValue("{ ... }", this->clone()));
}
if (open) {
for (auto &member : this->m_sortedMembers)
member->draw(provider);
if (!this->isInlined())
ImGui::TreePop();
}
}
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);
}
private:
std::vector<std::shared_ptr<Pattern>> m_members;
std::vector<Pattern *> m_sortedMembers;
};
}

View File

@ -0,0 +1,44 @@
#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));
}
void createEntry(prv::Provider *&provider) override {
u128 data = 0;
provider->read(this->getOffset(), &data, this->getSize());
data = hex::changeEndianess(data, this->getSize(), this->getEndian());
this->createDefaultEntry(hex::format("{:d} (0x{:0{}X})", data, data, this->getSize() * 2), data);
}
[[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); }
};
}

View File

@ -0,0 +1,42 @@
#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));
}
void createEntry(prv::Provider *&provider) override {
char16_t character;
provider->read(this->getOffset(), &character, 2);
character = hex::changeEndianess(character, this->getEndian());
u128 literal = character;
this->createDefaultEntry(hex::format("'{0}'", std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> {}.to_bytes(character)), literal);
}
[[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); }
};
}

View File

@ -0,0 +1,61 @@
#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));
}
void createEntry(prv::Provider *&provider) override {
auto size = std::min<size_t>(this->getSize(), 0x100);
if (size == 0)
return;
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());
buffer.erase(std::remove_if(buffer.begin(), buffer.end(), [](auto c) {
return c == 0x00;
}),
buffer.end());
auto utf8String = std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> {}.to_bytes(buffer);
this->createDefaultEntry(hex::format("\"{0}\" {1}", utf8String, size > this->getSize() ? "(truncated)" : ""), utf8String);
}
[[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); }
};
}

View File

@ -11,11 +11,13 @@
namespace hex::pl {
class PatternData;
class ASTNode;
class Pattern;
class Token {
public:
enum class Type : u64 {
enum class Type : u64
{
Keyword,
ValueType,
Operator,
@ -25,7 +27,8 @@ namespace hex::pl {
Separator
};
enum class Keyword {
enum class Keyword
{
Struct,
Union,
Using,
@ -48,7 +51,8 @@ namespace hex::pl {
Continue
};
enum class Operator {
enum class Operator
{
AtDeclaration,
Assignment,
Inherit,
@ -80,7 +84,8 @@ namespace hex::pl {
ScopeResolution
};
enum class ValueType {
enum class ValueType
{
Unsigned8Bit = 0x10,
Signed8Bit = 0x11,
Unsigned16Bit = 0x20,
@ -108,7 +113,8 @@ namespace hex::pl {
Any = 0xFFFF
};
enum class Separator {
enum class Separator
{
RoundBracketOpen,
RoundBracketClose,
CurlyBracketOpen,
@ -133,7 +139,7 @@ namespace hex::pl {
std::string m_identifier;
};
using Literal = std::variant<char, bool, u128, i128, double, std::string, PatternData *>;
using Literal = std::variant<char, bool, u128, i128, double, std::string, std::shared_ptr<Pattern>>;
using ValueTypes = std::variant<Keyword, Identifier, Operator, Literal, ValueType, Separator>;
Token(Type type, auto value, u32 lineNumber) : type(type), value(value), lineNumber(lineNumber) {
@ -158,7 +164,7 @@ namespace hex::pl {
static u128 literalToUnsigned(const pl::Token::Literal &literal) {
return std::visit(overloaded {
[](const std::string &) -> u128 { LogConsole::abortEvaluation("expected integral type, got string"); },
[](PatternData *) -> u128 { LogConsole::abortEvaluation("expected integral type, got custom type"); },
[](const std::shared_ptr<Pattern> &) -> u128 { LogConsole::abortEvaluation("expected integral type, got custom type"); },
[](auto &&result) -> u128 { return result; } },
literal);
}
@ -166,7 +172,7 @@ namespace hex::pl {
static i128 literalToSigned(const pl::Token::Literal &literal) {
return std::visit(overloaded {
[](const std::string &) -> i128 { LogConsole::abortEvaluation("expected integral type, got string"); },
[](PatternData *) -> i128 { LogConsole::abortEvaluation("expected integral type, got custom type"); },
[](const std::shared_ptr<Pattern> &) -> i128 { LogConsole::abortEvaluation("expected integral type, got custom type"); },
[](auto &&result) -> i128 { return result; } },
literal);
}
@ -174,7 +180,7 @@ namespace hex::pl {
static double literalToFloatingPoint(const pl::Token::Literal &literal) {
return std::visit(overloaded {
[](const std::string &) -> double { LogConsole::abortEvaluation("expected integral type, got string"); },
[](PatternData *) -> double { LogConsole::abortEvaluation("expected integral type, got custom type"); },
[](const std::shared_ptr<Pattern> &) -> double { LogConsole::abortEvaluation("expected integral type, got custom type"); },
[](auto &&result) -> double { return result; } },
literal);
}
@ -182,7 +188,7 @@ namespace hex::pl {
static bool literalToBoolean(const pl::Token::Literal &literal) {
return std::visit(overloaded {
[](const std::string &) -> bool { LogConsole::abortEvaluation("expected integral type, got string"); },
[](PatternData *) -> bool { LogConsole::abortEvaluation("expected integral type, got custom type"); },
[](const std::unique_ptr<Pattern> &) -> bool { LogConsole::abortEvaluation("expected integral type, got custom type"); },
[](auto &&result) -> bool { return result != 0; } },
literal);
}
@ -197,7 +203,7 @@ namespace hex::pl {
[](i128 result) -> std::string { return hex::to_string(result); },
[](bool result) -> std::string { return result ? "true" : "false"; },
[](char result) -> std::string { return { 1, result }; },
[](PatternData *) -> std::string { LogConsole::abortEvaluation("expected integral type, got custom type"); },
[](const std::shared_ptr<Pattern> &) -> std::string { LogConsole::abortEvaluation("expected integral type, got custom type"); },
[](auto &&result) -> std::string { return std::to_string(result); } },
literal);
}

View File

@ -2,6 +2,7 @@
#include <hex.hpp>
#include <memory>
#include <optional>
#include <string>
#include <vector>
@ -16,7 +17,7 @@ namespace hex::pl {
public:
Validator() = default;
bool validate(const std::vector<ASTNode *> &ast);
bool validate(const std::vector<std::shared_ptr<ASTNode>> &ast);
const std::optional<PatternLanguageError> &getError() { return this->m_error; }

View File

@ -9,7 +9,11 @@
#include <vector>
#include <hex/providers/overlay.hpp>
#include <hex/pattern_language/pattern_language.hpp>
#include <hex/helpers/paths.hpp>
namespace hex::pl {
class PatternLanguage;
}
namespace hex::prv {
@ -79,7 +83,7 @@ namespace hex::prv {
virtual void drawLoadInterface();
virtual void drawInterface();
pl::PatternLanguage &getPatternLanguageRuntime() { return this->m_patternLanguageRuntime; }
pl::PatternLanguage &getPatternLanguageRuntime() { return *this->m_patternLanguageRuntime; }
std::string &getPatternLanguageSourceCode() { return this->m_patternLanguageSourceCode; }
protected:
@ -90,7 +94,7 @@ namespace hex::prv {
std::list<std::map<u64, u8>> m_patches;
std::list<Overlay *> m_overlays;
pl::PatternLanguage m_patternLanguageRuntime;
std::unique_ptr<pl::PatternLanguage> m_patternLanguageRuntime;
std::string m_patternLanguageSourceCode;
};

View File

@ -13,6 +13,7 @@
#include <hex/api/imhex_api.hpp>
#include <hex/api/event.hpp>
#include <hex/providers/provider.hpp>
#include <hex/helpers/utils.hpp>
#include <hex/api/localization.hpp>

View File

@ -1,6 +1,19 @@
#include <hex/pattern_language/evaluator.hpp>
#include <hex/pattern_language/ast_node.hpp>
#include <hex/pattern_language/pattern_data.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>
namespace hex::pl {
@ -19,34 +32,37 @@ namespace hex::pl {
}
}
auto startOffset = this->dataOffset();
auto pattern = type == nullptr ? nullptr : type->createPatterns(this).front();
auto startOffset = this->dataOffset();
std::unique_ptr<Pattern> pattern;
this->dataOffset() = startOffset;
bool referenceType = false;
if (pattern == nullptr) {
if (type == nullptr) {
// 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 = new PatternDataUnsigned(this, 0, sizeof(u128));
pattern = std::unique_ptr<Pattern>(new PatternUnsigned(this, 0, sizeof(u128)));
else if (std::get_if<i128>(&value.value()) != nullptr)
pattern = new PatternDataSigned(this, 0, sizeof(i128));
pattern = std::unique_ptr<Pattern>(new PatternSigned(this, 0, sizeof(i128)));
else if (std::get_if<double>(&value.value()) != nullptr)
pattern = new PatternDataFloat(this, 0, sizeof(double));
pattern = std::unique_ptr<Pattern>(new PatternFloat(this, 0, sizeof(double)));
else if (std::get_if<bool>(&value.value()) != nullptr)
pattern = new PatternDataBoolean(this, 0);
pattern = std::unique_ptr<Pattern>(new PatternBoolean(this, 0));
else if (std::get_if<char>(&value.value()) != nullptr)
pattern = new PatternDataCharacter(this, 0);
pattern = std::unique_ptr<Pattern>(new PatternCharacter(this, 0));
else if (std::get_if<std::string>(&value.value()) != nullptr)
pattern = new PatternDataString(this, 0, 1);
else if (std::get_if<PatternData *>(&value.value()) != nullptr) {
pattern = std::get<PatternData *>(value.value())->clone();
pattern = std::unique_ptr<Pattern>(new PatternString(this, 0, 1));
else if (auto patternValue = std::get_if<std::shared_ptr<Pattern>>(&value.value()); patternValue != nullptr) {
pattern = (*patternValue)->clone();
referenceType = true;
} else
LogConsole::abortEvaluation("cannot determine type of auto variable", type);
} else {
pattern = std::move(type->createPatterns(this).front());
}
pattern->setVariableName(name);
@ -57,20 +73,20 @@ namespace hex::pl {
this->getStack().emplace_back();
}
variables.push_back(pattern);
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) {
PatternData *pattern = nullptr;
std::unique_ptr<Pattern> pattern = nullptr;
{
auto &variables = *this->getScope(0).scope;
for (auto &variable : variables) {
if (variable->getVariableName() == name) {
pattern = variable;
pattern = variable->clone();
break;
}
}
@ -83,7 +99,7 @@ namespace hex::pl {
if (!variable->isLocal())
LogConsole::abortEvaluation(hex::format("cannot modify global variable '{}' which has been placed in memory", name));
pattern = variable;
pattern = variable->clone();
break;
}
}
@ -96,37 +112,37 @@ namespace hex::pl {
Token::Literal castedLiteral = std::visit(overloaded {
[&](double &value) -> Token::Literal {
if (dynamic_cast<PatternDataUnsigned *>(pattern))
if (dynamic_cast<PatternUnsigned *>(pattern.get()))
return u128(value) & bitmask(pattern->getSize() * 8);
else if (dynamic_cast<PatternDataSigned *>(pattern))
else if (dynamic_cast<PatternSigned *>(pattern.get()))
return i128(value) & bitmask(pattern->getSize() * 8);
else if (dynamic_cast<PatternDataFloat *>(pattern))
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<PatternDataString *>(pattern))
if (dynamic_cast<PatternString *>(pattern.get()))
return value;
else
LogConsole::abortEvaluation(hex::format("cannot cast type 'string' to type '{}'", pattern->getTypeName()));
},
[&](PatternData *const &value) -> Token::Literal {
[&](const std::shared_ptr<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<PatternDataUnsigned *>(pattern) || dynamic_cast<PatternDataEnum *>(pattern))
if (dynamic_cast<PatternUnsigned *>(pattern.get()) || dynamic_cast<PatternEnum *>(pattern.get()))
return u128(value) & bitmask(pattern->getSize() * 8);
else if (dynamic_cast<PatternDataSigned *>(pattern))
else if (dynamic_cast<PatternSigned *>(pattern.get()))
return i128(value) & bitmask(pattern->getSize() * 8);
else if (dynamic_cast<PatternDataCharacter *>(pattern))
else if (dynamic_cast<PatternCharacter *>(pattern.get()))
return char(value);
else if (dynamic_cast<PatternDataBoolean *>(pattern))
else if (dynamic_cast<PatternBoolean *>(pattern.get()))
return bool(value);
else if (dynamic_cast<PatternDataFloat *>(pattern))
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()));
@ -136,7 +152,7 @@ namespace hex::pl {
this->getStack()[pattern->getOffset()] = castedLiteral;
}
std::optional<std::vector<PatternData *>> Evaluator::evaluate(const std::vector<ASTNode *> &ast) {
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();
@ -155,11 +171,9 @@ namespace hex::pl {
this->dataOffset() = 0x00;
this->m_currPatternCount = 0;
for (auto &func : this->m_customFunctionDefinitions)
delete func;
this->m_customFunctionDefinitions.clear();
std::vector<PatternData *> patterns;
std::vector<std::shared_ptr<Pattern>> patterns;
try {
this->setCurrentControlFlowStatement(ControlFlowStatement::None);
@ -168,33 +182,30 @@ namespace hex::pl {
popScope();
};
for (auto node : ast) {
if (dynamic_cast<ASTNodeTypeDecl *>(node)) {
for (auto &node : ast) {
if (dynamic_cast<ASTNodeTypeDecl *>(node.get())) {
; // Don't create patterns from type declarations
} else if (dynamic_cast<ASTNodeFunctionCall *>(node)) {
delete node->evaluate(this);
} else if (dynamic_cast<ASTNodeFunctionDefinition *>(node)) {
} 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)) {
auto pattern = node->createPatterns(this).front();
} 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);
if (varDeclNode->getPlacementOffset() == nullptr) {
auto type = varDeclNode->getType()->evaluate(this);
ON_SCOPE_EXIT { delete type; };
auto &name = pattern->getVariableName();
this->createVariable(name, type.get(), std::nullopt, varDeclNode->isOutVariable());
auto &name = pattern->getVariableName();
this->createVariable(name, type, std::nullopt, varDeclNode->isOutVariable());
if (varDeclNode->isInVariable() && this->m_inVariables.contains(name))
this->setVariable(name, this->m_inVariables[name]);
delete pattern;
} else {
patterns.push_back(pattern);
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::copy(newPatterns.begin(), newPatterns.end(), std::back_inserter(patterns));
std::move(newPatterns.begin(), newPatterns.end(), std::back_inserter(patterns));
}
}
@ -212,8 +223,6 @@ namespace hex::pl {
if (error.getLineNumber() != 0)
this->m_console.setHardError(error);
for (auto &pattern : patterns)
delete pattern;
patterns.clear();
this->m_currPatternCount = 0;
@ -222,7 +231,7 @@ namespace hex::pl {
}
// Remove global local variables
std::erase_if(patterns, [](PatternData *pattern) {
std::erase_if(patterns, [](const std::shared_ptr<Pattern> &pattern) {
return pattern->isLocal();
});

View File

@ -1,13 +1,9 @@
#include <hex/pattern_language/log_console.hpp>
#include <hex/pattern_language/ast_node.hpp>
#include <hex/pattern_language/ast/ast_node.hpp>
namespace hex::pl {
void LogConsole::log(Level level, const std::string &message) {
this->m_consoleLog.emplace_back(level, message);
}
[[noreturn]] void LogConsole::abortEvaluation(const std::string &message, const ASTNode *node) {
if (node == nullptr)
throw PatternLanguageError(0, "Evaluator: " + message);
@ -15,9 +11,4 @@ namespace hex::pl {
throw PatternLanguageError(node->getLineNumber(), "Evaluator: " + message);
}
void LogConsole::clear() {
this->m_consoleLog.clear();
this->m_lastHardError = {};
}
}

File diff suppressed because it is too large Load Diff

View File

@ -12,7 +12,7 @@
namespace hex::pl {
class PatternData;
class Pattern;
PatternLanguage::PatternLanguage() {
this->m_preprocessor = new Preprocessor();
@ -92,7 +92,7 @@ namespace hex::pl {
delete this->m_validator;
}
std::optional<std::vector<ASTNode *>> PatternLanguage::parseString(const std::string &code) {
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();
@ -114,9 +114,6 @@ namespace hex::pl {
if (!this->m_validator->validate(*ast)) {
this->m_currError = this->m_validator->getError();
for (auto &node : *ast)
delete node;
return std::nullopt;
}
@ -140,17 +137,18 @@ namespace hex::pl {
for (const auto &[name, value] : envVars)
this->m_evaluator->setEnvVariable(name, value);
for (auto &node : this->m_currAST)
delete node;
this->m_currAST.clear();
auto ast = this->parseString(code);
if (!ast)
return false;
{
auto ast = this->parseString(code);
if (!ast)
return false;
this->m_currAST = ast.value();
this->m_currAST = std::move(ast.value());
}
auto patterns = this->m_evaluator->evaluate(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;
@ -187,14 +185,14 @@ namespace hex::pl {
auto success = this->executeString(provider, functionContent, {}, {}, false);
auto result = this->m_evaluator->getMainResult();
return { success, result };
return { success, std::move(result) };
}
void PatternLanguage::abort() {
this->m_evaluator->abort();
}
const std::vector<ASTNode *> &PatternLanguage::getCurrentAST() const {
const std::vector<std::shared_ptr<ASTNode>> &PatternLanguage::getCurrentAST() const {
return this->m_currAST;
}
@ -229,8 +227,6 @@ namespace hex::pl {
}
void PatternLanguage::reset() {
for (auto &pattern : this->m_patterns)
delete pattern;
this->m_patterns.clear();
this->m_currAST.clear();

View File

@ -1,6 +1,11 @@
#include <hex/pattern_language/validator.hpp>
#include <hex/pattern_language/ast_node.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>
@ -9,7 +14,7 @@
namespace hex::pl {
bool Validator::validate(const std::vector<ASTNode *> &ast) {
bool Validator::validate(const std::vector<std::shared_ptr<ASTNode>> &ast) {
std::unordered_set<std::string> identifiers;
std::unordered_set<std::string> types;
@ -19,21 +24,21 @@ namespace hex::pl {
if (node == nullptr)
throwValidatorError("nullptr in AST. This is a bug!", 1);
if (auto variableDeclNode = dynamic_cast<ASTNodeVariableDecl *>(node); variableDeclNode != nullptr) {
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({ variableDeclNode->getType() });
} else if (auto typeDeclNode = dynamic_cast<ASTNodeTypeDecl *>(node); typeDeclNode != nullptr) {
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());
this->validate({ typeDeclNode->getType() });
} else if (auto structNode = dynamic_cast<ASTNodeStruct *>(node); structNode != nullptr) {
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); unionNode != nullptr) {
} else if (auto unionNode = dynamic_cast<ASTNodeUnion *>(node.get()); unionNode != nullptr) {
this->validate(unionNode->getMembers());
} else if (auto enumNode = dynamic_cast<ASTNodeEnum *>(node); enumNode != nullptr) {
} 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)

View File

@ -2,7 +2,10 @@
#include <hex.hpp>
#include <hex/api/event.hpp>
#include <hex/pattern_language/pattern_data.hpp>
#include <hex/pattern_language/patterns/pattern.hpp>
#include <hex/pattern_language/pattern_language.hpp>
#include <hex/ui/view.hpp>
#include <cmath>
#include <cstring>
@ -14,6 +17,7 @@ namespace hex::prv {
Provider::Provider() {
this->m_patches.emplace_back();
this->m_patternLanguageRuntime = std::make_unique<pl::PatternLanguage>();
if (this->hasLoadInterface())
EventManager::post<RequestOpenPopup>(View::toWindowName("hex.builtin.view.provider_settings.load_popup"));

View File

@ -3,10 +3,12 @@
#include <imgui.h>
#include <imgui_freetype.h>
#include <hex/helpers/net.hpp>
#include <hex/api/content_registry.hpp>
#include <hex/pattern_language/pattern_data.hpp>
#include <hex/pattern_language/patterns/pattern.hpp>
#include <hex/ui/view.hpp>
#include <hex/helpers/net.hpp>
#include <hex/helpers/paths.hpp>
#include <hex/helpers/logger.hpp>
#include <fontawesome_font.h>
#include <codicons_font.h>

View File

@ -3,6 +3,7 @@
#include <hex/helpers/socket.hpp>
#include <hex/providers/provider.hpp>
#include <array>
#include <mutex>
#include <string_view>
#include <thread>

View File

@ -59,7 +59,7 @@ namespace hex::plugin::builtin {
u64 m_resizeSize = 0;
std::vector<u8> m_dataToSave;
std::set<pl::PatternData *> m_highlightedPatterns;
std::set<pl::Pattern *> m_highlightedPatterns;
std::string m_loaderScriptScriptPath;
std::string m_loaderScriptFilePath;

View File

@ -19,7 +19,7 @@ namespace hex::plugin::builtin {
void drawContent() override;
private:
std::map<prv::Provider *, std::vector<pl::PatternData *>> m_sortedPatterns;
std::map<prv::Provider *, std::vector<std::shared_ptr<pl::Pattern>>> m_sortedPatterns;
};
}

View File

@ -51,7 +51,8 @@ namespace hex::plugin::builtin {
std::map<std::string, PatternVariable> m_patternVariables;
std::vector<std::string> m_patternTypes;
enum class EnvVarType {
enum class EnvVarType
{
Integer,
Float,
String,
@ -77,7 +78,7 @@ namespace hex::plugin::builtin {
void drawVariableSettings(ImVec2 size);
void loadPatternFile(const fs::path &path);
void clearPatternData();
void clearPatterns();
void parsePattern(const std::string &code);
void evaluatePattern(const std::string &code);

View File

@ -7,7 +7,7 @@
#include <hex/pattern_language/token.hpp>
#include <hex/pattern_language/log_console.hpp>
#include <hex/pattern_language/evaluator.hpp>
#include <hex/pattern_language/pattern_data.hpp>
#include <hex/pattern_language/patterns/pattern.hpp>
#include <vector>
@ -15,7 +15,7 @@
namespace hex::plugin::builtin {
std::string format(pl::Evaluator *ctx, auto params) {
std::string format(pl::Evaluator *ctx, const auto &params) {
auto format = pl::Token::literalToString(params[0], true);
std::string message;
@ -25,7 +25,7 @@ namespace hex::plugin::builtin {
auto &param = params[i];
std::visit(overloaded {
[&](pl::PatternData *value) {
[&](const std::shared_ptr<pl::Pattern> &value) {
formatArgs.push_back(value->toString(ctx->getProvider()));
},
[&](auto &&value) {

View File

@ -1,8 +1,10 @@
#include "content/providers/disk_provider.hpp"
#include <hex/helpers/fmt.hpp>
#include <hex/api/localization.hpp>
#include <hex/helpers/fmt.hpp>
#include <hex/helpers/utils.hpp>
#include <bitset>
#include <filesystem>

View File

@ -1,7 +1,9 @@
#include "content/views/view_pattern_data.hpp"
#include <hex/providers/provider.hpp>
#include <hex/pattern_language/pattern_data.hpp>
#include <hex/pattern_language/pattern_language.hpp>
#include <hex/pattern_language/patterns/pattern.hpp>
namespace hex::plugin::builtin {
@ -18,8 +20,8 @@ namespace hex::plugin::builtin {
EventManager::unsubscribe<EventHighlightingChanged>(this);
}
static bool beginPatternDataTable(prv::Provider *&provider, const std::vector<pl::PatternData *> &patterns, std::vector<pl::PatternData *> &sortedPatterns) {
if (ImGui::BeginTable("##patterndatatable", 6, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Sortable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY)) {
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);
ImGui::TableSetupColumn("hex.builtin.view.pattern_data.var_name"_lang, 0, 0, ImGui::GetID("name"));
ImGui::TableSetupColumn("hex.builtin.view.pattern_data.color"_lang, 0, 0, ImGui::GetID("color"));
@ -33,8 +35,8 @@ namespace hex::plugin::builtin {
if (sortSpecs->SpecsDirty || sortedPatterns.empty()) {
sortedPatterns = patterns;
std::sort(sortedPatterns.begin(), sortedPatterns.end(), [&sortSpecs, &provider](pl::PatternData *left, pl::PatternData *right) -> bool {
return pl::PatternData::sortPatternDataTable(sortSpecs, provider, left, right);
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());
});
for (auto &pattern : sortedPatterns)
@ -55,12 +57,12 @@ namespace hex::plugin::builtin {
if (ImHexApi::Provider::isValid() && provider->isReadable()) {
auto &sortedPatterns = this->m_sortedPatterns[ImHexApi::Provider::get()];
if (beginPatternDataTable(provider, provider->getPatternLanguageRuntime().getPatterns(), sortedPatterns)) {
if (beginPatternTable(provider, provider->getPatternLanguageRuntime().getPatterns(), sortedPatterns)) {
ImGui::TableHeadersRow();
if (!sortedPatterns.empty()) {
for (auto &patternData : sortedPatterns)
patternData->draw(provider);
for (auto &patterns : sortedPatterns)
patterns->draw(provider);
}
ImGui::EndTable();

View File

@ -1,13 +1,17 @@
#include "content/views/view_pattern_editor.hpp"
#include <hex/helpers/project_file_handler.hpp>
#include <hex/pattern_language/preprocessor.hpp>
#include <hex/pattern_language/pattern_data.hpp>
#include <hex/pattern_language/ast_node.hpp>
#include <hex/pattern_language/patterns/pattern.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_builtin_type.hpp>
#include <hex/helpers/paths.hpp>
#include <hex/helpers/utils.hpp>
#include <hex/helpers/file.hpp>
#include <hex/helpers/project_file_handler.hpp>
#include <hex/helpers/magic.hpp>
namespace hex::plugin::builtin {
@ -225,7 +229,7 @@ namespace hex::plugin::builtin {
ImHexApi::HexEditor::addHighlightingProvider([](u64 address) -> std::optional<ImHexApi::HexEditor::Highlighting> {
auto patterns = ImHexApi::Provider::get()->getPatternLanguageRuntime().getPatterns();
const auto &patterns = ImHexApi::Provider::get()->getPatternLanguageRuntime().getPatterns();
for (const auto &pattern : patterns) {
auto child = pattern->getPattern(address);
if (child != nullptr) {
@ -586,7 +590,7 @@ namespace hex::plugin::builtin {
}
}
void ViewPatternEditor::clearPatternData() {
void ViewPatternEditor::clearPatterns() {
if (!ImHexApi::Provider::isValid()) return;
ImHexApi::Provider::get()->getPatternLanguageRuntime().reset();
@ -603,12 +607,12 @@ namespace hex::plugin::builtin {
this->m_patternTypes.clear();
if (ast) {
for (auto node : *ast) {
if (auto variableDecl = dynamic_cast<pl::ASTNodeVariableDecl *>(node)) {
auto type = dynamic_cast<pl::ASTNodeTypeDecl *>(variableDecl->getType());
for (auto &node : *ast) {
if (auto variableDecl = dynamic_cast<pl::ASTNodeVariableDecl *>(node.get())) {
auto type = dynamic_cast<pl::ASTNodeTypeDecl *>(variableDecl->getType().get());
if (type == nullptr) continue;
auto builtinType = dynamic_cast<pl::ASTNodeBuiltinType *>(type->getType());
auto builtinType = dynamic_cast<pl::ASTNodeBuiltinType *>(type->getType().get());
if (builtinType == nullptr) continue;
PatternVariable variable = {
@ -634,7 +638,7 @@ namespace hex::plugin::builtin {
this->m_textEditor.SetErrorMarkers({});
this->m_console.clear();
this->clearPatternData();
this->clearPatterns();
EventManager::post<EventHighlightingChanged>();

View File

@ -1,295 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "cc"
version = "1.0.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d26a6ce4b6a484fa3edb70f7efa6fc430fd2b87285fe8b84304fd0936faa0dc0"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chlorine"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd650552110e39b7c5058986cf177decd3365841836578ac50a286094eac0be6"
[[package]]
name = "codespan-reporting"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
dependencies = [
"termcolor",
"unicode-width",
]
[[package]]
name = "cxx"
version = "1.0.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "638fb099591b763a988ca69e5f0ee4b82bedb8bc762b3c6cfbfdd3a6dc6b54b4"
dependencies = [
"cc",
"cxxbridge-flags",
"cxxbridge-macro",
"link-cplusplus",
]
[[package]]
name = "cxx-build"
version = "1.0.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83363b96cfd226eb820e37a21088c30c55e47f9fc8299c2d08a6090d50414ccc"
dependencies = [
"cc",
"codespan-reporting",
"lazy_static",
"proc-macro2",
"quote",
"scratch",
"syn",
]
[[package]]
name = "cxxbridge-flags"
version = "1.0.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d4bde25840be4cf0eb1d7e3a74634105189d5609b855bcc8d601ffb037f5f4"
[[package]]
name = "cxxbridge-macro"
version = "1.0.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3869bf8972075de8f0446b433d40026f2dfdbd8a9edbc24e9d645ca2ebf2f93a"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "example_rust"
version = "0.1.0"
dependencies = [
"libimhex-rs",
]
[[package]]
name = "imgui"
version = "0.8.0"
dependencies = [
"bitflags",
"imgui-sys",
"parking_lot",
]
[[package]]
name = "imgui-sys"
version = "0.8.0"
dependencies = [
"cc",
"chlorine",
]
[[package]]
name = "imhex-macros"
version = "0.1.0"
dependencies = [
"quote",
"syn",
]
[[package]]
name = "instant"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "716d3d89f35ac6a34fd0eed635395f4c3b76fa889338a4632e5231a8684216bd"
dependencies = [
"cfg-if",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6"
[[package]]
name = "libimhex-rs"
version = "0.1.0"
dependencies = [
"cxx",
"cxx-build",
"imgui",
"imhex-macros",
]
[[package]]
name = "link-cplusplus"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f1becd27d473556dc610b8afa1636ef90747b574a84553bc11e82371d5ef2d1"
dependencies = [
"cc",
]
[[package]]
name = "lock_api"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109"
dependencies = [
"scopeguard",
]
[[package]]
name = "parking_lot"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
dependencies = [
"instant",
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216"
dependencies = [
"cfg-if",
"instant",
"libc",
"redox_syscall",
"smallvec",
"winapi",
]
[[package]]
name = "proc-macro2"
version = "1.0.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
dependencies = [
"proc-macro2",
]
[[package]]
name = "redox_syscall"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff"
dependencies = [
"bitflags",
]
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "scratch"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e114536316b51a5aa7a0e59fc49661fd263c5507dd08bd28de052e57626ce69"
[[package]]
name = "smallvec"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309"
[[package]]
name = "syn"
version = "1.0.77"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5239bc68e0fef57495900cfea4e8dc75596d9a319d7e16b1e0a440d24e6fe0a0"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "termcolor"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
dependencies = [
"winapi-util",
]
[[package]]
name = "unicode-width"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
[[package]]
name = "unicode-xid"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

View File

@ -1,9 +1,8 @@
project(unit_tests)
enable_testing()
add_compile_definitions(IMHEX_PROJECT_NAME="${PROJECT_NAME}")
add_custom_target(unit_tests)
add_custom_target(unit_tests DEPENDS helpers algorithms pattern_language)
add_subdirectory(common)
add_subdirectory(helpers)

View File

@ -3,7 +3,7 @@
#include <string>
#include <vector>
#include <hex/pattern_language/pattern_data.hpp>
#include <hex/pattern_language/patterns/pattern.hpp>
#define TEST(name) (hex::test::TestPattern *)new hex::test::TestPattern##name()
@ -11,7 +11,8 @@ namespace hex::test {
using namespace pl;
enum class Mode {
enum class Mode
{
Succeeding,
Failing
};
@ -22,25 +23,22 @@ namespace hex::test {
TestPattern::s_tests.insert({ name, this });
}
virtual ~TestPattern() {
for (auto &pattern : this->m_patterns)
delete pattern;
}
virtual ~TestPattern() = default;
template<typename T>
static T *create(const std::string &typeName, const std::string &varName, auto... args) {
auto pattern = new T(nullptr, args...);
static std::unique_ptr<T> create(const std::string &typeName, const std::string &varName, auto... args) {
auto pattern = std::make_unique<T>(nullptr, args...);
pattern->setTypeName(typeName);
pattern->setVariableName(varName);
return pattern;
return std::move(pattern);
}
[[nodiscard]] virtual std::string getSourceCode() const = 0;
[[nodiscard]] virtual const std::vector<PatternData *> &getPatterns() const final { return this->m_patterns; }
virtual void addPattern(PatternData *pattern) final {
this->m_patterns.push_back(pattern);
[[nodiscard]] virtual const std::vector<std::unique_ptr<Pattern>> &getPatterns() const final { return this->m_patterns; }
virtual void addPattern(std::unique_ptr<Pattern> &&pattern) final {
this->m_patterns.push_back(std::move(pattern));
}
[[nodiscard]] auto failing() {
@ -58,7 +56,7 @@ namespace hex::test {
}
private:
std::vector<PatternData *> m_patterns;
std::vector<std::unique_ptr<Pattern>> m_patterns;
Mode m_mode;
static inline std::map<std::string, TestPattern *> s_tests;

View File

@ -2,19 +2,27 @@
#include "test_pattern.hpp"
#include <hex/pattern_language/patterns/pattern_bitfield.hpp>
namespace hex::test {
class TestPatternBitfields : public TestPattern {
public:
TestPatternBitfields() : TestPattern("Bitfields") {
auto testBitfield = create<PatternDataBitfield>("TestBitfield", "testBitfield", 0x12, (4 * 4) / 8);
auto testBitfield = create<PatternBitfield>("TestBitfield", "testBitfield", 0x12, (4 * 4) / 8);
testBitfield->setEndian(std::endian::big);
testBitfield->setFields({ create<PatternDataBitfieldField>("", "a", 0x12, 0, 4, testBitfield),
create<PatternDataBitfieldField>("", "b", 0x12, 4, 4, testBitfield),
create<PatternDataBitfieldField>("", "c", 0x12, 8, 4, testBitfield),
create<PatternDataBitfieldField>("", "d", 0x12, 12, 4, testBitfield) });
addPattern(testBitfield);
std::vector<std::shared_ptr<hex::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()));
bitfieldFields.push_back(create<PatternBitfieldField>("", "c", 0x12, 8, 4, testBitfield.get()));
bitfieldFields.push_back(create<PatternBitfieldField>("", "d", 0x12, 12, 4, testBitfield.get()));
}
testBitfield->setFields(std::move(bitfieldFields));
addPattern(std::move(testBitfield));
}
~TestPatternBitfields() override = default;

View File

@ -2,12 +2,14 @@
#include "test_pattern.hpp"
#include <hex/pattern_language/patterns/pattern_enum.hpp>
namespace hex::test {
class TestPatternEnums : public TestPattern {
public:
TestPatternEnums() : TestPattern("Enums") {
auto testEnum = create<PatternDataEnum>("TestEnum", "testEnum", 0x08, sizeof(u32));
auto testEnum = create<PatternEnum>("TestEnum", "testEnum", 0x08, sizeof(u32));
testEnum->setEnumValues({
{u128(0x00), "A"},
{ i128(0x0C), "B"},
@ -16,7 +18,7 @@ namespace hex::test {
});
testEnum->setEndian(std::endian::big);
addPattern(testEnum);
addPattern(std::move(testEnum));
}
~TestPatternEnums() override = default;

View File

@ -2,21 +2,34 @@
#include "test_pattern.hpp"
#include <hex/pattern_language/patterns/pattern_unsigned.hpp>
#include <hex/pattern_language/patterns/pattern_signed.hpp>
#include <hex/pattern_language/patterns/pattern_struct.hpp>
#include <hex/pattern_language/patterns/pattern_padding.hpp>
#include <hex/pattern_language/patterns/pattern_array_static.hpp>
namespace hex::test {
class TestPatternPadding : public TestPattern {
public:
TestPatternPadding() : TestPattern("Padding") {
auto testStruct = create<PatternDataStruct>("TestStruct", "testStruct", 0x100, sizeof(i32) + 20 + sizeof(u8[0x10]));
auto testStruct = create<PatternStruct>("TestStruct", "testStruct", 0x100, sizeof(i32) + 20 + sizeof(u8[0x10]));
auto variable = create<PatternDataSigned>("s32", "variable", 0x100, sizeof(i32));
auto padding = create<PatternDataPadding>("padding", "", 0x100 + sizeof(i32), 20);
auto array = create<PatternDataStaticArray>("u8", "array", 0x100 + sizeof(i32) + 20, sizeof(u8[0x10]));
array->setEntries(create<PatternDataUnsigned>("u8", "", 0x100 + sizeof(i32) + 20, sizeof(u8)), 0x10);
auto variable = create<PatternSigned>("s32", "variable", 0x100, sizeof(i32));
auto padding = create<PatternPadding>("padding", "", 0x100 + sizeof(i32), 20);
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);
testStruct->setMembers({ variable, padding, array });
std::vector<std::shared_ptr<hex::pl::Pattern>> structMembers;
{
structMembers.push_back(std::move(variable));
structMembers.push_back(std::move(padding));
structMembers.push_back(std::move(array));
}
addPattern(testStruct);
testStruct->setMembers(std::move(structMembers));
addPattern(std::move(testStruct));
}
~TestPatternPadding() override = default;

View File

@ -2,6 +2,9 @@
#include "test_pattern.hpp"
#include <hex/pattern_language/patterns/pattern_unsigned.hpp>
#include <hex/pattern_language/patterns/pattern_array_static.hpp>
namespace hex::test {
class TestPatternPlacement : public TestPattern {
@ -9,14 +12,14 @@ namespace hex::test {
TestPatternPlacement() : TestPattern("Placement") {
// placementVar
{
addPattern(create<PatternDataUnsigned>("u32", "placementVar", 0x00, sizeof(u32)));
addPattern(create<PatternUnsigned>("u32", "placementVar", 0x00, sizeof(u32)));
}
// placementArray
{
auto placementArray = create<PatternDataStaticArray>("u8", "placementArray", 0x10, sizeof(u8) * 10);
placementArray->setEntries(create<PatternDataUnsigned>("u8", "", 0x10, sizeof(u8)), 10);
addPattern(placementArray);
auto placementArray = create<PatternArrayStatic>("u8", "placementArray", 0x10, sizeof(u8) * 10);
placementArray->setEntries(std::move(create<PatternUnsigned>("u8", "", 0x10, sizeof(u8))), 10);
addPattern(std::move(placementArray));
}
}
~TestPatternPlacement() override = default;

View File

@ -2,6 +2,9 @@
#include "test_pattern.hpp"
#include <hex/pattern_language/patterns/pattern_unsigned.hpp>
#include <hex/pattern_language/patterns/pattern_pointer.hpp>
namespace hex::test {
class TestPatternPointers : public TestPattern {
@ -9,12 +12,12 @@ namespace hex::test {
TestPatternPointers() : TestPattern("Pointers") {
// placementPointer
{
auto placementPointer = create<PatternDataPointer>("", "placementPointer", 0x0C, sizeof(u8));
auto placementPointer = create<PatternPointer>("", "placementPointer", 0x0C, sizeof(u8));
placementPointer->setPointedAtAddress(0x49);
auto pointedTo = create<PatternDataUnsigned>("u32", "", 0x49, sizeof(u32));
placementPointer->setPointedAtPattern(pointedTo);
addPattern(placementPointer);
auto pointedTo = create<PatternUnsigned>("u32", "", 0x49, sizeof(u32));
placementPointer->setPointedAtPattern(std::move(pointedTo));
addPattern(std::move(placementPointer));
}
}
~TestPatternPointers() override = default;

View File

@ -2,20 +2,30 @@
#include "test_pattern.hpp"
#include <hex/pattern_language/patterns/pattern_unsigned.hpp>
#include <hex/pattern_language/patterns/pattern_signed.hpp>
#include <hex/pattern_language/patterns/pattern_struct.hpp>
#include <hex/pattern_language/patterns/pattern_array_static.hpp>
namespace hex::test {
class TestPatternStructs : public TestPattern {
public:
TestPatternStructs() : TestPattern("Structs") {
auto testStruct = create<PatternDataStruct>("TestStruct", "testStruct", 0x100, sizeof(i32) + sizeof(u8[0x10]));
auto testStruct = create<PatternStruct>("TestStruct", "testStruct", 0x100, sizeof(i32) + sizeof(u8[0x10]));
auto variable = create<PatternDataSigned>("s32", "variable", 0x100, sizeof(i32));
auto array = create<PatternDataStaticArray>("u8", "array", 0x100 + sizeof(i32), sizeof(u8[0x10]));
array->setEntries(create<PatternDataUnsigned>("u8", "", 0x100 + sizeof(i32), sizeof(u8)), 0x10);
auto variable = create<PatternSigned>("s32", "variable", 0x100, sizeof(i32));
auto array = create<PatternArrayStatic>("u8", "array", 0x100 + sizeof(i32), sizeof(u8[0x10]));
array->setEntries(create<PatternUnsigned>("u8", "", 0x100 + sizeof(i32), sizeof(u8)), 0x10);
testStruct->setMembers({ variable, array });
std::vector<std::shared_ptr<hex::pl::Pattern>> structMembers;
{
structMembers.push_back(std::move(variable));
structMembers.push_back(std::move(array));
}
testStruct->setMembers(std::move(structMembers));
addPattern(testStruct);
addPattern(std::move(testStruct));
}
~TestPatternStructs() override = default;

View File

@ -2,20 +2,31 @@
#include "test_pattern.hpp"
#include <hex/pattern_language/patterns/pattern_unsigned.hpp>
#include <hex/pattern_language/patterns/pattern_signed.hpp>
#include <hex/pattern_language/patterns/pattern_array_static.hpp>
#include <hex/pattern_language/patterns/pattern_union.hpp>
namespace hex::test {
class TestPatternUnions : public TestPattern {
public:
TestPatternUnions() : TestPattern("Unions") {
auto testUnion = create<PatternDataUnion>("TestUnion", "testUnion", 0x200, sizeof(u128));
auto testUnion = create<PatternUnion>("TestUnion", "testUnion", 0x200, sizeof(u128));
auto array = create<PatternDataStaticArray>("s32", "array", 0x200, sizeof(i32[2]));
array->setEntries(create<PatternDataSigned>("s32", "", 0x200, sizeof(i32)), 2);
auto variable = create<PatternDataUnsigned>("u128", "variable", 0x200, sizeof(u128));
auto array = create<PatternArrayStatic>("s32", "array", 0x200, sizeof(i32[2]));
array->setEntries(create<PatternSigned>("s32", "", 0x200, sizeof(i32)), 2);
auto variable = create<PatternUnsigned>("u128", "variable", 0x200, sizeof(u128));
testUnion->setMembers({ array, variable });
std::vector<std::shared_ptr<hex::pl::Pattern>> unionMembers;
{
unionMembers.push_back(std::move(array));
unionMembers.push_back(std::move(variable));
}
addPattern(testUnion);
testUnion->setMembers(std::move(unionMembers));
addPattern(std::move(testUnion));
}
~TestPatternUnions() override = default;

View File

@ -7,14 +7,43 @@
#include <hex/helpers/fmt.hpp>
#include <hex/pattern_language/pattern_language.hpp>
#include <hex/pattern_language/evaluator.hpp>
#include <hex/pattern_language/ast_node.hpp>
#include <hex/pattern_language/ast/ast_node.hpp>
#include <hex/pattern_language/patterns/pattern.hpp>
#include <hex/api/content_registry.hpp>
#include "test_provider.hpp"
#include "test_patterns/test_pattern.hpp"
#include <fmt/args.h>
using namespace hex::test;
static std::string format(hex::pl::Evaluator *ctx, const auto &params) {
auto format = hex::pl::Token::literalToString(params[0], true);
std::string message;
fmt::dynamic_format_arg_store<fmt::format_context> formatArgs;
for (u32 i = 1; i < params.size(); i++) {
auto &param = params[i];
std::visit(hex::overloaded {
[&](const std::shared_ptr<hex::pl::Pattern> &value) {
formatArgs.push_back(value->toString(ctx->getProvider()));
},
[&](auto &&value) {
formatArgs.push_back(value);
} },
param);
}
try {
return fmt::vformat(format, formatArgs);
} catch (fmt::format_error &error) {
hex::pl::LogConsole::abortEvaluation(hex::format("format error: {}", error.what()));
}
}
void addFunctions() {
hex::ContentRegistry::PatternLanguage::Namespace nsStd = { "std" };
hex::ContentRegistry::PatternLanguage::addFunction(nsStd, "assert", 2, [](Evaluator *ctx, auto params) -> Token::Literal {
@ -26,6 +55,12 @@ void addFunctions() {
return {};
});
hex::ContentRegistry::PatternLanguage::addFunction(nsStd, "print", hex::ContentRegistry::PatternLanguage::MoreParametersThan | 0, [](Evaluator *ctx, auto params) -> std::optional<Token::Literal> {
ctx->getConsole().log(LogConsole::Level::Info, format(ctx, params));
return std::nullopt;
});
}
int test(int argc, char **argv) {
@ -55,7 +90,6 @@ int test(int argc, char **argv) {
}
hex::pl::PatternLanguage language;
addFunctions();
// Check if compilation succeeded
auto result = language.executeString(provider, testPatterns[testName]->getSourceCode());
@ -75,11 +109,6 @@ int test(int argc, char **argv) {
return EXIT_FAILURE;
}
ON_SCOPE_EXIT {
for (auto &pattern : language.getPatterns())
delete pattern;
};
// Check if the right number of patterns have been produced
if (language.getPatterns().size() != currTest->getPatterns().size() && !currTest->getPatterns().empty()) {
hex::log::fatal("Source didn't produce expected number of patterns");
@ -103,6 +132,8 @@ int test(int argc, char **argv) {
int main(int argc, char **argv) {
int result = EXIT_SUCCESS;
addFunctions();
for (u32 i = 0; i < 16; i++) {
result = test(argc, argv);
if (result != EXIT_SUCCESS)