diff --git a/.gitignore b/.gitignore index bf27a3448..28597356b 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,5 @@ imgui.ini plugins/.rustc_info.json **/target + +plugins/example_rust/Cargo.lock diff --git a/CMakeLists.txt b/CMakeLists.txt index 67b73983a..be004e72e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/lib/libimhex/include/hex/api/event.hpp b/lib/libimhex/include/hex/api/event.hpp index b0fcd56a5..cb638cbf2 100644 --- a/lib/libimhex/include/hex/api/event.hpp +++ b/lib/libimhex/include/hex/api/event.hpp @@ -97,7 +97,7 @@ namespace hex { }; namespace pl { - class PatternData; + class Pattern; } /* Default Events */ diff --git a/lib/libimhex/include/hex/helpers/concepts.hpp b/lib/libimhex/include/hex/helpers/concepts.hpp index c6c11bd4c..78edb0979 100644 --- a/lib/libimhex/include/hex/helpers/concepts.hpp +++ b/lib/libimhex/include/hex/helpers/concepts.hpp @@ -3,6 +3,7 @@ #include #include +#include namespace hex { @@ -166,7 +167,7 @@ namespace hex { template class Cloneable { public: - [[nodiscard]] virtual T *clone() const = 0; + [[nodiscard]] virtual std::unique_ptr clone() const = 0; }; } \ No newline at end of file diff --git a/lib/libimhex/include/hex/helpers/utils.hpp b/lib/libimhex/include/hex/helpers/utils.hpp index 8b40f4b6f..d697fd0a7 100644 --- a/lib/libimhex/include/hex/helpers/utils.hpp +++ b/lib/libimhex/include/hex/helpers/utils.hpp @@ -193,6 +193,22 @@ namespace hex { return T(1) << bit_width(T(x - 1)); } + template + void moveToVector(std::vector &buffer, T &&first, Args &&...rest) { + buffer.push_back(std::move(first)); + + if constexpr (sizeof...(rest) > 0) + moveToVector(buffer, std::move(rest)...); + } + + template + std::vector moveToVector(T &&first, Args &&...rest) { + std::vector result; + moveToVector(result, T(std::move(first)), std::move(rest)...); + + return result; + } + std::vector splitString(const std::string &string, const std::string &delimiter); std::string combineStrings(const std::vector &strings, const std::string &delimiter = ""); diff --git a/lib/libimhex/include/hex/pattern_language/ast/ast_node.hpp b/lib/libimhex/include/hex/pattern_language/ast/ast_node.hpp new file mode 100644 index 000000000..3a34c20ca --- /dev/null +++ b/lib/libimhex/include/hex/pattern_language/ast/ast_node.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include +#include + +namespace hex::pl { + + class Pattern; + class Evaluator; + + class ASTNode : public Cloneable { + 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 evaluate(Evaluator *evaluator) const { return this->clone(); } + + [[nodiscard]] virtual std::vector> createPatterns(Evaluator *evaluator) const { return {}; } + + using FunctionResult = std::optional; + virtual FunctionResult execute(Evaluator *evaluator) const { LogConsole::abortEvaluation("cannot execute non-function statement", this); } + + private: + u32 m_lineNumber = 1; + }; + +} \ No newline at end of file diff --git a/lib/libimhex/include/hex/pattern_language/ast/ast_node_array_variable_decl.hpp b/lib/libimhex/include/hex/pattern_language/ast/ast_node_array_variable_decl.hpp new file mode 100644 index 000000000..5d4bd8d06 --- /dev/null +++ b/lib/libimhex/include/hex/pattern_language/ast/ast_node_array_variable_decl.hpp @@ -0,0 +1,309 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace hex::pl { + + class ASTNodeArrayVariableDecl : public ASTNode, + public Attributable { + public: + ASTNodeArrayVariableDecl(std::string name, std::unique_ptr &&type, std::unique_ptr &&size, std::unique_ptr &&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 clone() const override { + return std::unique_ptr(new ASTNodeArrayVariableDecl(*this)); + } + + [[nodiscard]] std::vector> 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(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 &) -> 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; + if (dynamic_cast(type.get())) + pattern = createStaticArray(evaluator); + else if (auto attributable = dynamic_cast(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 m_type; + std::unique_ptr m_size; + std::unique_ptr m_placementOffset; + + std::unique_ptr 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(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 &) -> i128 { LogConsole::abortEvaluation("cannot use custom type to index array", this); }, + [](auto &&size) -> i128 { return size; } }, + literal->getValue()); + } else if (auto whileStatement = dynamic_cast(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 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 outputPattern; + if (dynamic_cast(templatePattern.get())) { + outputPattern = std::unique_ptr(new PatternPadding(evaluator, startOffset, 0)); + } else if (dynamic_cast(templatePattern.get())) { + outputPattern = std::unique_ptr(new PatternString(evaluator, startOffset, 0)); + } else if (dynamic_cast(templatePattern.get())) { + outputPattern = std::unique_ptr(new PatternWideString(evaluator, startOffset, 0)); + } else { + auto arrayPattern = std::make_unique(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 createDynamicArray(Evaluator *evaluator) const { + auto arrayPattern = std::make_unique(evaluator, evaluator->dataOffset(), 0); + arrayPattern->setVariableName(this->m_name); + + std::vector> entries; + + size_t size = 0; + u64 entryIndex = 0; + + auto addEntries = [&](std::vector> &&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(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 &) -> 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(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 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); + } + }; + +} \ No newline at end of file diff --git a/lib/libimhex/include/hex/pattern_language/ast/ast_node_assignment.hpp b/lib/libimhex/include/hex/pattern_language/ast/ast_node_assignment.hpp new file mode 100644 index 000000000..3fd529d52 --- /dev/null +++ b/lib/libimhex/include/hex/pattern_language/ast/ast_node_assignment.hpp @@ -0,0 +1,53 @@ +#pragma once + +#include +#include + +namespace hex::pl { + + class ASTNodeAssignment : public ASTNode { + public: + ASTNodeAssignment(std::string lvalueName, std::unique_ptr &&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 clone() const override { + return std::unique_ptr(new ASTNodeAssignment(*this)); + } + + [[nodiscard]] const std::string &getLValueName() const { + return this->m_lvalueName; + } + + [[nodiscard]] const std::unique_ptr &getRValue() const { + return this->m_rvalue; + } + + [[nodiscard]] std::vector> 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(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 m_rvalue; + }; + +} \ No newline at end of file diff --git a/lib/libimhex/include/hex/pattern_language/ast/ast_node_attribute.hpp b/lib/libimhex/include/hex/pattern_language/ast/ast_node_attribute.hpp new file mode 100644 index 000000000..684d480f4 --- /dev/null +++ b/lib/libimhex/include/hex/pattern_language/ast/ast_node_attribute.hpp @@ -0,0 +1,216 @@ +#pragma once + +#include + +#include +#include + +namespace hex::pl { + + class ASTNodeAttribute : public ASTNode { + public: + explicit ASTNodeAttribute(std::string attribute, std::optional 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 clone() const override { + return std::unique_ptr(new ASTNodeAttribute(*this)); + } + + [[nodiscard]] const std::string &getAttribute() const { + return this->m_attribute; + } + + [[nodiscard]] const std::optional &getValue() const { + return this->m_value; + } + + private: + std::string m_attribute; + std::optional 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(copy.get())) + this->m_attributes.push_back(std::unique_ptr(node)); + } + } + + public: + virtual void addAttribute(std::unique_ptr &&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 &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 getAttributeValue(const std::string &key) const { + auto attribute = std::find_if(this->m_attributes.begin(), this->m_attributes.end(), [&](const std::unique_ptr &attribute) { + return attribute->getAttribute() == key; + }); + + if (attribute != this->m_attributes.end()) + return (*attribute)->getValue(); + else + return std::nullopt; + } + + private: + std::vector> m_attributes; + }; + + + inline void applyTypeAttributes(Evaluator *evaluator, const ASTNode *node, Pattern *pattern) { + auto attributable = dynamic_cast(node); + if (attributable == nullptr) + LogConsole::abortEvaluation("attribute cannot be applied here", node); + + if (attributable->hasAttribute("inline", false)) { + auto inlinable = dynamic_cast(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(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(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(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(); + } + } + +} \ No newline at end of file diff --git a/lib/libimhex/include/hex/pattern_language/ast/ast_node_bitfield.hpp b/lib/libimhex/include/hex/pattern_language/ast/ast_node_bitfield.hpp new file mode 100644 index 000000000..07ceffa67 --- /dev/null +++ b/lib/libimhex/include/hex/pattern_language/ast/ast_node_bitfield.hpp @@ -0,0 +1,85 @@ +#pragma once + +#include +#include + +#include + +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 clone() const override { + return std::unique_ptr(new ASTNodeBitfield(*this)); + } + + [[nodiscard]] const std::vector>> &getEntries() const { return this->m_entries; } + void addEntry(const std::string &name, std::unique_ptr &&size) { this->m_entries.emplace_back(name, std::move(size)); } + + [[nodiscard]] std::vector> createPatterns(Evaluator *evaluator) const override { + auto pattern = std::make_unique(evaluator, evaluator->dataOffset(), 0); + + size_t bitOffset = 0; + std::vector> 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> 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 &) -> u8 { LogConsole::abortEvaluation("bitfield field size cannot be a custom type", this); }, + [](auto &&offset) -> u8 { return static_cast(offset); } }, + dynamic_cast(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(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::move(pattern)); + } + + private: + std::vector>> m_entries; + }; + +} \ No newline at end of file diff --git a/lib/libimhex/include/hex/pattern_language/ast/ast_node_builtin_type.hpp b/lib/libimhex/include/hex/pattern_language/ast/ast_node_builtin_type.hpp new file mode 100644 index 000000000..f76ddb8d8 --- /dev/null +++ b/lib/libimhex/include/hex/pattern_language/ast/ast_node_builtin_type.hpp @@ -0,0 +1,64 @@ +#pragma once + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +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 clone() const override { + return std::unique_ptr(new ASTNodeBuiltinType(*this)); + } + + [[nodiscard]] std::vector> createPatterns(Evaluator *evaluator) const override { + auto offset = evaluator->dataOffset(); + auto size = Token::getTypeSize(this->m_type); + + evaluator->dataOffset() += size; + + std::unique_ptr pattern; + if (Token::isUnsigned(this->m_type)) + pattern = std::unique_ptr(new PatternUnsigned(evaluator, offset, size)); + else if (Token::isSigned(this->m_type)) + pattern = std::unique_ptr(new PatternSigned(evaluator, offset, size)); + else if (Token::isFloatingPoint(this->m_type)) + pattern = std::unique_ptr(new PatternFloat(evaluator, offset, size)); + else if (this->m_type == Token::ValueType::Boolean) + pattern = std::unique_ptr(new PatternBoolean(evaluator, offset)); + else if (this->m_type == Token::ValueType::Character) + pattern = std::unique_ptr(new PatternCharacter(evaluator, offset)); + else if (this->m_type == Token::ValueType::Character16) + pattern = std::unique_ptr(new PatternWideCharacter(evaluator, offset)); + else if (this->m_type == Token::ValueType::Padding) + pattern = std::unique_ptr(new PatternPadding(evaluator, offset, 1)); + else if (this->m_type == Token::ValueType::String) + pattern = std::unique_ptr(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; + }; + +} \ No newline at end of file diff --git a/lib/libimhex/include/hex/pattern_language/ast/ast_node_cast.hpp b/lib/libimhex/include/hex/pattern_language/ast/ast_node_cast.hpp new file mode 100644 index 000000000..58b115d85 --- /dev/null +++ b/lib/libimhex/include/hex/pattern_language/ast/ast_node_cast.hpp @@ -0,0 +1,92 @@ +#pragma once + +#include + +namespace hex::pl { + + class ASTNodeCast : public ASTNode { + public: + ASTNodeCast(std::unique_ptr &&value, std::unique_ptr &&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 clone() const override { + return std::unique_ptr(new ASTNodeCast(*this)); + } + + [[nodiscard]] std::unique_ptr evaluate(Evaluator *evaluator) const override { + auto evaluatedValue = this->m_value->evaluate(evaluator); + auto evaluatedType = this->m_type->evaluate(evaluator); + + auto literal = dynamic_cast(evaluatedValue.get()); + auto type = dynamic_cast(evaluatedType.get())->getType(); + + auto startOffset = evaluator->dataOffset(); + + auto typePatterns = this->m_type->createPatterns(evaluator); + auto &typePattern = typePatterns.front(); + + return std::unique_ptr(std::visit(overloaded { + [&, this](const std::shared_ptr &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 m_value; + std::unique_ptr m_type; + }; + +} \ No newline at end of file diff --git a/lib/libimhex/include/hex/pattern_language/ast/ast_node_compound_statement.hpp b/lib/libimhex/include/hex/pattern_language/ast/ast_node_compound_statement.hpp new file mode 100644 index 000000000..1129e54ba --- /dev/null +++ b/lib/libimhex/include/hex/pattern_language/ast/ast_node_compound_statement.hpp @@ -0,0 +1,80 @@ +#pragma once + +#include + +namespace hex::pl { + + class ASTNodeCompoundStatement : public ASTNode { + public: + explicit ASTNodeCompoundStatement(std::vector> &&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 clone() const override { + return std::unique_ptr(new ASTNodeCompoundStatement(*this)); + } + + [[nodiscard]] std::unique_ptr evaluate(Evaluator *evaluator) const override { + std::unique_ptr result = nullptr; + + for (const auto &statement : this->m_statements) { + result = statement->evaluate(evaluator); + } + + return result; + } + + [[nodiscard]] std::vector> createPatterns(Evaluator *evaluator) const override { + std::vector> 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> m_statements; + bool m_newScope = false; + }; + +} \ No newline at end of file diff --git a/lib/libimhex/include/hex/pattern_language/ast/ast_node_conditional_statement.hpp b/lib/libimhex/include/hex/pattern_language/ast/ast_node_conditional_statement.hpp new file mode 100644 index 000000000..1525de6e9 --- /dev/null +++ b/lib/libimhex/include/hex/pattern_language/ast/ast_node_conditional_statement.hpp @@ -0,0 +1,92 @@ +#pragma once + +#include + +namespace hex::pl { + + class ASTNodeConditionalStatement : public ASTNode { + public: + explicit ASTNodeConditionalStatement(std::unique_ptr condition, std::vector> &&trueBody, std::vector> &&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 clone() const override { + return std::unique_ptr(new ASTNodeConditionalStatement(*this)); + } + + [[nodiscard]] std::vector> 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 &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(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 m_condition; + std::vector> m_trueBody, m_falseBody; + }; + +} \ No newline at end of file diff --git a/lib/libimhex/include/hex/pattern_language/ast/ast_node_control_flow_statement.hpp b/lib/libimhex/include/hex/pattern_language/ast/ast_node_control_flow_statement.hpp new file mode 100644 index 000000000..1eb428fb1 --- /dev/null +++ b/lib/libimhex/include/hex/pattern_language/ast/ast_node_control_flow_statement.hpp @@ -0,0 +1,54 @@ +#pragma once + +#include + +namespace hex::pl { + + class ASTNodeControlFlowStatement : public ASTNode { + public: + explicit ASTNodeControlFlowStatement(ControlFlowStatement type, std::unique_ptr &&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 clone() const override { + return std::unique_ptr(new ASTNodeControlFlowStatement(*this)); + } + + [[nodiscard]] const std::unique_ptr &getReturnValue() const { + return this->m_rvalue; + } + + [[nodiscard]] std::vector> 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(returnValue.get()); + + evaluator->setCurrentControlFlowStatement(this->m_type); + + if (literal == nullptr) + return std::nullopt; + else + return literal->getValue(); + } + + private: + ControlFlowStatement m_type; + std::unique_ptr m_rvalue; + }; + +} \ No newline at end of file diff --git a/lib/libimhex/include/hex/pattern_language/ast/ast_node_enum.hpp b/lib/libimhex/include/hex/pattern_language/ast/ast_node_enum.hpp new file mode 100644 index 000000000..3e4c2340b --- /dev/null +++ b/lib/libimhex/include/hex/pattern_language/ast/ast_node_enum.hpp @@ -0,0 +1,59 @@ +#pragma once + +#include +#include + +#include + +namespace hex::pl { + + class ASTNodeEnum : public ASTNode, + public Attributable { + public: + explicit ASTNodeEnum(std::unique_ptr &&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 clone() const override { + return std::unique_ptr(new ASTNodeEnum(*this)); + } + + [[nodiscard]] std::vector> createPatterns(Evaluator *evaluator) const override { + auto pattern = std::make_unique(evaluator, evaluator->dataOffset(), 0); + + std::vector> enumEntries; + for (const auto &[name, value] : this->m_entries) { + const auto node = value->evaluate(evaluator); + auto literal = dynamic_cast(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::move(pattern)); + } + + [[nodiscard]] const std::map> &getEntries() const { return this->m_entries; } + void addEntry(const std::string &name, std::unique_ptr &&expression) { this->m_entries.insert({ name, std::move(expression) }); } + + [[nodiscard]] const std::unique_ptr &getUnderlyingType() { return this->m_underlyingType; } + + private: + std::map> m_entries; + std::unique_ptr m_underlyingType; + }; + +} \ No newline at end of file diff --git a/lib/libimhex/include/hex/pattern_language/ast/ast_node_function_call.hpp b/lib/libimhex/include/hex/pattern_language/ast/ast_node_function_call.hpp new file mode 100644 index 000000000..b353032c8 --- /dev/null +++ b/lib/libimhex/include/hex/pattern_language/ast/ast_node_function_call.hpp @@ -0,0 +1,126 @@ +#pragma once + +#include +#include +#include +#include + +#include + +namespace hex::pl { + + class ASTNodeFunctionCall : public ASTNode { + public: + explicit ASTNodeFunctionCall(std::string functionName, std::vector> &¶ms) + : ASTNode(), m_functionName(std::move(functionName)), m_params(std::move(params)) { } + + ASTNodeFunctionCall(const ASTNodeFunctionCall &other) : ASTNode(other) { + this->m_functionName = other.m_functionName; + + for (auto ¶m : other.m_params) + this->m_params.push_back(param->clone()); + } + + [[nodiscard]] std::unique_ptr clone() const override { + return std::unique_ptr(new ASTNodeFunctionCall(*this)); + } + + [[nodiscard]] const std::string &getFunctionName() { + return this->m_functionName; + } + + [[nodiscard]] const std::vector> &getParams() const { + return this->m_params; + } + + [[nodiscard]] std::vector> createPatterns(Evaluator *evaluator) const override { + + this->execute(evaluator); + + return {}; + } + + [[nodiscard]] std::unique_ptr evaluate(Evaluator *evaluator) const override { + std::vector evaluatedParams; + for (auto ¶m : this->m_params) { + const auto expression = param->evaluate(evaluator)->evaluate(evaluator); + + if (auto literal = dynamic_cast(expression.get())) { + evaluatedParams.push_back(literal->getValue()); + } else if (auto parameterPack = dynamic_cast(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(new ASTNodeLiteral(std::move(result.value()))); + else + return std::unique_ptr(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> m_params; + }; + +} \ No newline at end of file diff --git a/lib/libimhex/include/hex/pattern_language/ast/ast_node_function_definition.hpp b/lib/libimhex/include/hex/pattern_language/ast/ast_node_function_definition.hpp new file mode 100644 index 000000000..c89748b75 --- /dev/null +++ b/lib/libimhex/include/hex/pattern_language/ast/ast_node_function_definition.hpp @@ -0,0 +1,103 @@ +#pragma once + +#include + +namespace hex::pl { + + class ASTNodeFunctionDefinition : public ASTNode { + public: + ASTNodeFunctionDefinition(std::string name, std::vector>> &¶ms, std::vector> &&body, std::optional 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 clone() const override { + return std::unique_ptr(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 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 ¶ms) -> std::optional { + std::vector> variables; + + ctx->pushScope(nullptr, variables); + ON_SCOPE_EXIT { + ctx->popScope(); + }; + + if (this->m_parameterPack.has_value()) { + std::vector 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>> m_params; + std::vector> m_body; + std::optional m_parameterPack; + }; + +} \ No newline at end of file diff --git a/lib/libimhex/include/hex/pattern_language/ast/ast_node_literal.hpp b/lib/libimhex/include/hex/pattern_language/ast/ast_node_literal.hpp new file mode 100644 index 000000000..7be3e7c20 --- /dev/null +++ b/lib/libimhex/include/hex/pattern_language/ast/ast_node_literal.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include + +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 clone() const override { + return std::unique_ptr(new ASTNodeLiteral(*this)); + } + + [[nodiscard]] const auto &getValue() const { + return this->m_literal; + } + + private: + Token::Literal m_literal; + }; + +} \ No newline at end of file diff --git a/lib/libimhex/include/hex/pattern_language/ast/ast_node_mathematical_expression.hpp b/lib/libimhex/include/hex/pattern_language/ast/ast_node_mathematical_expression.hpp new file mode 100644 index 000000000..a773a8fb5 --- /dev/null +++ b/lib/libimhex/include/hex/pattern_language/ast/ast_node_mathematical_expression.hpp @@ -0,0 +1,208 @@ +#pragma once + +#include + +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 &&left, std::unique_ptr &&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 clone() const override { + return std::unique_ptr(new ASTNodeMathematicalExpression(*this)); + } + + [[nodiscard]] std::unique_ptr 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(leftValue.get()); + auto *right = dynamic_cast(rightValue.get()); + + return std::unique_ptr(std::visit(overloaded { + // TODO: :notlikethis: + [this](u128 left, const std::shared_ptr &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); }, + [this](i128 left, const std::shared_ptr &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); }, + [this](double left, const std::shared_ptr &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); }, + [this](char left, const std::shared_ptr &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); }, + [this](bool left, const std::shared_ptr &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); }, + [this](const std::string &left, const std::shared_ptr &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); }, + [this](const std::shared_ptr &left, u128 right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); }, + [this](const std::shared_ptr &left, i128 right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); }, + [this](const std::shared_ptr &left, double right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); }, + [this](const std::shared_ptr &left, char right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); }, + [this](const std::shared_ptr &left, bool right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); }, + [this](const std::shared_ptr &left, const std::string &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); }, + [this](const std::shared_ptr &left, const std::shared_ptr &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 &getLeftOperand() const { return this->m_left; } + [[nodiscard]] const std::unique_ptr &getRightOperand() const { return this->m_right; } + [[nodiscard]] Token::Operator getOperator() const { return this->m_operator; } + + private: + std::unique_ptr m_left, m_right; + Token::Operator m_operator; + }; + +#undef FLOAT_BIT_OPERATION + +} \ No newline at end of file diff --git a/lib/libimhex/include/hex/pattern_language/ast/ast_node_multi_variable_decl.hpp b/lib/libimhex/include/hex/pattern_language/ast/ast_node_multi_variable_decl.hpp new file mode 100644 index 000000000..d108d9ccf --- /dev/null +++ b/lib/libimhex/include/hex/pattern_language/ast/ast_node_multi_variable_decl.hpp @@ -0,0 +1,52 @@ +#pragma once + +#include + +#include + +namespace hex::pl { + + class ASTNodeMultiVariableDecl : public ASTNode { + public: + explicit ASTNodeMultiVariableDecl(std::vector> &&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 clone() const override { + return std::unique_ptr(new ASTNodeMultiVariableDecl(*this)); + } + + [[nodiscard]] const std::vector> &getVariables() { + return this->m_variables; + } + + [[nodiscard]] std::vector> createPatterns(Evaluator *evaluator) const override { + std::vector> 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(variable.get()); + auto variableType = variableDecl->getType()->evaluate(evaluator); + + evaluator->createVariable(variableDecl->getName(), variableType.get()); + } + + return std::nullopt; + } + + private: + std::vector> m_variables; + }; + +} \ No newline at end of file diff --git a/lib/libimhex/include/hex/pattern_language/ast/ast_node_parameter_pack.hpp b/lib/libimhex/include/hex/pattern_language/ast/ast_node_parameter_pack.hpp new file mode 100644 index 000000000..a008a9e0a --- /dev/null +++ b/lib/libimhex/include/hex/pattern_language/ast/ast_node_parameter_pack.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include + +namespace hex::pl { + + class ASTNodeParameterPack : public ASTNode { + public: + explicit ASTNodeParameterPack(std::vector &&values) : m_values(std::move(values)) { } + + [[nodiscard]] std::unique_ptr clone() const override { + return std::unique_ptr(new ASTNodeParameterPack(*this)); + } + + [[nodiscard]] const std::vector &getValues() const { + return this->m_values; + } + + private: + std::vector m_values; + }; + +} \ No newline at end of file diff --git a/lib/libimhex/include/hex/pattern_language/ast/ast_node_pointer_variable_decl.hpp b/lib/libimhex/include/hex/pattern_language/ast/ast_node_pointer_variable_decl.hpp new file mode 100644 index 000000000..3ab071cce --- /dev/null +++ b/lib/libimhex/include/hex/pattern_language/ast/ast_node_pointer_variable_decl.hpp @@ -0,0 +1,95 @@ +#pragma once + +#include +#include + +#include + +namespace hex::pl { + + class ASTNodePointerVariableDecl : public ASTNode, + public Attributable { + public: + ASTNodePointerVariableDecl(std::string name, std::shared_ptr &&type, std::shared_ptr &&sizeType, std::unique_ptr &&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 clone() const override { + return std::unique_ptr(new ASTNodePointerVariableDecl(*this)); + } + + [[nodiscard]] const std::string &getName() const { return this->m_name; } + [[nodiscard]] constexpr const std::shared_ptr &getType() const { return this->m_type; } + [[nodiscard]] constexpr const std::shared_ptr &getSizeType() const { return this->m_sizeType; } + [[nodiscard]] constexpr const std::unique_ptr &getPlacementOffset() const { return this->m_placementOffset; } + + [[nodiscard]] std::vector> 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(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 &) -> 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(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::move(pattern)); + } + + private: + std::string m_name; + std::shared_ptr m_type; + std::shared_ptr m_sizeType; + std::unique_ptr m_placementOffset; + }; + +} \ No newline at end of file diff --git a/lib/libimhex/include/hex/pattern_language/ast/ast_node_rvalue.hpp b/lib/libimhex/include/hex/pattern_language/ast/ast_node_rvalue.hpp new file mode 100644 index 000000000..8fed02189 --- /dev/null +++ b/lib/libimhex/include/hex/pattern_language/ast/ast_node_rvalue.hpp @@ -0,0 +1,293 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace hex::pl { + + class ASTNodeRValue : public ASTNode { + public: + using PathSegment = std::variant>; + using Path = std::vector; + + 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(&part); stringPart != nullptr) + this->m_path.push_back(*stringPart); + else if (auto nodePart = std::get_if>(&part); nodePart != nullptr) + this->m_path.push_back((*nodePart)->clone()); + } + } + + [[nodiscard]] std::unique_ptr clone() const override { + return std::unique_ptr(new ASTNodeRValue(*this)); + } + + [[nodiscard]] const Path &getPath() const { + return this->m_path; + } + + [[nodiscard]] std::unique_ptr evaluate(Evaluator *evaluator) const override { + if (this->getPath().size() == 1) { + if (auto name = std::get_if(&this->getPath().front()); name != nullptr) { + if (*name == "$") return std::unique_ptr(new ASTNodeLiteral(u128(evaluator->dataOffset()))); + + auto parameterPack = evaluator->getScope(0).parameterPack; + if (parameterPack && *name == parameterPack->name) + return std::unique_ptr(new ASTNodeParameterPack(std::move(parameterPack->values))); + } + } + + auto patterns = this->createPatterns(evaluator); + auto &pattern = patterns.front(); + + Token::Literal literal; + if (dynamic_cast(pattern.get()) || dynamic_cast(pattern.get())) { + u128 value = 0; + readVariable(evaluator, value, pattern.get()); + literal = value; + } else if (dynamic_cast(pattern.get())) { + i128 value = 0; + readVariable(evaluator, value, pattern.get()); + value = hex::signExtend(pattern->getSize() * 8, value); + literal = value; + } else if (dynamic_cast(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(pattern.get())) { + char value = 0; + readVariable(evaluator, value, pattern.get()); + literal = value; + } else if (dynamic_cast(pattern.get())) { + bool value = false; + readVariable(evaluator, value, pattern.get()); + literal = value; + } else if (dynamic_cast(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(assignmentValue) && !dynamic_cast(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(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(new ASTNodeLiteral(std::move(literal))); + } + + [[nodiscard]] std::vector> createPatterns(Evaluator *evaluator) const override { + std::vector> searchScope; + std::unique_ptr 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(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>(part)->evaluate(evaluator); + auto index = dynamic_cast(node.get()); + + std::visit(overloaded { + [this](const std::string &) { LogConsole::abortEvaluation("cannot use string to index array", this); }, + [this](const std::shared_ptr &) { LogConsole::abortEvaluation("cannot use custom type to index array", this); }, + [&, this](auto &&index) { + if (auto dynamicArrayPattern = dynamic_cast(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(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(currPattern.get())) { + currPattern = pointerPattern->getPointedAtPattern()->clone(); + } + + std::shared_ptr indexPattern; + if (currPattern->isLocal()) { + auto stackLiteral = evaluator->getStack()[currPattern->getOffset()]; + if (auto stackPattern = std::get_if>(&stackLiteral); stackPattern != nullptr) + indexPattern = *stackPattern; + else + return hex::moveToVector>(std::move(currPattern)); + } else + indexPattern = currPattern->clone(); + + if (auto structPattern = dynamic_cast(indexPattern.get())) + searchScope = structPattern->getMembers(); + else if (auto unionPattern = dynamic_cast(indexPattern.get())) + searchScope = unionPattern->getMembers(); + else if (auto bitfieldPattern = dynamic_cast(indexPattern.get())) + searchScope = bitfieldPattern->getFields(); + else if (auto dynamicArrayPattern = dynamic_cast(indexPattern.get())) + searchScope = dynamicArrayPattern->getEntries(); + else if (auto staticArrayPattern = dynamic_cast(indexPattern.get())) + searchScope = { staticArrayPattern->getTemplate() }; + } + + if (currPattern == nullptr) + LogConsole::abortEvaluation("cannot reference global scope", this); + + return hex::moveToVector>(std::move(currPattern)); + } + + private: + Path m_path; + + void readVariable(Evaluator *evaluator, auto &value, Pattern *variablePattern) const { + constexpr bool isString = std::same_as, 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 &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()); + } + }; + +} \ No newline at end of file diff --git a/lib/libimhex/include/hex/pattern_language/ast/ast_node_scope_resolution.hpp b/lib/libimhex/include/hex/pattern_language/ast/ast_node_scope_resolution.hpp new file mode 100644 index 000000000..df375b73f --- /dev/null +++ b/lib/libimhex/include/hex/pattern_language/ast/ast_node_scope_resolution.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include + +namespace hex::pl { + + class ASTNodeScopeResolution : public ASTNode { + public: + explicit ASTNodeScopeResolution(std::unique_ptr &&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 clone() const override { + return std::unique_ptr(new ASTNodeScopeResolution(*this)); + } + + [[nodiscard]] std::unique_ptr evaluate(Evaluator *evaluator) const override { + auto type = this->m_type->evaluate(evaluator); + + if (auto enumType = dynamic_cast(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 m_type; + std::string m_name; + }; + +} \ No newline at end of file diff --git a/lib/libimhex/include/hex/pattern_language/ast/ast_node_struct.hpp b/lib/libimhex/include/hex/pattern_language/ast/ast_node_struct.hpp new file mode 100644 index 000000000..9ca36c305 --- /dev/null +++ b/lib/libimhex/include/hex/pattern_language/ast/ast_node_struct.hpp @@ -0,0 +1,73 @@ +#pragma once + +#include +#include + +#include + +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 clone() const override { + return std::unique_ptr(new ASTNodeStruct(*this)); + } + + [[nodiscard]] std::vector> createPatterns(Evaluator *evaluator) const override { + auto pattern = std::make_unique(evaluator, evaluator->dataOffset(), 0); + + u64 startOffset = evaluator->dataOffset(); + std::vector> 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(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::move(pattern)); + } + + [[nodiscard]] const std::vector> &getMembers() const { return this->m_members; } + void addMember(std::shared_ptr &&node) { this->m_members.push_back(std::move(node)); } + + [[nodiscard]] const std::vector> &getInheritance() const { return this->m_inheritance; } + void addInheritance(std::shared_ptr &&node) { this->m_inheritance.push_back(std::move(node)); } + + private: + std::vector> m_members; + std::vector> m_inheritance; + }; + +} \ No newline at end of file diff --git a/lib/libimhex/include/hex/pattern_language/ast/ast_node_ternary_expression.hpp b/lib/libimhex/include/hex/pattern_language/ast/ast_node_ternary_expression.hpp new file mode 100644 index 000000000..aecfe1171 --- /dev/null +++ b/lib/libimhex/include/hex/pattern_language/ast/ast_node_ternary_expression.hpp @@ -0,0 +1,58 @@ +#pragma once + +#include + +namespace hex::pl { + + class ASTNodeTernaryExpression : public ASTNode { + public: + ASTNodeTernaryExpression(std::unique_ptr &&first, std::unique_ptr &&second, std::unique_ptr &&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 clone() const override { + return std::unique_ptr(new ASTNodeTernaryExpression(*this)); + } + + [[nodiscard]] std::unique_ptr 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(firstNode.get()); + auto *second = dynamic_cast(secondNode.get()); + auto *third = dynamic_cast(thirdNode.get()); + + auto condition = std::visit(overloaded { + [](const std::string &value) -> bool { return !value.empty(); }, + [this](const std::shared_ptr &) -> bool { LogConsole::abortEvaluation("cannot cast custom type to bool", this); }, + [](auto &&value) -> bool { return bool(value); } }, + first->getValue()); + + return std::visit(overloaded { + [condition](const T &second, const T &third) -> std::unique_ptr { return std::unique_ptr(new ASTNodeLiteral(condition ? second : third)); }, + [this](auto &&second, auto &&third) -> std::unique_ptr { LogConsole::abortEvaluation("operands to ternary expression have different types", this); } }, + second->getValue(), + third->getValue()); + } + + [[nodiscard]] const std::unique_ptr &getFirstOperand() const { return this->m_first; } + [[nodiscard]] const std::unique_ptr &getSecondOperand() const { return this->m_second; } + [[nodiscard]] const std::unique_ptr &getThirdOperand() const { return this->m_third; } + [[nodiscard]] Token::Operator getOperator() const { return this->m_operator; } + + private: + std::unique_ptr m_first, m_second, m_third; + Token::Operator m_operator; + }; + +} \ No newline at end of file diff --git a/lib/libimhex/include/hex/pattern_language/ast/ast_node_type_decl.hpp b/lib/libimhex/include/hex/pattern_language/ast/ast_node_type_decl.hpp new file mode 100644 index 000000000..39b35c4a3 --- /dev/null +++ b/lib/libimhex/include/hex/pattern_language/ast/ast_node_type_decl.hpp @@ -0,0 +1,75 @@ +#pragma once + +#include +#include + +namespace hex::pl { + + class ASTNodeTypeDecl : public ASTNode, + public Attributable { + public: + ASTNodeTypeDecl(std::string name, std::shared_ptr &&type, std::optional 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 clone() const override { + return std::unique_ptr(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 &getType() { return this->m_type; } + [[nodiscard]] std::optional getEndian() const { return this->m_endian; } + + [[nodiscard]] std::unique_ptr evaluate(Evaluator *evaluator) const override { + auto type = this->m_type->evaluate(evaluator); + + if (auto attributable = dynamic_cast(type.get())) { + for (auto &attribute : this->getAttributes()) { + if (auto node = dynamic_cast(attribute.get())) + attributable->addAttribute(std::unique_ptr(node)); + } + } + + return type; + } + + [[nodiscard]] std::vector> 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 &&attribute) override { + if (auto attributable = dynamic_cast(this->m_type.get()); attributable != nullptr) { + attributable->addAttribute(std::unique_ptr(static_cast(attribute->clone().release()))); + } + + Attributable::addAttribute(std::move(attribute)); + } + + private: + std::string m_name; + std::shared_ptr m_type; + std::optional m_endian; + }; + +} \ No newline at end of file diff --git a/lib/libimhex/include/hex/pattern_language/ast/ast_node_type_operator.hpp b/lib/libimhex/include/hex/pattern_language/ast/ast_node_type_operator.hpp new file mode 100644 index 000000000..febe5eb15 --- /dev/null +++ b/lib/libimhex/include/hex/pattern_language/ast/ast_node_type_operator.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include + +namespace hex::pl { + + class ASTNodeTypeOperator : public ASTNode { + public: + ASTNodeTypeOperator(Token::Operator op, std::unique_ptr &&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 clone() const override { + return std::unique_ptr(new ASTNodeTypeOperator(*this)); + } + + [[nodiscard]] Token::Operator getOperator() const { + return this->m_op; + } + + [[nodiscard]] const std::unique_ptr &getExpression() const { + return this->m_expression; + } + + [[nodiscard]] std::unique_ptr 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(new ASTNodeLiteral(u128(pattern->getOffset()))); + case Token::Operator::SizeOf: + return std::unique_ptr(new ASTNodeLiteral(u128(pattern->getSize()))); + default: + LogConsole::abortEvaluation("invalid type operator", this); + } + } + + + private: + Token::Operator m_op; + std::unique_ptr m_expression; + }; + +} \ No newline at end of file diff --git a/lib/libimhex/include/hex/pattern_language/ast/ast_node_union.hpp b/lib/libimhex/include/hex/pattern_language/ast/ast_node_union.hpp new file mode 100644 index 000000000..736cf89e8 --- /dev/null +++ b/lib/libimhex/include/hex/pattern_language/ast/ast_node_union.hpp @@ -0,0 +1,61 @@ +#pragma once + +#include +#include + +#include + +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 clone() const override { + return std::unique_ptr(new ASTNodeUnion(*this)); + } + + [[nodiscard]] std::vector> createPatterns(Evaluator *evaluator) const override { + auto pattern = std::make_unique(evaluator, evaluator->dataOffset(), 0); + + size_t size = 0; + std::vector> 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::move(pattern)); + } + + [[nodiscard]] const std::vector> &getMembers() const { return this->m_members; } + void addMember(std::shared_ptr &&node) { this->m_members.push_back(std::move(node)); } + + private: + std::vector> m_members; + }; + +} \ No newline at end of file diff --git a/lib/libimhex/include/hex/pattern_language/ast/ast_node_variable_decl.hpp b/lib/libimhex/include/hex/pattern_language/ast/ast_node_variable_decl.hpp new file mode 100644 index 000000000..75c5404bd --- /dev/null +++ b/lib/libimhex/include/hex/pattern_language/ast/ast_node_variable_decl.hpp @@ -0,0 +1,80 @@ +#pragma once + +#include +#include +#include + +namespace hex::pl { + + class ASTNodeVariableDecl : public ASTNode, + public Attributable { + public: + ASTNodeVariableDecl(std::string name, std::unique_ptr &&type, std::unique_ptr &&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 clone() const override { + return std::unique_ptr(new ASTNodeVariableDecl(*this)); + } + + [[nodiscard]] const std::string &getName() const { return this->m_name; } + [[nodiscard]] constexpr const std::unique_ptr &getType() const { return this->m_type; } + [[nodiscard]] constexpr const std::unique_ptr &getPlacementOffset() const { return this->m_placementOffset; } + + [[nodiscard]] constexpr bool isInVariable() const { return this->m_inVariable; } + [[nodiscard]] constexpr bool isOutVariable() const { return this->m_outVariable; } + + [[nodiscard]] std::vector> createPatterns(Evaluator *evaluator) const override { + u64 startOffset = evaluator->dataOffset(); + + if (this->m_placementOffset != nullptr) { + const auto node = this->m_placementOffset->evaluate(evaluator); + const auto offset = dynamic_cast(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 &) -> 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::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 m_type; + std::unique_ptr m_placementOffset; + + bool m_inVariable = false, m_outVariable = false; + }; + +} \ No newline at end of file diff --git a/lib/libimhex/include/hex/pattern_language/ast/ast_node_while_statement.hpp b/lib/libimhex/include/hex/pattern_language/ast/ast_node_while_statement.hpp new file mode 100644 index 000000000..827a52645 --- /dev/null +++ b/lib/libimhex/include/hex/pattern_language/ast/ast_node_while_statement.hpp @@ -0,0 +1,107 @@ +#pragma once + +#include + +namespace hex::pl { + + class ASTNodeWhileStatement : public ASTNode { + public: + explicit ASTNodeWhileStatement(std::unique_ptr &&condition, std::vector> &&body, std::unique_ptr &&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 clone() const override { + return std::unique_ptr(new ASTNodeWhileStatement(*this)); + } + + [[nodiscard]] const std::unique_ptr &getCondition() { + return this->m_condition; + } + + [[nodiscard]] const std::vector> &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(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 m_condition; + std::vector> m_body; + std::unique_ptr m_postExpression; + }; + +} \ No newline at end of file diff --git a/lib/libimhex/include/hex/pattern_language/ast_node.hpp b/lib/libimhex/include/hex/pattern_language/ast_node.hpp deleted file mode 100644 index 9a9598f17..000000000 --- a/lib/libimhex/include/hex/pattern_language/ast_node.hpp +++ /dev/null @@ -1,2548 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace hex::pl { - - class PatternData; - class Evaluator; - - class ASTNode : public Cloneable { - 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 ASTNode *evaluate(Evaluator *evaluator) const { return this->clone(); } - - [[nodiscard]] virtual std::vector createPatterns(Evaluator *evaluator) const { return {}; } - - using FunctionResult = std::optional; - virtual FunctionResult execute(Evaluator *evaluator) const { LogConsole::abortEvaluation("cannot execute non-function statement", this); } - - private: - u32 m_lineNumber = 1; - }; - - class ASTNodeAttribute : public ASTNode { - public: - explicit ASTNodeAttribute(std::string attribute, std::optional 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]] ASTNode *clone() const override { - return new ASTNodeAttribute(*this); - } - - [[nodiscard]] const std::string &getAttribute() const { - return this->m_attribute; - } - - [[nodiscard]] const std::optional &getValue() const { - return this->m_value; - } - - private: - std::string m_attribute; - std::optional m_value; - }; - - class Attributable { - protected: - Attributable() = default; - ~Attributable() { - for (auto &attribute : this->m_attributes) - delete attribute; - } - - Attributable(const Attributable &other) { - for (auto &attribute : other.m_attributes) { - if (auto node = dynamic_cast(attribute->clone())) - this->m_attributes.push_back(node); - else - delete node; - } - } - - public: - virtual void addAttribute(ASTNodeAttribute *attribute) { - this->m_attributes.push_back(attribute); - } - - [[nodiscard]] const auto &getAttributes() const { - return this->m_attributes; - } - - bool hasAttribute(const std::string &key, bool needsParameter) const { - return std::any_of(this->m_attributes.begin(), this->m_attributes.end(), [&](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 getAttributeValue(const std::string &key) const { - auto attribute = std::find_if(this->m_attributes.begin(), this->m_attributes.end(), [&](ASTNodeAttribute *attribute) { - return attribute->getAttribute() == key; - }); - - if (attribute != this->m_attributes.end()) - return (*attribute)->getValue(); - else - return std::nullopt; - } - - private: - std::vector m_attributes; - }; - - - inline void applyTypeAttributes(Evaluator *evaluator, const ASTNode *node, PatternData *pattern) { - auto attributable = dynamic_cast(node); - if (attributable == nullptr) - LogConsole::abortEvaluation("attribute cannot be applied here", node); - - if (attributable->hasAttribute("inline", false)) { - auto inlinable = dynamic_cast(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(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(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 value = attributable->getAttributeValue("color"); value) { - u32 color = strtoul(value->c_str(), nullptr, 16); - pattern->setColor(hex::changeEndianess(color, std::endian::big) >> 8); - } else if (auto value = attributable->hasAttribute("single_color", false); value) { - pattern->setColor(ContentRegistry::PatternLanguage::getNextColor()); - } - } - } - - inline void applyVariableAttributes(Evaluator *evaluator, const ASTNode *node, PatternData *pattern) { - auto attributable = dynamic_cast(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 value = attributable->getAttributeValue("color"); value) { - u32 color = strtoul(value->c_str(), nullptr, 16); - pattern->setColor(hex::changeEndianess(color, std::endian::big) >> 8); - } else if (auto value = attributable->hasAttribute("single_color", false); value) { - 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(); - } - } - - - class ASTNodeLiteral : public ASTNode { - public: - explicit ASTNodeLiteral(Token::Literal literal) : ASTNode(), m_literal(std::move(literal)) { } - - ASTNodeLiteral(const ASTNodeLiteral &) = default; - - [[nodiscard]] ASTNode *clone() const override { - return new ASTNodeLiteral(*this); - } - - [[nodiscard]] const auto &getValue() const { - return this->m_literal; - } - - private: - Token::Literal m_literal; - }; - - class ASTNodeMathematicalExpression : public ASTNode { -#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 - - 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(ASTNode *left, ASTNode *right, Token::Operator op) - : ASTNode(), m_left(left), m_right(right), m_operator(op) { } - - ~ASTNodeMathematicalExpression() override { - delete this->m_left; - delete this->m_right; - } - - 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]] ASTNode *clone() const override { - return new ASTNodeMathematicalExpression(*this); - } - - [[nodiscard]] 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 *left = dynamic_cast(this->getLeftOperand()->evaluate(evaluator)); - auto *right = dynamic_cast(this->getRightOperand()->evaluate(evaluator)); - ON_SCOPE_EXIT { - delete left; - delete right; - }; - - return std::visit(overloaded { - // TODO: :notlikethis: - [this](u128 left, PatternData *const &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); }, - [this](i128 left, PatternData *const &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); }, - [this](double left, PatternData *const &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); }, - [this](char left, PatternData *const &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); }, - [this](bool left, PatternData *const &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); }, - [this](const std::string &left, PatternData *const &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); }, - [this](PatternData *const &left, u128 right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); }, - [this](PatternData *const &left, i128 right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); }, - [this](PatternData *const &left, double right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); }, - [this](PatternData *const &left, char right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); }, - [this](PatternData *const &left, bool right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); }, - [this](PatternData *const &left, const std::string &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); }, - [this](PatternData *const &left, PatternData *const &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]] ASTNode *getLeftOperand() const { return this->m_left; } - [[nodiscard]] ASTNode *getRightOperand() const { return this->m_right; } - [[nodiscard]] Token::Operator getOperator() const { return this->m_operator; } - - private: - ASTNode *m_left, *m_right; - Token::Operator m_operator; - }; - - class ASTNodeTernaryExpression : public ASTNode { - public: - ASTNodeTernaryExpression(ASTNode *first, ASTNode *second, ASTNode *third, Token::Operator op) - : ASTNode(), m_first(first), m_second(second), m_third(third), m_operator(op) { } - - ~ASTNodeTernaryExpression() override { - delete this->m_first; - delete this->m_second; - delete this->m_third; - } - - 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]] ASTNode *clone() const override { - return new ASTNodeTernaryExpression(*this); - } - - [[nodiscard]] 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 *first = dynamic_cast(this->getFirstOperand()->evaluate(evaluator)); - auto *second = dynamic_cast(this->getSecondOperand()->evaluate(evaluator)); - auto *third = dynamic_cast(this->getThirdOperand()->evaluate(evaluator)); - ON_SCOPE_EXIT { - delete first; - delete second; - delete third; - }; - - auto condition = std::visit(overloaded { - [](const std::string &value) -> bool { return !value.empty(); }, - [this](PatternData *const &) -> bool { LogConsole::abortEvaluation("cannot cast custom type to bool", this); }, - [](auto &&value) -> bool { return bool(value); } }, - first->getValue()); - - return std::visit(overloaded { - [condition](const T &second, const T &third) -> ASTNode * { return new ASTNodeLiteral(condition ? second : third); }, - [this](auto &&second, auto &&third) -> ASTNode * { LogConsole::abortEvaluation("operands to ternary expression have different types", this); } }, - second->getValue(), - third->getValue()); - } - - [[nodiscard]] ASTNode *getFirstOperand() const { return this->m_first; } - [[nodiscard]] ASTNode *getSecondOperand() const { return this->m_second; } - [[nodiscard]] ASTNode *getThirdOperand() const { return this->m_third; } - [[nodiscard]] Token::Operator getOperator() const { return this->m_operator; } - - private: - ASTNode *m_first, *m_second, *m_third; - Token::Operator m_operator; - }; - - 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]] ASTNode *clone() const override { - return new ASTNodeBuiltinType(*this); - } - - [[nodiscard]] std::vector createPatterns(Evaluator *evaluator) const override { - auto offset = evaluator->dataOffset(); - auto size = Token::getTypeSize(this->m_type); - - evaluator->dataOffset() += size; - - PatternData *pattern; - if (Token::isUnsigned(this->m_type)) - pattern = new PatternDataUnsigned(evaluator, offset, size); - else if (Token::isSigned(this->m_type)) - pattern = new PatternDataSigned(evaluator, offset, size); - else if (Token::isFloatingPoint(this->m_type)) - pattern = new PatternDataFloat(evaluator, offset, size); - else if (this->m_type == Token::ValueType::Boolean) - pattern = new PatternDataBoolean(evaluator, offset); - else if (this->m_type == Token::ValueType::Character) - pattern = new PatternDataCharacter(evaluator, offset); - else if (this->m_type == Token::ValueType::Character16) - pattern = new PatternDataCharacter16(evaluator, offset); - else if (this->m_type == Token::ValueType::Padding) - pattern = new PatternDataPadding(evaluator, offset, 1); - else if (this->m_type == Token::ValueType::String) - pattern = new PatternDataString(evaluator, offset, 1); - else if (this->m_type == Token::ValueType::Auto) - return { nullptr }; - else - LogConsole::abortEvaluation("invalid built-in type", this); - - pattern->setTypeName(Token::getTypeName(this->m_type)); - - return { pattern }; - } - - private: - const Token::ValueType m_type; - }; - - class ASTNodeTypeDecl : public ASTNode, - public Attributable { - public: - ASTNodeTypeDecl(std::string name, ASTNode *type, std::optional endian = std::nullopt) - : ASTNode(), m_name(std::move(name)), m_type(type), m_endian(endian) { } - - ASTNodeTypeDecl(const ASTNodeTypeDecl &other) : ASTNode(other), Attributable(other) { - this->m_name = other.m_name; - this->m_type = other.m_type; - this->m_endian = other.m_endian; - } - - [[nodiscard]] ASTNode *clone() const override { - return new ASTNodeTypeDecl(*this); - } - - void setName(const std::string &name) { this->m_name = name; } - [[nodiscard]] const std::string &getName() const { return this->m_name; } - [[nodiscard]] ASTNode *getType() { return this->m_type; } - [[nodiscard]] std::optional getEndian() const { return this->m_endian; } - - [[nodiscard]] ASTNode *evaluate(Evaluator *evaluator) const override { - auto type = this->m_type->evaluate(evaluator); - - if (auto attributable = dynamic_cast(type)) { - for (auto &attribute : this->getAttributes()) { - if (auto node = dynamic_cast(attribute->clone())) - attributable->addAttribute(node); - else - delete node; - } - } - - return type; - } - - [[nodiscard]] std::vector 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); - } - - return patterns; - } - - void addAttribute(ASTNodeAttribute *attribute) override { - Attributable::addAttribute(attribute); - - if (auto attributable = dynamic_cast(this->m_type); attributable != nullptr) - attributable->addAttribute(static_cast(attribute->clone())); - } - - private: - std::string m_name; - ASTNode *m_type; - std::optional m_endian; - }; - - class ASTNodeCast : public ASTNode { - public: - ASTNodeCast(ASTNode *value, ASTNode *type) : m_value(value), m_type(type) { } - ASTNodeCast(const ASTNodeCast &other) : ASTNode(other) { - this->m_value = other.m_value->clone(); - this->m_type = other.m_type->clone(); - } - - ~ASTNodeCast() override { - delete this->m_value; - delete this->m_type; - } - - [[nodiscard]] ASTNode *clone() const override { - return new ASTNodeCast(*this); - } - - [[nodiscard]] ASTNode *evaluate(Evaluator *evaluator) const override { - auto literal = dynamic_cast(this->m_value->evaluate(evaluator)); - auto type = dynamic_cast(this->m_type->evaluate(evaluator))->getType(); - - auto startOffset = evaluator->dataOffset(); - - auto typePattern = this->m_type->createPatterns(evaluator).front(); - ON_SCOPE_EXIT { - evaluator->dataOffset() = startOffset; - delete typePattern; - }; - - return std::visit(overloaded { - [&, this](PatternData *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: - ASTNode *m_value; - ASTNode *m_type; - }; - - class ASTNodeWhileStatement : public ASTNode { - public: - explicit ASTNodeWhileStatement(ASTNode *condition, std::vector body, ASTNode *postExpression = nullptr) - : ASTNode(), m_condition(condition), m_body(std::move(body)), m_postExpression(postExpression) { } - - ~ASTNodeWhileStatement() override { - delete this->m_condition; - - for (auto &statement : this->m_body) - delete statement; - - delete this->m_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]] ASTNode *clone() const override { - return new ASTNodeWhileStatement(*this); - } - - [[nodiscard]] ASTNode *getCondition() { - return this->m_condition; - } - - [[nodiscard]] const std::vector &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--; - delete variables[i]; - } - 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 { - auto literal = dynamic_cast(this->m_condition->evaluate(evaluator)); - ON_SCOPE_EXIT { delete literal; }; - - return std::visit(overloaded { - [](const std::string &value) -> bool { return !value.empty(); }, - [this](PatternData *const &) -> bool { LogConsole::abortEvaluation("cannot cast custom type to bool", this); }, - [](auto &&value) -> bool { return value != 0; } }, - literal->getValue()); - } - - private: - ASTNode *m_condition; - std::vector m_body; - ASTNode *m_postExpression; - }; - - class ASTNodeVariableDecl : public ASTNode, - public Attributable { - public: - ASTNodeVariableDecl(std::string name, ASTNode *type, ASTNode *placementOffset = nullptr, bool inVariable = false, bool outVariable = false) - : ASTNode(), m_name(std::move(name)), m_type(type), m_placementOffset(placementOffset), m_inVariable(inVariable), m_outVariable(outVariable) { } - - ASTNodeVariableDecl(const ASTNodeVariableDecl &other) : ASTNode(other), Attributable(other) { - this->m_name = other.m_name; - 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; - } - - ~ASTNodeVariableDecl() override { - delete this->m_type; - delete this->m_placementOffset; - } - - [[nodiscard]] ASTNode *clone() const override { - return new ASTNodeVariableDecl(*this); - } - - [[nodiscard]] const std::string &getName() const { return this->m_name; } - [[nodiscard]] constexpr ASTNode *getType() const { return this->m_type; } - [[nodiscard]] constexpr auto getPlacementOffset() const { return this->m_placementOffset; } - - [[nodiscard]] constexpr bool isInVariable() const { return this->m_inVariable; } - [[nodiscard]] constexpr bool isOutVariable() const { return this->m_outVariable; } - - [[nodiscard]] std::vector createPatterns(Evaluator *evaluator) const override { - u64 startOffset = evaluator->dataOffset(); - - if (this->m_placementOffset != nullptr) { - auto offset = dynamic_cast(this->m_placementOffset->evaluate(evaluator)); - ON_SCOPE_EXIT { delete offset; }; - - evaluator->dataOffset() = std::visit(overloaded { - [this](const std::string &) -> u64 { LogConsole::abortEvaluation("placement offset cannot be a string", this); }, - [this](PatternData *const &) -> u64 { LogConsole::abortEvaluation("placement offset cannot be a custom type", this); }, - [](auto &&offset) -> u64 { return offset; } }, - offset->getValue()); - } - - auto pattern = this->m_type->createPatterns(evaluator).front(); - pattern->setVariableName(this->m_name); - - applyVariableAttributes(evaluator, this, pattern); - - if (this->m_placementOffset != nullptr && !evaluator->isGlobalScope()) { - evaluator->dataOffset() = startOffset; - } - - return { pattern }; - } - - FunctionResult execute(Evaluator *evaluator) const override { - evaluator->createVariable(this->getName(), this->getType()); - - return std::nullopt; - } - - private: - std::string m_name; - ASTNode *m_type; - ASTNode *m_placementOffset; - - bool m_inVariable = false, m_outVariable = false; - }; - - class ASTNodeArrayVariableDecl : public ASTNode, - public Attributable { - public: - ASTNodeArrayVariableDecl(std::string name, ASTNode *type, ASTNode *size, ASTNode *placementOffset = nullptr) - : ASTNode(), m_name(std::move(name)), m_type(type), m_size(size), m_placementOffset(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; - } - - ~ASTNodeArrayVariableDecl() override { - delete this->m_type; - delete this->m_size; - delete this->m_placementOffset; - } - - [[nodiscard]] ASTNode *clone() const override { - return new ASTNodeArrayVariableDecl(*this); - } - - [[nodiscard]] std::vector createPatterns(Evaluator *evaluator) const override { - auto startOffset = evaluator->dataOffset(); - - if (this->m_placementOffset != nullptr) { - auto offset = dynamic_cast(this->m_placementOffset->evaluate(evaluator)); - ON_SCOPE_EXIT { delete offset; }; - - evaluator->dataOffset() = std::visit(overloaded { - [this](const std::string &) -> u64 { LogConsole::abortEvaluation("placement offset cannot be a string", this); }, - [this](PatternData *const &) -> 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); - ON_SCOPE_EXIT { delete type; }; - - PatternData *pattern; - if (dynamic_cast(type)) - pattern = createStaticArray(evaluator); - else if (auto attributable = dynamic_cast(type)) { - 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); - - if (this->m_placementOffset != nullptr && !evaluator->isGlobalScope()) { - evaluator->dataOffset() = startOffset; - } - - return { pattern }; - } - - [[nodiscard]] const std::string &getName() const { return this->m_name; } - [[nodiscard]] constexpr ASTNode *getType() const { return this->m_type; } - [[nodiscard]] constexpr ASTNode *getSize() const { return this->m_size; } - [[nodiscard]] constexpr auto getPlacementOffset() const { return this->m_placementOffset; } - - private: - std::string m_name; - ASTNode *m_type; - ASTNode *m_size; - ASTNode *m_placementOffset; - - PatternData *createStaticArray(Evaluator *evaluator) const { - u64 startOffset = evaluator->dataOffset(); - - PatternData *templatePattern = this->m_type->createPatterns(evaluator).front(); - ON_SCOPE_EXIT { delete templatePattern; }; - - evaluator->dataOffset() = startOffset; - - i128 entryCount = 0; - - if (this->m_size != nullptr) { - auto sizeNode = this->m_size->evaluate(evaluator); - ON_SCOPE_EXIT { delete sizeNode; }; - - if (auto literal = dynamic_cast(sizeNode)) { - entryCount = std::visit(overloaded { - [this](const std::string &) -> u128 { LogConsole::abortEvaluation("cannot use string to index array", this); }, - [this](PatternData *) -> u128 { LogConsole::abortEvaluation("cannot use custom type to index array", this); }, - [](auto &&size) -> u128 { return size; } }, - literal->getValue()); - } else if (auto whileStatement = dynamic_cast(sizeNode)) { - 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 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(); - } - } - - PatternData *outputPattern; - if (dynamic_cast(templatePattern)) { - outputPattern = new PatternDataPadding(evaluator, startOffset, 0); - } else if (dynamic_cast(templatePattern)) { - outputPattern = new PatternDataString(evaluator, startOffset, 0); - } else if (dynamic_cast(templatePattern)) { - outputPattern = new PatternDataString16(evaluator, startOffset, 0); - } else { - auto arrayPattern = new PatternDataStaticArray(evaluator, startOffset, 0); - arrayPattern->setEntries(templatePattern->clone(), entryCount); - outputPattern = 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; - } - - PatternData *createDynamicArray(Evaluator *evaluator) const { - auto arrayPattern = new PatternDataDynamicArray(evaluator, evaluator->dataOffset(), 0); - arrayPattern->setVariableName(this->m_name); - - std::vector entries; - auto arrayCleanup = SCOPE_GUARD { - for (auto entry : entries) - delete entry; - }; - - size_t size = 0; - u64 entryIndex = 0; - - auto addEntries = [&](const std::vector &patterns) { - for (auto pattern : patterns) { - pattern->setVariableName(hex::format("[{}]", entryIndex)); - pattern->setEndian(arrayPattern->getEndian()); - entries.push_back(pattern); - - size += pattern->getSize(); - entryIndex++; - - evaluator->handleAbort(); - } - }; - - auto discardEntries = [&](u32 count) { - for (u32 i = 0; i < count; i++) { - delete entries.back(); - entries.pop_back(); - entryIndex--; - } - }; - - if (this->m_size != nullptr) { - auto sizeNode = this->m_size->evaluate(evaluator); - ON_SCOPE_EXIT { delete sizeNode; }; - - if (auto literal = dynamic_cast(sizeNode)) { - auto entryCount = std::visit(overloaded { - [this](const std::string &) -> u128 { LogConsole::abortEvaluation("cannot use string to index array", this); }, - [this](PatternData *) -> 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); - - if (!patterns.empty()) - addEntries(patterns); - - auto ctrlFlow = evaluator->getCurrentControlFlowStatement(); - if (ctrlFlow == ControlFlowStatement::Break) - break; - else if (ctrlFlow == ControlFlowStatement::Continue) { - - discardEntries(patterns.size()); - continue; - } - } - } else if (auto whileStatement = dynamic_cast(sizeNode)) { - 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); - - if (!patterns.empty()) - addEntries(patterns); - - auto ctrlFlow = evaluator->getCurrentControlFlowStatement(); - if (ctrlFlow == ControlFlowStatement::Break) - break; - else if (ctrlFlow == ControlFlowStatement::Continue) { - discardEntries(patterns.size()); - 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 buffer(pattern->getSize()); - - if (evaluator->dataOffset() >= evaluator->getProvider()->getActualSize() - buffer.size()) { - delete pattern; - LogConsole::abortEvaluation("reached end of file before finding end of unsized array", this); - } - - addEntries({ pattern }); - - auto ctrlFlow = evaluator->getCurrentControlFlowStatement(); - if (ctrlFlow == ControlFlowStatement::None) - break; - - evaluator->getProvider()->read(evaluator->dataOffset() - pattern->getSize(), 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(entries); - arrayPattern->setSize(size); - - if (auto &entries = arrayPattern->getEntries(); !entries.empty()) - arrayPattern->setTypeName(entries.front()->getTypeName()); - - arrayCleanup.release(); - - return arrayPattern; - } - }; - - class ASTNodePointerVariableDecl : public ASTNode, - public Attributable { - public: - ASTNodePointerVariableDecl(std::string name, ASTNode *type, ASTNode *sizeType, ASTNode *placementOffset = nullptr) - : ASTNode(), m_name(std::move(name)), m_type(type), m_sizeType(sizeType), m_placementOffset(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; - } - - ~ASTNodePointerVariableDecl() override { - delete this->m_type; - delete this->m_sizeType; - delete this->m_placementOffset; - } - - [[nodiscard]] ASTNode *clone() const override { - return new ASTNodePointerVariableDecl(*this); - } - - [[nodiscard]] const std::string &getName() const { return this->m_name; } - [[nodiscard]] constexpr ASTNode *getType() const { return this->m_type; } - [[nodiscard]] constexpr ASTNode *getSizeType() const { return this->m_sizeType; } - [[nodiscard]] constexpr auto getPlacementOffset() const { return this->m_placementOffset; } - - [[nodiscard]] std::vector createPatterns(Evaluator *evaluator) const override { - auto startOffset = evaluator->dataOffset(); - - if (this->m_placementOffset != nullptr) { - auto offset = dynamic_cast(this->m_placementOffset->evaluate(evaluator)); - ON_SCOPE_EXIT { delete offset; }; - - evaluator->dataOffset() = std::visit(overloaded { - [this](const std::string &) -> u64 { LogConsole::abortEvaluation("placement offset cannot be a string", this); }, - [this](PatternData *) -> u64 { LogConsole::abortEvaluation("placement offset cannot be a custom type", this); }, - [](auto &&offset) -> u64 { return u64(offset); } }, - offset->getValue()); - } - - auto pointerStartOffset = evaluator->dataOffset(); - - auto sizePattern = this->m_sizeType->createPatterns(evaluator).front(); - ON_SCOPE_EXIT { delete sizePattern; }; - - auto pattern = new PatternDataPointer(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); - - evaluator->dataOffset() = pattern->getPointedAtAddress(); - - auto pointedAtPattern = this->m_type->createPatterns(evaluator).front(); - - pattern->setPointedAtPattern(pointedAtPattern); - pattern->setEndian(sizePattern->getEndian()); - } - - if (this->m_placementOffset != nullptr && !evaluator->isGlobalScope()) { - evaluator->dataOffset() = startOffset; - } else { - evaluator->dataOffset() = pointerEndOffset; - } - - return { pattern }; - } - - private: - std::string m_name; - ASTNode *m_type; - ASTNode *m_sizeType; - ASTNode *m_placementOffset; - }; - - class ASTNodeMultiVariableDecl : public ASTNode { - public: - explicit ASTNodeMultiVariableDecl(std::vector 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()); - } - - ~ASTNodeMultiVariableDecl() override { - for (auto &variable : this->m_variables) - delete variable; - } - - [[nodiscard]] ASTNode *clone() const override { - return new ASTNodeMultiVariableDecl(*this); - } - - [[nodiscard]] std::vector getVariables() { - return this->m_variables; - } - - [[nodiscard]] std::vector createPatterns(Evaluator *evaluator) const override { - std::vector patterns; - - for (auto &node : this->m_variables) { - auto newPatterns = node->createPatterns(evaluator); - patterns.insert(patterns.end(), newPatterns.begin(), newPatterns.end()); - } - - return patterns; - } - - FunctionResult execute(Evaluator *evaluator) const override { - for (auto &variable : this->m_variables) { - auto variableDecl = dynamic_cast(variable); - - evaluator->createVariable(variableDecl->getName(), variableDecl->getType()->evaluate(evaluator)); - } - - return std::nullopt; - } - - private: - std::vector m_variables; - }; - - 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()); - } - - ~ASTNodeStruct() override { - for (auto &member : this->m_members) - delete member; - for (auto &inheritance : this->m_inheritance) - delete inheritance; - } - - [[nodiscard]] ASTNode *clone() const override { - return new ASTNodeStruct(*this); - } - - [[nodiscard]] std::vector createPatterns(Evaluator *evaluator) const override { - auto pattern = new PatternDataStruct(evaluator, evaluator->dataOffset(), 0); - - u64 startOffset = evaluator->dataOffset(); - std::vector memberPatterns; - auto structCleanup = SCOPE_GUARD { - delete pattern; - for (auto member : memberPatterns) - delete member; - }; - - evaluator->pushScope(pattern, memberPatterns); - ON_SCOPE_EXIT { - evaluator->popScope(); - }; - - for (auto inheritance : this->m_inheritance) { - auto inheritancePatterns = inheritance->createPatterns(evaluator).front(); - ON_SCOPE_EXIT { - delete inheritancePatterns; - }; - - if (auto structPattern = dynamic_cast(inheritancePatterns)) { - 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(memberPattern); - } - } - - pattern->setMembers(memberPatterns); - pattern->setSize(evaluator->dataOffset() - startOffset); - - structCleanup.release(); - - applyTypeAttributes(evaluator, this, pattern); - - return { pattern }; - } - - [[nodiscard]] const std::vector &getMembers() const { return this->m_members; } - void addMember(ASTNode *node) { this->m_members.push_back(node); } - - [[nodiscard]] const std::vector &getInheritance() const { return this->m_inheritance; } - void addInheritance(ASTNode *node) { this->m_inheritance.push_back(node); } - - private: - std::vector m_members; - std::vector m_inheritance; - }; - - 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()); - } - - ~ASTNodeUnion() override { - for (auto &member : this->m_members) - delete member; - } - - [[nodiscard]] ASTNode *clone() const override { - return new ASTNodeUnion(*this); - } - - [[nodiscard]] std::vector createPatterns(Evaluator *evaluator) const override { - auto pattern = new PatternDataUnion(evaluator, evaluator->dataOffset(), 0); - - size_t size = 0; - std::vector memberPatterns; - u64 startOffset = evaluator->dataOffset(); - - auto unionCleanup = SCOPE_GUARD { - delete pattern; - for (auto member : memberPatterns) - delete member; - }; - - evaluator->pushScope(pattern, memberPatterns); - ON_SCOPE_EXIT { - evaluator->popScope(); - }; - - for (auto member : this->m_members) { - for (auto &memberPattern : member->createPatterns(evaluator)) { - memberPattern->setOffset(startOffset); - memberPatterns.push_back(memberPattern); - size = std::max(memberPattern->getSize(), size); - } - } - - evaluator->dataOffset() = startOffset + size; - pattern->setMembers(memberPatterns); - pattern->setSize(size); - - applyTypeAttributes(evaluator, this, pattern); - - unionCleanup.release(); - - return { pattern }; - } - - [[nodiscard]] const std::vector &getMembers() const { return this->m_members; } - void addMember(ASTNode *node) { this->m_members.push_back(node); } - - private: - std::vector m_members; - }; - - class ASTNodeEnum : public ASTNode, - public Attributable { - public: - explicit ASTNodeEnum(ASTNode *underlyingType) : ASTNode(), m_underlyingType(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(); - } - - ~ASTNodeEnum() override { - for (auto &[name, expr] : this->m_entries) - delete expr; - delete this->m_underlyingType; - } - - [[nodiscard]] ASTNode *clone() const override { - return new ASTNodeEnum(*this); - } - - [[nodiscard]] std::vector createPatterns(Evaluator *evaluator) const override { - auto pattern = new PatternDataEnum(evaluator, evaluator->dataOffset(), 0); - auto enumCleanup = SCOPE_GUARD { delete pattern; }; - - - std::vector> enumEntries; - for (const auto &[name, value] : this->m_entries) { - auto literal = dynamic_cast(value->evaluate(evaluator)); - ON_SCOPE_EXIT { delete literal; }; - - enumEntries.emplace_back(literal->getValue(), name); - } - - pattern->setEnumValues(enumEntries); - - auto underlying = this->m_underlyingType->createPatterns(evaluator).front(); - ON_SCOPE_EXIT { delete underlying; }; - pattern->setSize(underlying->getSize()); - pattern->setEndian(underlying->getEndian()); - - applyTypeAttributes(evaluator, this, pattern); - - enumCleanup.release(); - - return { pattern }; - } - - [[nodiscard]] const std::map &getEntries() const { return this->m_entries; } - void addEntry(const std::string &name, ASTNode *expression) { this->m_entries.insert({ name, expression }); } - - [[nodiscard]] ASTNode *getUnderlyingType() { return this->m_underlyingType; } - - private: - std::map m_entries; - ASTNode *m_underlyingType; - }; - - 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()); - } - - ~ASTNodeBitfield() override { - for (auto &[name, expr] : this->m_entries) - delete expr; - } - - [[nodiscard]] ASTNode *clone() const override { - return new ASTNodeBitfield(*this); - } - - [[nodiscard]] const std::vector> &getEntries() const { return this->m_entries; } - void addEntry(const std::string &name, ASTNode *size) { this->m_entries.emplace_back(name, size); } - - [[nodiscard]] std::vector createPatterns(Evaluator *evaluator) const override { - auto pattern = new PatternDataBitfield(evaluator, evaluator->dataOffset(), 0); - - size_t bitOffset = 0; - std::vector fields; - - auto bitfieldCleanup = SCOPE_GUARD { - delete pattern; - for (auto field : fields) - delete field; - }; - - bool isLeftToRight = false; - if (this->hasAttribute("left_to_right", false)) - isLeftToRight = true; - else if (this->hasAttribute("right_to_left", false)) - isLeftToRight = false; - - auto entries = this->m_entries; - if (isLeftToRight) - std::reverse(entries.begin(), entries.end()); - - evaluator->pushScope(pattern, fields); - ON_SCOPE_EXIT { - evaluator->popScope(); - }; - - for (auto [name, bitSizeNode] : entries) { - auto literal = bitSizeNode->evaluate(evaluator); - ON_SCOPE_EXIT { delete literal; }; - - u8 bitSize = std::visit(overloaded { - [this](const std::string &) -> u8 { LogConsole::abortEvaluation("bitfield field size cannot be a string", this); }, - [this](PatternData *) -> u8 { LogConsole::abortEvaluation("bitfield field size cannot be a custom type", this); }, - [](auto &&offset) -> u8 { return static_cast(offset); } }, - dynamic_cast(literal)->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 = new PatternDataBitfieldField(evaluator, evaluator->dataOffset(), bitOffset, bitSize, pattern); - field->setVariableName(name); - - fields.push_back(field); - } - - bitOffset += bitSize; - } - - pattern->setSize((bitOffset + 7) / 8); - pattern->setFields(fields); - - evaluator->dataOffset() += pattern->getSize(); - - applyTypeAttributes(evaluator, this, pattern); - - bitfieldCleanup.release(); - - return { pattern }; - } - - private: - std::vector> m_entries; - }; - - class ASTNodeParameterPack : public ASTNode { - public: - explicit ASTNodeParameterPack(std::vector values) : m_values(std::move(values)) { } - - [[nodiscard]] ASTNode *clone() const override { - return new ASTNodeParameterPack(*this); - } - - [[nodiscard]] const std::vector &getValues() const { - return this->m_values; - } - - private: - std::vector m_values; - }; - - class ASTNodeRValue : public ASTNode { - public: - using Path = std::vector>; - - explicit ASTNodeRValue(Path path) : ASTNode(), m_path(std::move(path)) { } - - ASTNodeRValue(const ASTNodeRValue &other) { - for (auto &part : other.m_path) { - if (auto stringPart = std::get_if(&part); stringPart != nullptr) - this->m_path.push_back(*stringPart); - else if (auto nodePart = std::get_if(&part); nodePart != nullptr) - this->m_path.push_back((*nodePart)->clone()); - } - } - - ~ASTNodeRValue() override { - for (auto &part : this->m_path) { - if (auto node = std::get_if(&part); node != nullptr) - delete *node; - } - } - - [[nodiscard]] ASTNode *clone() const override { - return new ASTNodeRValue(*this); - } - - [[nodiscard]] const Path &getPath() const { - return this->m_path; - } - - [[nodiscard]] ASTNode *evaluate(Evaluator *evaluator) const override { - if (this->getPath().size() == 1) { - if (auto name = std::get_if(&this->getPath().front()); name != nullptr) { - if (*name == "$") return new ASTNodeLiteral(u128(evaluator->dataOffset())); - - auto ¶meterPack = evaluator->getScope(0).parameterPack; - if (parameterPack && *name == parameterPack->name) - return new ASTNodeParameterPack(parameterPack->values); - } - } - - auto pattern = this->createPatterns(evaluator).front(); - ON_SCOPE_EXIT { delete pattern; }; - - Token::Literal literal; - if (dynamic_cast(pattern) || dynamic_cast(pattern)) { - u128 value = 0; - readVariable(evaluator, value, pattern); - literal = value; - } else if (dynamic_cast(pattern)) { - i128 value = 0; - readVariable(evaluator, value, pattern); - value = hex::signExtend(pattern->getSize() * 8, value); - literal = value; - } else if (dynamic_cast(pattern)) { - if (pattern->getSize() == sizeof(u16)) { - u16 value = 0; - readVariable(evaluator, value, pattern); - literal = double(float16ToFloat32(value)); - } else if (pattern->getSize() == sizeof(float)) { - float value = 0; - readVariable(evaluator, value, pattern); - literal = double(value); - } else if (pattern->getSize() == sizeof(double)) { - double value = 0; - readVariable(evaluator, value, pattern); - literal = value; - } else LogConsole::abortEvaluation("invalid floating point type access", this); - } else if (dynamic_cast(pattern)) { - char value = 0; - readVariable(evaluator, value, pattern); - literal = value; - } else if (dynamic_cast(pattern)) { - bool value = false; - readVariable(evaluator, value, pattern); - literal = value; - } else if (dynamic_cast(pattern)) { - std::string value; - - if (pattern->isLocal()) { - auto &literal = evaluator->getStack()[pattern->getOffset()]; - - std::visit(overloaded { - [&](char assignmentValue) { if (assignmentValue != 0x00) value = std::string({ assignmentValue }); }, - [&](std::string assignmentValue) { value = assignmentValue; }, - [&, this](PatternData *const &assignmentValue) { - if (!dynamic_cast(assignmentValue) && !dynamic_cast(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); } }, - literal); - } 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(pattern)) { - u64 value = 0; - readVariable(evaluator, value, pattern); - 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, { literal }); - - if (!result.has_value()) - LogConsole::abortEvaluation("transform function did not return a value", this); - literal = result.value(); - } - - return new ASTNodeLiteral(literal); - } - - [[nodiscard]] std::vector createPatterns(Evaluator *evaluator) const override { - std::vector searchScope; - PatternData *currPattern = nullptr; - 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(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) { - auto newPattern = (*iter)->clone(); - delete currPattern; - currPattern = newPattern; - 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 index = dynamic_cast(std::get(part)->evaluate(evaluator)); - ON_SCOPE_EXIT { delete index; }; - - std::visit(overloaded { - [this](const std::string &) { LogConsole::abortEvaluation("cannot use string to index array", this); }, - [this](PatternData *const &) { LogConsole::abortEvaluation("cannot use custom type to index array", this); }, - [&, this](auto &&index) { - if (auto dynamicArrayPattern = dynamic_cast(currPattern)) { - if (index >= searchScope.size() || index < 0) - LogConsole::abortEvaluation("array index out of bounds", this); - - auto newPattern = searchScope[index]->clone(); - delete currPattern; - currPattern = newPattern; - } else if (auto staticArrayPattern = dynamic_cast(currPattern)) { - 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()); - delete currPattern; - currPattern = newPattern; - } - } }, - index->getValue()); - } - - if (currPattern == nullptr) - break; - - if (auto pointerPattern = dynamic_cast(currPattern)) { - auto newPattern = pointerPattern->getPointedAtPattern()->clone(); - delete currPattern; - currPattern = newPattern; - } - - PatternData *indexPattern; - if (currPattern->isLocal()) { - auto stackLiteral = evaluator->getStack()[currPattern->getOffset()]; - if (auto stackPattern = std::get_if(&stackLiteral); stackPattern != nullptr) - indexPattern = *stackPattern; - else - return { currPattern }; - } else - indexPattern = currPattern; - - if (auto structPattern = dynamic_cast(indexPattern)) - searchScope = structPattern->getMembers(); - else if (auto unionPattern = dynamic_cast(indexPattern)) - searchScope = unionPattern->getMembers(); - else if (auto bitfieldPattern = dynamic_cast(indexPattern)) - searchScope = bitfieldPattern->getFields(); - else if (auto dynamicArrayPattern = dynamic_cast(indexPattern)) - searchScope = dynamicArrayPattern->getEntries(); - else if (auto staticArrayPattern = dynamic_cast(indexPattern)) - searchScope = { staticArrayPattern->getTemplate() }; - } - - if (currPattern == nullptr) - LogConsole::abortEvaluation("cannot reference global scope", this); - - return { currPattern }; - } - - private: - Path m_path; - - void readVariable(Evaluator *evaluator, auto &value, PatternData *variablePattern) const { - constexpr bool isString = std::same_as, std::string>; - - if (variablePattern->isLocal()) { - auto &literal = evaluator->getStack()[variablePattern->getOffset()]; - - std::visit(overloaded { - [&](std::string &assignmentValue) { - if constexpr (isString) value = assignmentValue; - }, - [&](PatternData *assignmentValue) { readVariable(evaluator, value, assignmentValue); }, - [&](auto &&assignmentValue) { value = assignmentValue; } }, - literal); - } else { - if constexpr (isString) { - value.resize(variablePattern->getSize()); - evaluator->getProvider()->read(variablePattern->getOffset(), value.data(), value.size()); - value.erase(std::find(value.begin(), value.end(), '\0'), value.end()); - } else { - evaluator->getProvider()->read(variablePattern->getOffset(), &value, variablePattern->getSize()); - } - } - - if constexpr (!isString) - value = hex::changeEndianess(value, variablePattern->getSize(), variablePattern->getEndian()); - } - }; - - class ASTNodeScopeResolution : public ASTNode { - public: - explicit ASTNodeScopeResolution(ASTNode *type, std::string name) : ASTNode(), m_type(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; - } - - ~ASTNodeScopeResolution() override { - delete this->m_type; - } - - [[nodiscard]] ASTNode *clone() const override { - return new ASTNodeScopeResolution(*this); - } - - [[nodiscard]] ASTNode *evaluate(Evaluator *evaluator) const override { - auto type = this->m_type->evaluate(evaluator); - ON_SCOPE_EXIT { delete type; }; - - if (auto enumType = dynamic_cast(type)) { - 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: - ASTNode *m_type; - std::string m_name; - }; - - class ASTNodeConditionalStatement : public ASTNode { - public: - explicit ASTNodeConditionalStatement(ASTNode *condition, std::vector trueBody, std::vector falseBody) - : ASTNode(), m_condition(condition), m_trueBody(std::move(trueBody)), m_falseBody(std::move(falseBody)) { } - - ~ASTNodeConditionalStatement() override { - delete this->m_condition; - - for (auto &statement : this->m_trueBody) - delete statement; - for (auto &statement : this->m_falseBody) - delete statement; - } - - 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]] ASTNode *clone() const override { - return new ASTNodeConditionalStatement(*this); - } - - [[nodiscard]] std::vector 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(pattern); - } - } - - return {}; - } - - [[nodiscard]] 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--; - delete variables[i]; - } - 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 { - auto literal = dynamic_cast(this->m_condition->evaluate(evaluator)); - ON_SCOPE_EXIT { delete literal; }; - - return std::visit(overloaded { - [](const std::string &value) -> bool { return !value.empty(); }, - [this](PatternData *const &) -> bool { LogConsole::abortEvaluation("cannot cast custom type to bool", this); }, - [](auto &&value) -> bool { return value != 0; } }, - literal->getValue()); - } - - ASTNode *m_condition; - std::vector m_trueBody, m_falseBody; - }; - - class ASTNodeFunctionCall : public ASTNode { - public: - explicit ASTNodeFunctionCall(std::string functionName, std::vector params) - : ASTNode(), m_functionName(std::move(functionName)), m_params(std::move(params)) { } - - ~ASTNodeFunctionCall() override { - for (auto ¶m : this->m_params) - delete param; - } - - ASTNodeFunctionCall(const ASTNodeFunctionCall &other) : ASTNode(other) { - this->m_functionName = other.m_functionName; - - for (auto ¶m : other.m_params) - this->m_params.push_back(param->clone()); - } - - [[nodiscard]] ASTNode *clone() const override { - return new ASTNodeFunctionCall(*this); - } - - [[nodiscard]] const std::string &getFunctionName() { - return this->m_functionName; - } - - [[nodiscard]] const std::vector &getParams() const { - return this->m_params; - } - - [[nodiscard]] std::vector createPatterns(Evaluator *evaluator) const override { - - this->execute(evaluator); - - return {}; - } - - [[nodiscard]] ASTNode *evaluate(Evaluator *evaluator) const override { - std::vector evaluatedParams; - for (auto param : this->m_params) { - auto expression = param->evaluate(evaluator); - ON_SCOPE_EXIT { delete expression; }; - - if (auto literal = dynamic_cast(expression->evaluate(evaluator))) { - evaluatedParams.push_back(literal->getValue()); - delete literal; - } else if (auto parameterPack = dynamic_cast(expression->evaluate(evaluator))) { - for (auto &value : parameterPack->getValues()) { - evaluatedParams.push_back(value); - } - - delete parameterPack; - } - } - - 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 { - auto &function = functions[this->m_functionName]; - - 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 new ASTNodeLiteral(result.value()); - else - return new ASTNodeMathematicalExpression(nullptr, nullptr, Token::Operator::Plus); - } catch (std::string &error) { - LogConsole::abortEvaluation(error, this); - } - - return nullptr; - } - - FunctionResult execute(Evaluator *evaluator) const override { - delete this->evaluate(evaluator); - - return {}; - } - - private: - std::string m_functionName; - std::vector m_params; - }; - - class ASTNodeTypeOperator : public ASTNode { - public: - ASTNodeTypeOperator(Token::Operator op, ASTNode *expression) : m_op(op), m_expression(expression) { - } - - ASTNodeTypeOperator(const ASTNodeTypeOperator &other) : ASTNode(other) { - this->m_op = other.m_op; - this->m_expression = other.m_expression->clone(); - } - - [[nodiscard]] ASTNode *clone() const override { - return new ASTNodeTypeOperator(*this); - } - - ~ASTNodeTypeOperator() override { - delete this->m_expression; - } - - [[nodiscard]] Token::Operator getOperator() const { - return this->m_op; - } - - [[nodiscard]] ASTNode *getExpression() const { - return this->m_expression; - } - - [[nodiscard]] ASTNode *evaluate(Evaluator *evaluator) const override { - auto pattern = this->m_expression->createPatterns(evaluator).front(); - ON_SCOPE_EXIT { delete pattern; }; - - switch (this->getOperator()) { - case Token::Operator::AddressOf: - return new ASTNodeLiteral(u128(pattern->getOffset())); - case Token::Operator::SizeOf: - return new ASTNodeLiteral(u128(pattern->getSize())); - default: - LogConsole::abortEvaluation("invalid type operator", this); - } - } - - - private: - Token::Operator m_op; - ASTNode *m_expression; - }; - - - class ASTNodeAssignment : public ASTNode { - public: - ASTNodeAssignment(std::string lvalueName, ASTNode *rvalue) : m_lvalueName(std::move(lvalueName)), m_rvalue(rvalue) { - } - - ASTNodeAssignment(const ASTNodeAssignment &other) : ASTNode(other) { - this->m_lvalueName = other.m_lvalueName; - this->m_rvalue = other.m_rvalue->clone(); - } - - [[nodiscard]] ASTNode *clone() const override { - return new ASTNodeAssignment(*this); - } - - ~ASTNodeAssignment() override { - delete this->m_rvalue; - } - - [[nodiscard]] const std::string &getLValueName() const { - return this->m_lvalueName; - } - - [[nodiscard]] ASTNode *getRValue() const { - return this->m_rvalue; - } - - [[nodiscard]] std::vector createPatterns(Evaluator *evaluator) const override { - this->execute(evaluator); - - return {}; - } - - FunctionResult execute(Evaluator *evaluator) const override { - auto literal = dynamic_cast(this->getRValue()->evaluate(evaluator)); - ON_SCOPE_EXIT { delete literal; }; - - if (this->getLValueName() == "$") - evaluator->dataOffset() = Token::literalToUnsigned(literal->getValue()); - else - evaluator->setVariable(this->getLValueName(), literal->getValue()); - - return {}; - } - - private: - std::string m_lvalueName; - ASTNode *m_rvalue; - }; - - class ASTNodeControlFlowStatement : public ASTNode { - public: - explicit ASTNodeControlFlowStatement(ControlFlowStatement type, ASTNode *rvalue) : m_type(type), m_rvalue(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]] ASTNode *clone() const override { - return new ASTNodeControlFlowStatement(*this); - } - - ~ASTNodeControlFlowStatement() override { - delete this->m_rvalue; - } - - [[nodiscard]] ASTNode *getReturnValue() const { - return this->m_rvalue; - } - - [[nodiscard]] std::vector createPatterns(Evaluator *evaluator) const override { - - this->execute(evaluator); - - return {}; - } - - FunctionResult execute(Evaluator *evaluator) const override { - auto returnValue = this->getReturnValue(); - - auto literal = dynamic_cast(returnValue->evaluate(evaluator)); - ON_SCOPE_EXIT { delete literal; }; - - evaluator->setCurrentControlFlowStatement(this->m_type); - - if (literal == nullptr) - return std::nullopt; - else - return literal->getValue(); - } - - private: - ControlFlowStatement m_type; - ASTNode *m_rvalue; - }; - - class ASTNodeFunctionDefinition : public ASTNode { - public: - ASTNodeFunctionDefinition(std::string name, std::vector> params, std::vector body, std::optional 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; - this->m_params = other.m_params; - - 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]] ASTNode *clone() const override { - return new ASTNodeFunctionDefinition(*this); - } - - ~ASTNodeFunctionDefinition() override { - for (auto &[name, type] : this->m_params) - delete type; - for (auto statement : this->m_body) - delete statement; - } - - [[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]] 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 ¶ms) -> std::optional { - std::vector variables; - - ctx->pushScope(nullptr, variables); - ON_SCOPE_EXIT { - for (auto variable : variables) - delete variable; - - ctx->popScope(); - }; - - if (this->m_parameterPack.has_value()) { - std::vector 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, 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> m_params; - std::vector m_body; - std::optional m_parameterPack; - }; - - class ASTNodeCompoundStatement : public ASTNode { - public: - explicit ASTNodeCompoundStatement(std::vector 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]] ASTNode *clone() const override { - return new ASTNodeCompoundStatement(*this); - } - - ~ASTNodeCompoundStatement() override { - for (const auto &statement : this->m_statements) { - delete statement; - } - } - - [[nodiscard]] ASTNode *evaluate(Evaluator *evaluator) const override { - ASTNode *result = nullptr; - - for (const auto &statement : this->m_statements) { - delete result; - result = statement->evaluate(evaluator); - } - - return result; - } - - [[nodiscard]] std::vector createPatterns(Evaluator *evaluator) const override { - std::vector result; - - for (const auto &statement : this->m_statements) { - auto patterns = statement->createPatterns(evaluator); - std::copy(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--; - delete variables[i]; - } - if (stackSize < 0) LogConsole::abortEvaluation("stack pointer underflow!", this); - evaluator->getStack().resize(stackSize); - - evaluator->popScope(); - } - - return result; - } - - public: - std::vector m_statements; - bool m_newScope = false; - }; - -}; diff --git a/lib/libimhex/include/hex/pattern_language/evaluator.hpp b/lib/libimhex/include/hex/pattern_language/evaluator.hpp index 7f9ec8b46..d29701c94 100644 --- a/lib/libimhex/include/hex/pattern_language/evaluator.hpp +++ b/lib/libimhex/include/hex/pattern_language/evaluator.hpp @@ -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> evaluate(const std::vector &ast); + std::optional>> evaluate(const std::vector> &ast); [[nodiscard]] LogConsole &getConsole() { return this->m_console; @@ -54,11 +54,11 @@ namespace hex::pl { }; struct Scope { - PatternData *parent; - std::vector *scope; + Pattern *parent; + std::vector> *scope; std::optional parameterPack; }; - void pushScope(PatternData *parent, std::vector &scope) { + void pushScope(Pattern *parent, std::vector> &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 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 getMainResult() { + [[nodiscard]] const std::optional &getMainResult() { return this->m_mainResult; } @@ -257,7 +258,7 @@ namespace hex::pl { std::vector m_scopes; std::map m_customFunctions; - std::vector m_customFunctionDefinitions; + std::vector> m_customFunctionDefinitions; std::vector m_stack; std::optional m_mainResult; diff --git a/lib/libimhex/include/hex/pattern_language/log_console.hpp b/lib/libimhex/include/hex/pattern_language/log_console.hpp index 443be1a31..fd824183f 100644 --- a/lib/libimhex/include/hex/pattern_language/log_console.hpp +++ b/lib/libimhex/include/hex/pattern_language/log_console.hpp @@ -8,6 +8,8 @@ #include #include +#include + #include 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 + [[noreturn]] static void abortEvaluation(const std::string &message, const std::unique_ptr &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; } diff --git a/lib/libimhex/include/hex/pattern_language/parser.hpp b/lib/libimhex/include/hex/pattern_language/parser.hpp index dacfa7152..b388b2f28 100644 --- a/lib/libimhex/include/hex/pattern_language/parser.hpp +++ b/lib/libimhex/include/hex/pattern_language/parser.hpp @@ -5,7 +5,11 @@ #include #include -#include + +#include +#include +#include +#include #include #include @@ -21,7 +25,7 @@ namespace hex::pl { Parser() = default; ~Parser() = default; - std::optional> parse(const std::vector &tokens); + std::optional>> parse(const std::vector &tokens); const std::optional &getError() { return this->m_error; } private: @@ -29,7 +33,7 @@ namespace hex::pl { TokenIter m_curr; TokenIter m_originalPosition, m_partOriginalPosition; - std::unordered_map m_types; + std::unordered_map> m_types; std::vector m_matchedOptionals; std::vector> m_currNamespace; @@ -37,9 +41,10 @@ namespace hex::pl { return this->m_curr[index].lineNumber; } - auto *create(auto *node) { + template + std::unique_ptr create(T *node) { node->setLineNumber(this->getLineNumber(-1)); - return node; + return std::unique_ptr(node); } template @@ -67,79 +72,73 @@ namespace hex::pl { return result; } - ASTNode *parseFunctionCall(); - ASTNode *parseStringLiteral(); + std::unique_ptr parseFunctionCall(); + std::unique_ptr 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 parseScopeResolution(); + std::unique_ptr parseRValue(); + std::unique_ptr parseRValue(ASTNodeRValue::Path &path); + std::unique_ptr parseFactor(); + std::unique_ptr parseCastExpression(); + std::unique_ptr parseUnaryExpression(); + std::unique_ptr parseMultiplicativeExpression(); + std::unique_ptr parseAdditiveExpression(); + std::unique_ptr parseShiftExpression(); + std::unique_ptr parseBinaryAndExpression(); + std::unique_ptr parseBinaryXorExpression(); + std::unique_ptr parseBinaryOrExpression(); + std::unique_ptr parseBooleanAnd(); + std::unique_ptr parseBooleanXor(); + std::unique_ptr parseBooleanOr(); + std::unique_ptr parseRelationExpression(); + std::unique_ptr parseEqualityExpression(); + std::unique_ptr parseTernaryConditional(); + std::unique_ptr parseMathematicalExpression(); - ASTNode *parseFunctionDefinition(); - ASTNode *parseFunctionVariableDecl(); - ASTNode *parseFunctionStatement(); - ASTNode *parseFunctionVariableAssignment(const std::string &lvalue); - ASTNode *parseFunctionVariableCompoundAssignment(const std::string &lvalue); - ASTNode *parseFunctionControlFlowStatement(); - std::vector parseStatementBody(); - ASTNode *parseFunctionConditional(); - ASTNode *parseFunctionWhileLoop(); - ASTNode *parseFunctionForLoop(); + std::unique_ptr parseFunctionDefinition(); + std::unique_ptr parseFunctionVariableDecl(); + std::unique_ptr parseFunctionStatement(); + std::unique_ptr parseFunctionVariableAssignment(const std::string &lvalue); + std::unique_ptr parseFunctionVariableCompoundAssignment(const std::string &lvalue); + std::unique_ptr parseFunctionControlFlowStatement(); + std::vector> parseStatementBody(); + std::unique_ptr parseFunctionConditional(); + std::unique_ptr parseFunctionWhileLoop(); + std::unique_ptr 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 parseNamespace(); - std::vector parseStatements(); + std::unique_ptr parseConditional(); + std::unique_ptr parseWhileStatement(); + std::unique_ptr parseType(bool allowFunctionTypes = false); + std::shared_ptr parseUsingDeclaration(); + std::unique_ptr parsePadding(); + std::unique_ptr parseMemberVariable(std::unique_ptr &&type); + std::unique_ptr parseMemberArrayVariable(std::unique_ptr &&type); + std::unique_ptr parseMemberPointerVariable(std::unique_ptr &&type); + std::unique_ptr parseMember(); + std::shared_ptr parseStruct(); + std::shared_ptr parseUnion(); + std::shared_ptr parseEnum(); + std::shared_ptr parseBitfield(); + std::unique_ptr parseVariablePlacement(std::unique_ptr &&type); + std::unique_ptr parseArrayVariablePlacement(std::unique_ptr &&type); + std::unique_ptr parsePointerVariablePlacement(std::unique_ptr &&type); + std::unique_ptr parsePlacement(); + std::vector> parseNamespace(); + std::vector> parseStatements(); - ASTNodeTypeDecl *addType(const std::string &name, ASTNode *node, std::optional endian = std::nullopt); + std::shared_ptr addType(const std::string &name, std::unique_ptr &&node, std::optional endian = std::nullopt); - std::vector parseTillToken(Token::Type endTokenType, const auto value) { - std::vector program; - auto guard = SCOPE_GUARD { - for (auto &node : program) - delete node; - }; + std::vector> parseTillToken(Token::Type endTokenType, const auto value) { + std::vector> 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; } diff --git a/lib/libimhex/include/hex/pattern_language/pattern_data.hpp b/lib/libimhex/include/hex/pattern_language/pattern_data.hpp deleted file mode 100644 index 470c459ca..000000000 --- a/lib/libimhex/include/hex/pattern_language/pattern_data.hpp +++ /dev/null @@ -1,1672 +0,0 @@ -#pragma once - -#include - -#include - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -namespace hex::pl { - - using namespace ::std::literals::string_literals; - - namespace { - - std::string makeDisplayable(const std::string &string) { - std::string result; - for (auto &c : string) { - if (iscntrl(c) || c > 0x7F) - result += " "; - else - result += c; - } - - if (string.length() != 0 && string.back() == '\x00') - result.pop_back(); - - return result; - } - - } - - 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 PatternData : public PatternCreationLimiter, - public Cloneable { - public: - PatternData(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; - } - - PatternData(const PatternData &other) = default; - - ~PatternData() 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 &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 PatternData *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 &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 sortPatternDataTable(ImGuiTableSortSpecs *sortSpecs, prv::Provider *provider, pl::PatternData *left, pl::PatternData *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 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 PatternData &other) const final { return !operator==(other); } - [[nodiscard]] virtual bool operator==(const PatternData &other) const = 0; - - template - [[nodiscard]] bool areCommonPropertiesEqual(const PatternData &other) const { - return typeid(other) == typeid(std::remove_cvref_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(&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("##PatternDataLine", 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 m_endian; - bool m_hidden = false; - - private: - u64 m_offset = 0x00; - size_t m_size = 0x00; - - u32 m_color = 0x00; - std::optional m_displayName; - std::string m_variableName; - std::optional m_comment; - std::string m_typeName; - - std::optional m_formatterFunction; - std::optional m_transformFunction; - - bool m_local = false; - bool m_manualColor = false; - }; - - class PatternDataPadding : public PatternData { - public: - PatternDataPadding(Evaluator *evaluator, u64 offset, size_t size) : PatternData(evaluator, offset, size, 0xFF000000) { } - - [[nodiscard]] PatternData *clone() const override { - return new PatternDataPadding(*this); - } - - void createEntry(prv::Provider *&provider) override { - } - - [[nodiscard]] std::string getFormattedName() const override { - return ""; - } - - [[nodiscard]] bool operator==(const PatternData &other) const override { return areCommonPropertiesEqual(other); } - }; - - class PatternDataPointer : public PatternData, - public Inlinable { - public: - PatternDataPointer(Evaluator *evaluator, u64 offset, size_t size, u32 color = 0) - : PatternData(evaluator, offset, size, color), m_pointedAt(nullptr) { - } - - PatternDataPointer(const PatternDataPointer &other) : PatternData(other) { - this->m_pointedAt = other.m_pointedAt->clone(); - } - - ~PatternDataPointer() override { - delete this->m_pointedAt; - } - - [[nodiscard]] PatternData *clone() const override { - return new PatternDataPointer(*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(("##PatternDataLine"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 &highlight) const override { - PatternData::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(PatternData *pattern) { - this->m_pointedAt = 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]] PatternData *getPointedAtPattern() { - return this->m_pointedAt; - } - - void setColor(u32 color) override { - PatternData::setColor(color); - this->m_pointedAt->setColor(color); - } - - [[nodiscard]] bool operator==(const PatternData &other) const override { - return areCommonPropertiesEqual(other) && - *static_cast(&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 PatternData *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); - - PatternData::setEndian(endian); - } - - private: - PatternData *m_pointedAt = nullptr; - u64 m_pointedAtAddress = 0; - - u64 m_pointerBase = 0; - }; - - class PatternDataUnsigned : public PatternData { - public: - PatternDataUnsigned(Evaluator *evaluator, u64 offset, size_t size, u32 color = 0) - : PatternData(evaluator, offset, size, color) { } - - [[nodiscard]] PatternData *clone() const override { - return new PatternDataUnsigned(*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 PatternData &other) const override { return areCommonPropertiesEqual(other); } - }; - - class PatternDataSigned : public PatternData { - public: - PatternDataSigned(Evaluator *evaluator, u64 offset, size_t size, u32 color = 0) - : PatternData(evaluator, offset, size, color) { } - - [[nodiscard]] PatternData *clone() const override { - return new PatternDataSigned(*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 PatternData &other) const override { return areCommonPropertiesEqual(other); } - }; - - class PatternDataFloat : public PatternData { - public: - PatternDataFloat(Evaluator *evaluator, u64 offset, size_t size, u32 color = 0) - : PatternData(evaluator, offset, size, color) { } - - [[nodiscard]] PatternData *clone() const override { - return new PatternDataFloat(*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(&data), data, this->getSize() * 2), *reinterpret_cast(&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(&data), data, this->getSize() * 2), *reinterpret_cast(&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 PatternData &other) const override { return areCommonPropertiesEqual(other); } - }; - - class PatternDataBoolean : public PatternData { - public: - explicit PatternDataBoolean(Evaluator *evaluator, u64 offset, u32 color = 0) - : PatternData(evaluator, offset, 1, color) { } - - [[nodiscard]] PatternData *clone() const override { - return new PatternDataBoolean(*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 PatternData &other) const override { return areCommonPropertiesEqual(other); } - }; - - class PatternDataCharacter : public PatternData { - public: - explicit PatternDataCharacter(Evaluator *evaluator, u64 offset, u32 color = 0) - : PatternData(evaluator, offset, 1, color) { } - - [[nodiscard]] PatternData *clone() const override { - return new PatternDataCharacter(*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 PatternData &other) const override { return areCommonPropertiesEqual(other); } - }; - - class PatternDataCharacter16 : public PatternData { - public: - explicit PatternDataCharacter16(Evaluator *evaluator, u64 offset, u32 color = 0) - : PatternData(evaluator, offset, 2, color) { } - - [[nodiscard]] PatternData *clone() const override { - return new PatternDataCharacter16(*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, 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, char16_t> {}.to_bytes(character); - } - - [[nodiscard]] bool operator==(const PatternData &other) const override { return areCommonPropertiesEqual(other); } - }; - - class PatternDataString : public PatternData { - public: - PatternDataString(Evaluator *evaluator, u64 offset, size_t size, u32 color = 0) - : PatternData(evaluator, offset, size, color) { } - - [[nodiscard]] PatternData *clone() const override { - return new PatternDataString(*this); - } - - void createEntry(prv::Provider *&provider) override { - auto size = std::min(this->getSize(), 0x7F); - - if (size == 0) - return; - - std::string buffer(size, 0x00); - - provider->read(this->getOffset(), buffer.data(), size); - - this->createDefaultEntry(hex::format("\"{0}\" {1}", makeDisplayable(buffer), size > this->getSize() ? "(truncated)" : ""), buffer); - } - - [[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 PatternData &other) const override { return areCommonPropertiesEqual(other); } - }; - - class PatternDataString16 : public PatternData { - public: - PatternDataString16(Evaluator *evaluator, u64 offset, size_t size, u32 color = 0) - : PatternData(evaluator, offset, size, color) { } - - [[nodiscard]] PatternData *clone() const override { - return new PatternDataString16(*this); - } - - void createEntry(prv::Provider *&provider) override { - auto size = std::min(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, 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, char16_t> {}.to_bytes(buffer); - } - - [[nodiscard]] bool operator==(const PatternData &other) const override { return areCommonPropertiesEqual(other); } - }; - - class PatternDataDynamicArray : public PatternData, - public Inlinable { - public: - PatternDataDynamicArray(Evaluator *evaluator, u64 offset, size_t size, u32 color = 0) - : PatternData(evaluator, offset, size, color) { - } - - PatternDataDynamicArray(const PatternDataDynamicArray &other) : PatternData(other) { - std::vector entries; - for (const auto &entry : other.m_entries) - entries.push_back(entry->clone()); - - this->setEntries(entries); - } - - ~PatternDataDynamicArray() override { - for (const auto &entry : this->m_entries) - delete entry; - } - - [[nodiscard]] PatternData *clone() const override { - return new PatternDataDynamicArray(*this); - } - - void setColor(u32 color) override { - PatternData::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(("##PatternDataLine"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)); - } - - 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 &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); - - PatternData::setOffset(offset); - } - - [[nodiscard]] const std::vector &getEntries() { - return this->m_entries; - } - - void setEntries(const std::vector &entries) { - this->m_entries = entries; - - if (this->hasOverriddenColor()) { - for (auto &entry : this->m_entries) { - entry->setColor(this->getColor()); - } - } - } - - [[nodiscard]] bool operator==(const PatternData &other) const override { - if (!areCommonPropertiesEqual(other)) - return false; - - auto &otherArray = *static_cast(&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 PatternData *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); - } - - PatternData::setEndian(endian); - } - - private: - std::vector m_entries; - u64 m_displayEnd = 50; - }; - - class PatternDataStaticArray : public PatternData, - public Inlinable { - public: - PatternDataStaticArray(Evaluator *evaluator, u64 offset, size_t size, u32 color = 0) - : PatternData(evaluator, offset, size, color) { - } - - PatternDataStaticArray(const PatternDataStaticArray &other) : PatternData(other) { - this->setEntries(other.getTemplate()->clone(), other.getEntryCount()); - } - - ~PatternDataStaticArray() override { - delete this->m_template; - delete this->m_highlightTemplate; - } - - [[nodiscard]] PatternData *clone() const override { - return new PatternDataStaticArray(*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(("##PatternDataLine"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)); - } - - 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; - } - } - delete entry; - - if (!this->isInlined()) - ImGui::TreePop(); - } else { - this->m_displayEnd = 50; - } - } - - void getHighlightedAddresses(std::map &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); - } - - delete entry; - } - - void setOffset(u64 offset) override { - this->m_template->setOffset(this->m_template->getOffset() - this->getOffset() + offset); - - PatternData::setOffset(offset); - } - - void setColor(u32 color) override { - PatternData::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]] PatternData *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(PatternData *templatePattern, size_t count) { - this->m_template = templatePattern; - this->m_highlightTemplate = this->m_template->clone(); - this->m_entryCount = count; - - if (this->hasOverriddenColor()) this->setColor(this->m_template->getColor()); - this->m_template->setEndian(templatePattern->getEndian()); - } - - [[nodiscard]] bool operator==(const PatternData &other) const override { - if (!areCommonPropertiesEqual(other)) - return false; - - auto &otherArray = *static_cast(&other); - return *this->m_template == *otherArray.m_template && this->m_entryCount == otherArray.m_entryCount; - } - - [[nodiscard]] const PatternData *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); - - PatternData::setEndian(endian); - } - - private: - PatternData *m_template = nullptr; - mutable PatternData *m_highlightTemplate = nullptr; - size_t m_entryCount = 0; - u64 m_displayEnd = 50; - }; - - class PatternDataStruct : public PatternData, - public Inlinable { - public: - PatternDataStruct(Evaluator *evaluator, u64 offset, size_t size, u32 color = 0) - : PatternData(evaluator, offset, size, color) { - } - - PatternDataStruct(const PatternDataStruct &other) : PatternData(other) { - for (const auto &member : other.m_members) - this->m_members.push_back(member->clone()); - this->m_sortedMembers = this->m_members; - } - - ~PatternDataStruct() override { - for (const auto &member : this->m_members) - delete member; - } - - [[nodiscard]] PatternData *clone() const override { - return new PatternDataStruct(*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(("##PatternDataLine"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)); - } - - if (open) { - for (auto &member : this->m_sortedMembers) - member->draw(provider); - - if (!this->isInlined()) - ImGui::TreePop(); - } - } - - void getHighlightedAddresses(std::map &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); - - PatternData::setOffset(offset); - } - - void setColor(u32 color) override { - PatternData::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 = this->m_members; - - std::sort(this->m_sortedMembers.begin(), this->m_sortedMembers.end(), [&sortSpecs, &provider](PatternData *left, PatternData *right) { - return PatternData::sortPatternDataTable(sortSpecs, provider, left, right); - }); - - for (auto &member : this->m_members) - member->sort(sortSpecs, provider); - } - - [[nodiscard]] std::string getFormattedName() const override { - return "struct " + PatternData::getTypeName(); - } - - [[nodiscard]] const auto &getMembers() const { - return this->m_members; - } - - void setMembers(const std::vector &members) { - this->m_members.clear(); - - for (auto &member : members) { - if (member == nullptr) continue; - - this->m_members.push_back(member); - } - - this->m_sortedMembers = this->m_members; - } - - [[nodiscard]] bool operator==(const PatternData &other) const override { - if (!areCommonPropertiesEqual(other)) - return false; - - auto &otherStruct = *static_cast(&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 PatternData *getPattern(u64 offset) const override { - if (this->isHidden()) return nullptr; - - auto iter = std::find_if(this->m_members.begin(), this->m_members.end(), [offset](PatternData *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); - } - - PatternData::setEndian(endian); - } - - private: - std::vector m_members; - std::vector m_sortedMembers; - }; - - class PatternDataUnion : public PatternData, - public Inlinable { - public: - PatternDataUnion(Evaluator *evaluator, u64 offset, size_t size, u32 color = 0) - : PatternData(evaluator, offset, size, color) { - } - - PatternDataUnion(const PatternDataUnion &other) : PatternData(other) { - for (const auto &member : other.m_members) - this->m_members.push_back(member->clone()); - this->m_sortedMembers = this->m_members; - } - - ~PatternDataUnion() override { - for (const auto &member : this->m_members) - delete member; - } - - [[nodiscard]] PatternData *clone() const override { - return new PatternDataUnion(*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(("##PatternDataLine"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(PatternData::getTypeName().c_str()); - - ImGui::TableNextColumn(); - ImGui::TextFormatted("{}", this->formatDisplayValue("{ ... }", this)); - } - - if (open) { - for (auto &member : this->m_sortedMembers) - member->draw(provider); - - if (!this->isInlined()) - ImGui::TreePop(); - } - } - - void getHighlightedAddresses(std::map &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); - - PatternData::setOffset(offset); - } - - void setColor(u32 color) override { - PatternData::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 = this->m_members; - - std::sort(this->m_sortedMembers.begin(), this->m_sortedMembers.end(), [&sortSpecs, &provider](PatternData *left, PatternData *right) { - return PatternData::sortPatternDataTable(sortSpecs, provider, left, right); - }); - - for (auto &member : this->m_members) - member->sort(sortSpecs, provider); - } - - [[nodiscard]] std::string getFormattedName() const override { - return "union " + PatternData::getTypeName(); - ; - } - - [[nodiscard]] const auto &getMembers() const { - return this->m_members; - } - - void setMembers(const std::vector &members) { - this->m_members.clear(); - for (auto &member : members) { - if (member == nullptr) continue; - - this->m_members.push_back(member); - } - - this->m_sortedMembers = this->m_members; - } - - [[nodiscard]] bool operator==(const PatternData &other) const override { - if (!areCommonPropertiesEqual(other)) - return false; - - auto &otherUnion = *static_cast(&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 PatternData *getPattern(u64 offset) const override { - if (this->isHidden()) return nullptr; - - auto largestMember = std::find_if(this->m_members.begin(), this->m_members.end(), [this](PatternData *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); - } - - PatternData::setEndian(endian); - } - - private: - std::vector m_members; - std::vector m_sortedMembers; - }; - - class PatternDataEnum : public PatternData { - public: - PatternDataEnum(Evaluator *evaluator, u64 offset, size_t size, u32 color = 0) - : PatternData(evaluator, offset, size, color) { - } - - [[nodiscard]] PatternData *clone() const override { - return new PatternDataEnum(*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 = PatternData::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; }, - [](PatternData *) { 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(("##PatternDataLine"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(PatternData::getTypeName().c_str()); - ImGui::TableNextColumn(); - ImGui::TextFormatted("{}", this->formatDisplayValue(hex::format("{} (0x{:0{}X})", valueString.c_str(), value, this->getSize() * 2), this)); - } - - [[nodiscard]] std::string getFormattedName() const override { - return "enum " + PatternData::getTypeName(); - } - - [[nodiscard]] const auto &getEnumValues() const { - return this->m_enumValues; - } - - void setEnumValues(const std::vector> &enumValues) { - this->m_enumValues = enumValues; - } - - [[nodiscard]] bool operator==(const PatternData &other) const override { - if (!areCommonPropertiesEqual(other)) - return false; - - auto &otherEnum = *static_cast(&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> m_enumValues; - }; - - - class PatternDataBitfieldField : public PatternData { - public: - PatternDataBitfieldField(Evaluator *evaluator, u64 offset, u8 bitOffset, u8 bitSize, PatternData *bitField, u32 color = 0) - : PatternData(evaluator, offset, 0, color), m_bitOffset(bitOffset), m_bitSize(bitSize), m_bitField(bitField) { - } - - [[nodiscard]] PatternData *clone() const override { - return new PatternDataBitfieldField(*this); - } - - void createEntry(prv::Provider *&provider) override { - std::vector 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)); - } - } - - [[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 PatternData &other) const override { - if (!areCommonPropertiesEqual(other)) - return false; - - auto &otherBitfieldField = *static_cast(&other); - return this->m_bitOffset == otherBitfieldField.m_bitOffset && this->m_bitSize == otherBitfieldField.m_bitSize; - } - - private: - u8 m_bitOffset, m_bitSize; - PatternData *m_bitField; - }; - - class PatternDataBitfield : public PatternData, - public Inlinable { - public: - PatternDataBitfield(Evaluator *evaluator, u64 offset, size_t size, u32 color = 0) - : PatternData(evaluator, offset, size, color) { - } - - PatternDataBitfield(const PatternDataBitfield &other) : PatternData(other) { - for (auto &field : other.m_fields) - this->m_fields.push_back(field->clone()); - } - - ~PatternDataBitfield() override { - for (auto field : this->m_fields) - delete field; - } - - [[nodiscard]] PatternData *clone() const override { - return new PatternDataBitfield(*this); - } - - void createEntry(prv::Provider *&provider) override { - std::vector 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(("##PatternDataLine"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(PatternData::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)); - } - - 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); - - PatternData::setOffset(offset); - } - - [[nodiscard]] std::string getFormattedName() const override { - return "bitfield " + PatternData::getTypeName(); - } - - [[nodiscard]] const auto &getFields() const { - return this->m_fields; - } - - void setFields(const std::vector &fields) { - this->m_fields = fields; - - for (auto &field : this->m_fields) { - field->setSize(this->getSize()); - field->setColor(this->getColor()); - } - } - - void setColor(u32 color) override { - PatternData::setColor(color); - for (auto &field : this->m_fields) - field->setColor(color); - } - - [[nodiscard]] bool operator==(const PatternData &other) const override { - if (!areCommonPropertiesEqual(other)) - return false; - - auto &otherBitfield = *static_cast(&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 m_fields; - }; - -} diff --git a/lib/libimhex/include/hex/pattern_language/pattern_language.hpp b/lib/libimhex/include/hex/pattern_language/pattern_language.hpp index 5833da708..fec8277f8 100644 --- a/lib/libimhex/include/hex/pattern_language/pattern_language.hpp +++ b/lib/libimhex/include/hex/pattern_language/pattern_language.hpp @@ -11,6 +11,7 @@ #include #include +#include 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> parseString(const std::string &code); + [[nodiscard]] std::optional>> parseString(const std::string &code); [[nodiscard]] bool executeString(prv::Provider *provider, const std::string &string, const std::map &envVars = {}, const std::map &inVariables = {}, bool checkResult = true); [[nodiscard]] bool executeFile(prv::Provider *provider, const fs::path &path, const std::map &envVars = {}, const std::map &inVariables = {}); [[nodiscard]] std::pair> executeFunction(prv::Provider *provider, const std::string &code); - [[nodiscard]] const std::vector &getCurrentAST() const; + [[nodiscard]] const std::vector> &getCurrentAST() const; void abort(); @@ -50,8 +51,8 @@ namespace hex::pl { [[nodiscard]] bool hasDangerousFunctionBeenCalled() const; void allowDangerousFunctions(bool allow); - [[nodiscard]] const std::vector &getPatterns() { - const static std::vector empty; + [[nodiscard]] const std::vector> &getPatterns() { + const static std::vector> 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 m_currAST; + std::vector> m_currAST; std::optional m_currError; - std::vector m_patterns; + std::vector> m_patterns; bool m_running = false; }; diff --git a/lib/libimhex/include/hex/pattern_language/patterns/pattern.hpp b/lib/libimhex/include/hex/pattern_language/patterns/pattern.hpp new file mode 100644 index 000000000..061fc4549 --- /dev/null +++ b/lib/libimhex/include/hex/pattern_language/patterns/pattern.hpp @@ -0,0 +1,293 @@ +#pragma once + +#include + +#include +#include + +#include +#include + +#include + +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 { + 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 &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 &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 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 + [[nodiscard]] bool areCommonPropertiesEqual(const Pattern &other) const { + return typeid(other) == typeid(std::remove_cvref_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(&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 m_endian; + bool m_hidden = false; + + private: + u64 m_offset = 0x00; + size_t m_size = 0x00; + + u32 m_color = 0x00; + std::optional m_displayName; + std::string m_variableName; + std::optional m_comment; + std::string m_typeName; + + std::optional m_formatterFunction; + std::optional m_transformFunction; + + bool m_local = false; + bool m_manualColor = false; + }; + +} \ No newline at end of file diff --git a/lib/libimhex/include/hex/pattern_language/patterns/pattern_array_dynamic.hpp b/lib/libimhex/include/hex/pattern_language/patterns/pattern_array_dynamic.hpp new file mode 100644 index 000000000..c9e0b99f9 --- /dev/null +++ b/lib/libimhex/include/hex/pattern_language/patterns/pattern_array_dynamic.hpp @@ -0,0 +1,159 @@ +#pragma once + +#include + +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> entries; + for (const auto &entry : other.m_entries) + entries.push_back(entry->clone()); + + this->setEntries(std::move(entries)); + } + + [[nodiscard]] std::unique_ptr clone() const override { + return std::unique_ptr(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 &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> &&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(other)) + return false; + + auto &otherArray = *static_cast(&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> m_entries; + u64 m_displayEnd = 50; + }; + +} \ No newline at end of file diff --git a/lib/libimhex/include/hex/pattern_language/patterns/pattern_array_static.hpp b/lib/libimhex/include/hex/pattern_language/patterns/pattern_array_static.hpp new file mode 100644 index 000000000..ee74910c6 --- /dev/null +++ b/lib/libimhex/include/hex/pattern_language/patterns/pattern_array_static.hpp @@ -0,0 +1,162 @@ +#pragma once + +#include + +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 clone() const override { + return std::unique_ptr(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 &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 &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 &&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(other)) + return false; + + auto &otherArray = *static_cast(&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 m_template = nullptr; + mutable std::unique_ptr m_highlightTemplate = nullptr; + size_t m_entryCount = 0; + u64 m_displayEnd = 50; + }; + +} \ No newline at end of file diff --git a/lib/libimhex/include/hex/pattern_language/patterns/pattern_bitfield.hpp b/lib/libimhex/include/hex/pattern_language/patterns/pattern_bitfield.hpp new file mode 100644 index 000000000..ec827716b --- /dev/null +++ b/lib/libimhex/include/hex/pattern_language/patterns/pattern_bitfield.hpp @@ -0,0 +1,186 @@ +#pragma once + +#include + +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 clone() const override { + return std::unique_ptr(new PatternBitfieldField(*this)); + } + + void createEntry(prv::Provider *&provider) override { + std::vector 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(other)) + return false; + + auto &otherBitfieldField = *static_cast(&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 clone() const override { + return std::unique_ptr(new PatternBitfield(*this)); + } + + void createEntry(prv::Provider *&provider) override { + std::vector 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> &&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(other)) + return false; + + auto &otherBitfield = *static_cast(&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> m_fields; + }; + +} \ No newline at end of file diff --git a/lib/libimhex/include/hex/pattern_language/patterns/pattern_boolean.hpp b/lib/libimhex/include/hex/pattern_language/patterns/pattern_boolean.hpp new file mode 100644 index 000000000..70a65f92c --- /dev/null +++ b/lib/libimhex/include/hex/pattern_language/patterns/pattern_boolean.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include + +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 clone() const override { + return std::unique_ptr(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(other); } + }; + +} \ No newline at end of file diff --git a/lib/libimhex/include/hex/pattern_language/patterns/pattern_character.hpp b/lib/libimhex/include/hex/pattern_language/patterns/pattern_character.hpp new file mode 100644 index 000000000..ceade03eb --- /dev/null +++ b/lib/libimhex/include/hex/pattern_language/patterns/pattern_character.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include + +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 clone() const override { + return std::unique_ptr(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(other); } + }; + +} \ No newline at end of file diff --git a/lib/libimhex/include/hex/pattern_language/patterns/pattern_enum.hpp b/lib/libimhex/include/hex/pattern_language/patterns/pattern_enum.hpp new file mode 100644 index 000000000..31dbd601f --- /dev/null +++ b/lib/libimhex/include/hex/pattern_language/patterns/pattern_enum.hpp @@ -0,0 +1,101 @@ +#pragma once + +#include + +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 clone() const override { + return std::unique_ptr(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 &) { 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> &enumValues) { + this->m_enumValues = enumValues; + } + + [[nodiscard]] bool operator==(const Pattern &other) const override { + if (!areCommonPropertiesEqual(other)) + return false; + + auto &otherEnum = *static_cast(&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> m_enumValues; + }; + +} \ No newline at end of file diff --git a/lib/libimhex/include/hex/pattern_language/patterns/pattern_float.hpp b/lib/libimhex/include/hex/pattern_language/patterns/pattern_float.hpp new file mode 100644 index 000000000..b00e3d15c --- /dev/null +++ b/lib/libimhex/include/hex/pattern_language/patterns/pattern_float.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include + +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 clone() const override { + return std::unique_ptr(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(&data), data, this->getSize() * 2), *reinterpret_cast(&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(&data), data, this->getSize() * 2), *reinterpret_cast(&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(other); } + }; + +} \ No newline at end of file diff --git a/lib/libimhex/include/hex/pattern_language/patterns/pattern_padding.hpp b/lib/libimhex/include/hex/pattern_language/patterns/pattern_padding.hpp new file mode 100644 index 000000000..534f0c879 --- /dev/null +++ b/lib/libimhex/include/hex/pattern_language/patterns/pattern_padding.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include + +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 clone() const override { + return std::unique_ptr(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(other); } + }; + +} \ No newline at end of file diff --git a/lib/libimhex/include/hex/pattern_language/patterns/pattern_pointer.hpp b/lib/libimhex/include/hex/pattern_language/patterns/pattern_pointer.hpp new file mode 100644 index 000000000..8bd25c9b8 --- /dev/null +++ b/lib/libimhex/include/hex/pattern_language/patterns/pattern_pointer.hpp @@ -0,0 +1,143 @@ +#pragma once + +#include + +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 clone() const override { + return std::unique_ptr(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 &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) { + 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 &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(other) && + *static_cast(&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 m_pointedAt; + u64 m_pointedAtAddress = 0; + + u64 m_pointerBase = 0; + }; + +} \ No newline at end of file diff --git a/lib/libimhex/include/hex/pattern_language/patterns/pattern_signed.hpp b/lib/libimhex/include/hex/pattern_language/patterns/pattern_signed.hpp new file mode 100644 index 000000000..570a8963e --- /dev/null +++ b/lib/libimhex/include/hex/pattern_language/patterns/pattern_signed.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include + +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 clone() const override { + return std::unique_ptr(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(other); } + }; + +} \ No newline at end of file diff --git a/lib/libimhex/include/hex/pattern_language/patterns/pattern_string.hpp b/lib/libimhex/include/hex/pattern_language/patterns/pattern_string.hpp new file mode 100644 index 000000000..ebecfa17f --- /dev/null +++ b/lib/libimhex/include/hex/pattern_language/patterns/pattern_string.hpp @@ -0,0 +1,47 @@ +#pragma once + +#include + +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 clone() const override { + return std::unique_ptr(new PatternString(*this)); + } + + void createEntry(prv::Provider *&provider) override { + auto size = std::min(this->getSize(), 0x7F); + + if (size == 0) + return; + + std::vector 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(other); } + }; + +} \ No newline at end of file diff --git a/lib/libimhex/include/hex/pattern_language/patterns/pattern_struct.hpp b/lib/libimhex/include/hex/pattern_language/patterns/pattern_struct.hpp new file mode 100644 index 000000000..1f1e300b3 --- /dev/null +++ b/lib/libimhex/include/hex/pattern_language/patterns/pattern_struct.hpp @@ -0,0 +1,156 @@ +#pragma once + +#include + +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 clone() const override { + return std::unique_ptr(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 &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> &&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(other)) + return false; + + auto &otherStruct = *static_cast(&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) { + 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> m_members; + std::vector m_sortedMembers; + }; + +} \ No newline at end of file diff --git a/lib/libimhex/include/hex/pattern_language/patterns/pattern_union.hpp b/lib/libimhex/include/hex/pattern_language/patterns/pattern_union.hpp new file mode 100644 index 000000000..acaf57982 --- /dev/null +++ b/lib/libimhex/include/hex/pattern_language/patterns/pattern_union.hpp @@ -0,0 +1,158 @@ +#pragma once + +#include + +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 clone() const override { + return std::unique_ptr(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 &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> &&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(other)) + return false; + + auto &otherUnion = *static_cast(&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) { + 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> m_members; + std::vector m_sortedMembers; + }; + +} \ No newline at end of file diff --git a/lib/libimhex/include/hex/pattern_language/patterns/pattern_unsigned.hpp b/lib/libimhex/include/hex/pattern_language/patterns/pattern_unsigned.hpp new file mode 100644 index 000000000..6ad941ce2 --- /dev/null +++ b/lib/libimhex/include/hex/pattern_language/patterns/pattern_unsigned.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include + +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 clone() const override { + return std::unique_ptr(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(other); } + }; + +} \ No newline at end of file diff --git a/lib/libimhex/include/hex/pattern_language/patterns/pattern_wide_character.hpp b/lib/libimhex/include/hex/pattern_language/patterns/pattern_wide_character.hpp new file mode 100644 index 000000000..13c5d322a --- /dev/null +++ b/lib/libimhex/include/hex/pattern_language/patterns/pattern_wide_character.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include + +#include + +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 clone() const override { + return std::unique_ptr(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, 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, char16_t> {}.to_bytes(character); + } + + [[nodiscard]] bool operator==(const Pattern &other) const override { return areCommonPropertiesEqual(other); } + }; + +} \ No newline at end of file diff --git a/lib/libimhex/include/hex/pattern_language/patterns/pattern_wide_string.hpp b/lib/libimhex/include/hex/pattern_language/patterns/pattern_wide_string.hpp new file mode 100644 index 000000000..8ebae7a6f --- /dev/null +++ b/lib/libimhex/include/hex/pattern_language/patterns/pattern_wide_string.hpp @@ -0,0 +1,61 @@ +#pragma once + +#include + +#include + +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 clone() const override { + return std::unique_ptr(new PatternWideString(*this)); + } + + void createEntry(prv::Provider *&provider) override { + auto size = std::min(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, 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, char16_t> {}.to_bytes(buffer); + } + + [[nodiscard]] bool operator==(const Pattern &other) const override { return areCommonPropertiesEqual(other); } + }; + +} \ No newline at end of file diff --git a/lib/libimhex/include/hex/pattern_language/token.hpp b/lib/libimhex/include/hex/pattern_language/token.hpp index 37f8592f1..8c3d60f19 100644 --- a/lib/libimhex/include/hex/pattern_language/token.hpp +++ b/lib/libimhex/include/hex/pattern_language/token.hpp @@ -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; + using Literal = std::variant>; using ValueTypes = std::variant; 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 &) -> 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 &) -> 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 &) -> 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 &) -> 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 &) -> std::string { LogConsole::abortEvaluation("expected integral type, got custom type"); }, [](auto &&result) -> std::string { return std::to_string(result); } }, literal); } diff --git a/lib/libimhex/include/hex/pattern_language/validator.hpp b/lib/libimhex/include/hex/pattern_language/validator.hpp index 52d7cd0cb..0911881c2 100644 --- a/lib/libimhex/include/hex/pattern_language/validator.hpp +++ b/lib/libimhex/include/hex/pattern_language/validator.hpp @@ -2,6 +2,7 @@ #include +#include #include #include #include @@ -16,7 +17,7 @@ namespace hex::pl { public: Validator() = default; - bool validate(const std::vector &ast); + bool validate(const std::vector> &ast); const std::optional &getError() { return this->m_error; } diff --git a/lib/libimhex/include/hex/providers/provider.hpp b/lib/libimhex/include/hex/providers/provider.hpp index 5cb7951e9..4c13a947a 100644 --- a/lib/libimhex/include/hex/providers/provider.hpp +++ b/lib/libimhex/include/hex/providers/provider.hpp @@ -9,7 +9,11 @@ #include #include -#include +#include + +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> m_patches; std::list m_overlays; - pl::PatternLanguage m_patternLanguageRuntime; + std::unique_ptr m_patternLanguageRuntime; std::string m_patternLanguageSourceCode; }; diff --git a/lib/libimhex/include/hex/ui/view.hpp b/lib/libimhex/include/hex/ui/view.hpp index a3cc67821..5719870b8 100644 --- a/lib/libimhex/include/hex/ui/view.hpp +++ b/lib/libimhex/include/hex/ui/view.hpp @@ -13,6 +13,7 @@ #include #include #include +#include #include diff --git a/lib/libimhex/source/pattern_language/evaluator.cpp b/lib/libimhex/source/pattern_language/evaluator.cpp index 53e6e7ec0..ce460a55b 100644 --- a/lib/libimhex/source/pattern_language/evaluator.cpp +++ b/lib/libimhex/source/pattern_language/evaluator.cpp @@ -1,6 +1,19 @@ #include -#include -#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include 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; 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(&value.value()) != nullptr) - pattern = new PatternDataUnsigned(this, 0, sizeof(u128)); + pattern = std::unique_ptr(new PatternUnsigned(this, 0, sizeof(u128))); else if (std::get_if(&value.value()) != nullptr) - pattern = new PatternDataSigned(this, 0, sizeof(i128)); + pattern = std::unique_ptr(new PatternSigned(this, 0, sizeof(i128))); else if (std::get_if(&value.value()) != nullptr) - pattern = new PatternDataFloat(this, 0, sizeof(double)); + pattern = std::unique_ptr(new PatternFloat(this, 0, sizeof(double))); else if (std::get_if(&value.value()) != nullptr) - pattern = new PatternDataBoolean(this, 0); + pattern = std::unique_ptr(new PatternBoolean(this, 0)); else if (std::get_if(&value.value()) != nullptr) - pattern = new PatternDataCharacter(this, 0); + pattern = std::unique_ptr(new PatternCharacter(this, 0)); else if (std::get_if(&value.value()) != nullptr) - pattern = new PatternDataString(this, 0, 1); - else if (std::get_if(&value.value()) != nullptr) { - pattern = std::get(value.value())->clone(); + pattern = std::unique_ptr(new PatternString(this, 0, 1)); + else if (auto patternValue = std::get_if>(&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 = 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(pattern)) + if (dynamic_cast(pattern.get())) return u128(value) & bitmask(pattern->getSize() * 8); - else if (dynamic_cast(pattern)) + else if (dynamic_cast(pattern.get())) return i128(value) & bitmask(pattern->getSize() * 8); - else if (dynamic_cast(pattern)) + else if (dynamic_cast(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(pattern)) + if (dynamic_cast(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 &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(pattern) || dynamic_cast(pattern)) + if (dynamic_cast(pattern.get()) || dynamic_cast(pattern.get())) return u128(value) & bitmask(pattern->getSize() * 8); - else if (dynamic_cast(pattern)) + else if (dynamic_cast(pattern.get())) return i128(value) & bitmask(pattern->getSize() * 8); - else if (dynamic_cast(pattern)) + else if (dynamic_cast(pattern.get())) return char(value); - else if (dynamic_cast(pattern)) + else if (dynamic_cast(pattern.get())) return bool(value); - else if (dynamic_cast(pattern)) + else if (dynamic_cast(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> Evaluator::evaluate(const std::vector &ast) { + std::optional>> Evaluator::evaluate(const std::vector> &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 patterns; + std::vector> patterns; try { this->setCurrentControlFlowStatement(ControlFlowStatement::None); @@ -168,33 +182,30 @@ namespace hex::pl { popScope(); }; - for (auto node : ast) { - if (dynamic_cast(node)) { + for (auto &node : ast) { + if (dynamic_cast(node.get())) { ; // Don't create patterns from type declarations - } else if (dynamic_cast(node)) { - delete node->evaluate(this); - } else if (dynamic_cast(node)) { + } else if (dynamic_cast(node.get())) { + (void)node->evaluate(this); + } else if (dynamic_cast(node.get())) { this->m_customFunctionDefinitions.push_back(node->evaluate(this)); - } else if (auto varDeclNode = dynamic_cast(node)) { - auto pattern = node->createPatterns(this).front(); + } else if (auto varDeclNode = dynamic_cast(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) { return pattern->isLocal(); }); diff --git a/lib/libimhex/source/pattern_language/log_console.cpp b/lib/libimhex/source/pattern_language/log_console.cpp index f40bb6d1e..2fc104438 100644 --- a/lib/libimhex/source/pattern_language/log_console.cpp +++ b/lib/libimhex/source/pattern_language/log_console.cpp @@ -1,13 +1,9 @@ #include -#include +#include 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 = {}; - } - } \ No newline at end of file diff --git a/lib/libimhex/source/pattern_language/parser.cpp b/lib/libimhex/source/pattern_language/parser.cpp index aeb52a0c3..5a42cb7b1 100644 --- a/lib/libimhex/source/pattern_language/parser.cpp +++ b/lib/libimhex/source/pattern_language/parser.cpp @@ -1,5 +1,32 @@ #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + #include #define MATCHES(x) (begin() && resetIfFailed(x)) @@ -16,17 +43,13 @@ namespace hex::pl { /* Mathematical expressions */ // Identifier([(parseMathematicalExpression)|<(parseMathematicalExpression),...>(parseMathematicalExpression)] - ASTNode *Parser::parseFunctionCall() { + std::unique_ptr Parser::parseFunctionCall() { std::string functionName = parseNamespaceResolution(); if (!MATCHES(sequence(SEPARATOR_ROUNDBRACKETOPEN))) throwParserError("expected '(' after function name"); - std::vector params; - auto paramCleanup = SCOPE_GUARD { - for (auto ¶m : params) - delete param; - }; + std::vector> params; while (!MATCHES(sequence(SEPARATOR_ROUNDBRACKETCLOSE))) { params.push_back(parseMathematicalExpression()); @@ -39,12 +62,10 @@ namespace hex::pl { throwParserError("missing ',' between parameters", -1); } - paramCleanup.release(); - - return create(new ASTNodeFunctionCall(functionName, params)); + return create(new ASTNodeFunctionCall(functionName, std::move(params))); } - ASTNode *Parser::parseStringLiteral() { + std::unique_ptr Parser::parseStringLiteral() { return create(new ASTNodeLiteral(getValue(-1))); } @@ -64,7 +85,7 @@ namespace hex::pl { return name; } - ASTNode *Parser::parseScopeResolution() { + std::unique_ptr Parser::parseScopeResolution() { std::string typeName; while (true) { @@ -87,13 +108,13 @@ namespace hex::pl { throwParserError("failed to parse scope resolution. Expected 'TypeName::Identifier'"); } - ASTNode *Parser::parseRValue() { + std::unique_ptr Parser::parseRValue() { ASTNodeRValue::Path path; return this->parseRValue(path); } // - ASTNode *Parser::parseRValue(ASTNodeRValue::Path &path) { + std::unique_ptr Parser::parseRValue(ASTNodeRValue::Path &path) { if (peek(IDENTIFIER, -1)) path.push_back(getValue(-1).get()); else if (peek(KEYWORD_PARENT, -1)) @@ -113,21 +134,20 @@ namespace hex::pl { else throwParserError("expected member name or 'parent' keyword", -1); } else - return create(new ASTNodeRValue(path)); + return create(new ASTNodeRValue(std::move(path))); } // - ASTNode *Parser::parseFactor() { + std::unique_ptr Parser::parseFactor() { if (MATCHES(sequence(INTEGER))) - return new ASTNodeLiteral(getValue(-1)); + return create(new ASTNodeLiteral(getValue(-1))); else if (peek(OPERATOR_PLUS) || peek(OPERATOR_MINUS) || peek(OPERATOR_BITNOT) || peek(OPERATOR_BOOLNOT)) return this->parseMathematicalExpression(); else if (MATCHES(sequence(SEPARATOR_ROUNDBRACKETOPEN))) { auto node = this->parseMathematicalExpression(); - if (!MATCHES(sequence(SEPARATOR_ROUNDBRACKETCLOSE))) { - delete node; + if (!MATCHES(sequence(SEPARATOR_ROUNDBRACKETCLOSE))) throwParserError("expected closing parenthesis"); - } + return node; } else if (MATCHES(sequence(IDENTIFIER))) { auto originalPos = this->m_curr; @@ -146,36 +166,34 @@ namespace hex::pl { } else if (MATCHES(oneOf(KEYWORD_PARENT, KEYWORD_THIS))) { return this->parseRValue(); } else if (MATCHES(sequence(OPERATOR_DOLLAR))) { - return new ASTNodeRValue({ "$" }); + return create(new ASTNodeRValue(hex::moveToVector("$"))); } else if (MATCHES(oneOf(OPERATOR_ADDRESSOF, OPERATOR_SIZEOF) && sequence(SEPARATOR_ROUNDBRACKETOPEN))) { auto op = getValue(-2); - ASTNode *result = nullptr; + std::unique_ptr result; if (MATCHES(oneOf(IDENTIFIER, KEYWORD_PARENT, KEYWORD_THIS))) { result = create(new ASTNodeTypeOperator(op, this->parseRValue())); } else if (MATCHES(sequence(VALUETYPE_ANY))) { auto type = getValue(-1); - result = new ASTNodeLiteral(u128(Token::getTypeSize(type))); + result = create(new ASTNodeLiteral(u128(Token::getTypeSize(type)))); } else { throwParserError("expected rvalue identifier or built-in type"); } - if (!MATCHES(sequence(SEPARATOR_ROUNDBRACKETCLOSE))) { - delete result; + if (!MATCHES(sequence(SEPARATOR_ROUNDBRACKETCLOSE))) throwParserError("expected closing parenthesis"); - } return result; } else throwParserError("expected value or parenthesis"); } - ASTNode *Parser::parseCastExpression() { + std::unique_ptr Parser::parseCastExpression() { if (peek(KEYWORD_BE) || peek(KEYWORD_LE) || peek(VALUETYPE_ANY)) { auto type = parseType(true); - auto builtinType = dynamic_cast(type->getType()); + auto builtinType = dynamic_cast(type->getType().get()); if (builtinType == nullptr) throwParserError("invalid type used for pointer size", -1); @@ -185,16 +203,16 @@ namespace hex::pl { auto node = parseFactor(); - return new ASTNodeCast(node, type); + return create(new ASTNodeCast(std::move(node), std::move(type))); } else return parseFactor(); } // <+|-|!|~> (parseFactor) - ASTNode *Parser::parseUnaryExpression() { + std::unique_ptr Parser::parseUnaryExpression() { if (MATCHES(oneOf(OPERATOR_PLUS, OPERATOR_MINUS, OPERATOR_BOOLNOT, OPERATOR_BITNOT))) { auto op = getValue(-1); - return create(new ASTNodeMathematicalExpression(new ASTNodeLiteral(0), this->parseCastExpression(), op)); + return create(new ASTNodeMathematicalExpression(create(new ASTNodeLiteral(0)), this->parseCastExpression(), op)); } else if (MATCHES(sequence(STRING))) { return this->parseStringLiteral(); } @@ -203,198 +221,150 @@ namespace hex::pl { } // (parseUnaryExpression) <*|/|%> (parseUnaryExpression) - ASTNode *Parser::parseMultiplicativeExpression() { + std::unique_ptr Parser::parseMultiplicativeExpression() { auto node = this->parseUnaryExpression(); - auto nodeCleanup = SCOPE_GUARD { delete node; }; - while (MATCHES(oneOf(OPERATOR_STAR, OPERATOR_SLASH, OPERATOR_PERCENT))) { auto op = getValue(-1); - node = create(new ASTNodeMathematicalExpression(node, this->parseUnaryExpression(), op)); + node = create(new ASTNodeMathematicalExpression(std::move(node), this->parseUnaryExpression(), op)); } - nodeCleanup.release(); - return node; } // (parseMultiplicativeExpression) <+|-> (parseMultiplicativeExpression) - ASTNode *Parser::parseAdditiveExpression() { + std::unique_ptr Parser::parseAdditiveExpression() { auto node = this->parseMultiplicativeExpression(); - auto nodeCleanup = SCOPE_GUARD { delete node; }; - while (MATCHES(variant(OPERATOR_PLUS, OPERATOR_MINUS))) { auto op = getValue(-1); - node = create(new ASTNodeMathematicalExpression(node, this->parseMultiplicativeExpression(), op)); + node = create(new ASTNodeMathematicalExpression(std::move(node), this->parseMultiplicativeExpression(), op)); } - nodeCleanup.release(); - return node; } // (parseAdditiveExpression) < >>|<< > (parseAdditiveExpression) - ASTNode *Parser::parseShiftExpression() { + std::unique_ptr Parser::parseShiftExpression() { auto node = this->parseAdditiveExpression(); - auto nodeCleanup = SCOPE_GUARD { delete node; }; - while (MATCHES(variant(OPERATOR_SHIFTLEFT, OPERATOR_SHIFTRIGHT))) { auto op = getValue(-1); - node = create(new ASTNodeMathematicalExpression(node, this->parseAdditiveExpression(), op)); + node = create(new ASTNodeMathematicalExpression(std::move(node), this->parseAdditiveExpression(), op)); } - nodeCleanup.release(); - return node; } // (parseShiftExpression) & (parseShiftExpression) - ASTNode *Parser::parseBinaryAndExpression() { + std::unique_ptr Parser::parseBinaryAndExpression() { auto node = this->parseShiftExpression(); - auto nodeCleanup = SCOPE_GUARD { delete node; }; - while (MATCHES(sequence(OPERATOR_BITAND))) { - node = create(new ASTNodeMathematicalExpression(node, this->parseShiftExpression(), Token::Operator::BitAnd)); + node = create(new ASTNodeMathematicalExpression(std::move(node), this->parseShiftExpression(), Token::Operator::BitAnd)); } - nodeCleanup.release(); - return node; } // (parseBinaryAndExpression) ^ (parseBinaryAndExpression) - ASTNode *Parser::parseBinaryXorExpression() { + std::unique_ptr Parser::parseBinaryXorExpression() { auto node = this->parseBinaryAndExpression(); - auto nodeCleanup = SCOPE_GUARD { delete node; }; - while (MATCHES(sequence(OPERATOR_BITXOR))) { - node = create(new ASTNodeMathematicalExpression(node, this->parseBinaryAndExpression(), Token::Operator::BitXor)); + node = create(new ASTNodeMathematicalExpression(std::move(node), this->parseBinaryAndExpression(), Token::Operator::BitXor)); } - nodeCleanup.release(); - return node; } // (parseBinaryXorExpression) | (parseBinaryXorExpression) - ASTNode *Parser::parseBinaryOrExpression() { + std::unique_ptr Parser::parseBinaryOrExpression() { auto node = this->parseBinaryXorExpression(); - auto nodeCleanup = SCOPE_GUARD { delete node; }; - while (MATCHES(sequence(OPERATOR_BITOR))) { - node = create(new ASTNodeMathematicalExpression(node, this->parseBinaryXorExpression(), Token::Operator::BitOr)); + node = create(new ASTNodeMathematicalExpression(std::move(node), this->parseBinaryXorExpression(), Token::Operator::BitOr)); } - nodeCleanup.release(); - return node; } - // (parseBinaryOrExpression) && (parseBinaryOrExpression) - ASTNode *Parser::parseBooleanAnd() { + // (parseBinaryOrExpression) < >=|<=|>|< > (parseBinaryOrExpression) + std::unique_ptr Parser::parseRelationExpression() { auto node = this->parseBinaryOrExpression(); - auto nodeCleanup = SCOPE_GUARD { delete node; }; - - while (MATCHES(sequence(OPERATOR_BOOLAND))) { - node = create(new ASTNodeMathematicalExpression(node, this->parseBinaryOrExpression(), Token::Operator::BoolAnd)); - } - - nodeCleanup.release(); - - return node; - } - - // (parseBooleanAnd) ^^ (parseBooleanAnd) - ASTNode *Parser::parseBooleanXor() { - auto node = this->parseBooleanAnd(); - - auto nodeCleanup = SCOPE_GUARD { delete node; }; - - while (MATCHES(sequence(OPERATOR_BOOLXOR))) { - node = create(new ASTNodeMathematicalExpression(node, this->parseBooleanAnd(), Token::Operator::BoolXor)); - } - - nodeCleanup.release(); - - return node; - } - - // (parseBooleanXor) || (parseBooleanXor) - ASTNode *Parser::parseBooleanOr() { - auto node = this->parseBooleanXor(); - - auto nodeCleanup = SCOPE_GUARD { delete node; }; - - while (MATCHES(sequence(OPERATOR_BOOLOR))) { - node = create(new ASTNodeMathematicalExpression(node, this->parseBooleanXor(), Token::Operator::BoolOr)); - } - - nodeCleanup.release(); - - return node; - } - - // (parseBooleanOr) < >=|<=|>|< > (parseBooleanOr) - ASTNode *Parser::parseRelationExpression() { - auto node = this->parseBooleanOr(); - - auto nodeCleanup = SCOPE_GUARD { delete node; }; - while (MATCHES(sequence(OPERATOR_BOOLGREATERTHAN) || sequence(OPERATOR_BOOLLESSTHAN) || sequence(OPERATOR_BOOLGREATERTHANOREQUALS) || sequence(OPERATOR_BOOLLESSTHANOREQUALS))) { auto op = getValue(-1); - node = create(new ASTNodeMathematicalExpression(node, this->parseBooleanOr(), op)); + node = create(new ASTNodeMathematicalExpression(std::move(node), this->parseBinaryOrExpression(), op)); } - nodeCleanup.release(); - return node; } // (parseRelationExpression) <==|!=> (parseRelationExpression) - ASTNode *Parser::parseEqualityExpression() { + std::unique_ptr Parser::parseEqualityExpression() { auto node = this->parseRelationExpression(); - auto nodeCleanup = SCOPE_GUARD { delete node; }; - while (MATCHES(sequence(OPERATOR_BOOLEQUALS) || sequence(OPERATOR_BOOLNOTEQUALS))) { auto op = getValue(-1); - node = create(new ASTNodeMathematicalExpression(node, this->parseRelationExpression(), op)); + node = create(new ASTNodeMathematicalExpression(std::move(node), this->parseRelationExpression(), op)); } - nodeCleanup.release(); - return node; } - // (parseEqualityExpression) ? (parseEqualityExpression) : (parseEqualityExpression) - ASTNode *Parser::parseTernaryConditional() { + // (parseEqualityExpression) && (parseEqualityExpression) + std::unique_ptr Parser::parseBooleanAnd() { auto node = this->parseEqualityExpression(); - auto nodeCleanup = SCOPE_GUARD { delete node; }; + while (MATCHES(sequence(OPERATOR_BOOLAND))) { + node = create(new ASTNodeMathematicalExpression(std::move(node), this->parseEqualityExpression(), Token::Operator::BoolAnd)); + } + + return node; + } + + // (parseBooleanAnd) ^^ (parseBooleanAnd) + std::unique_ptr Parser::parseBooleanXor() { + auto node = this->parseBooleanAnd(); + + while (MATCHES(sequence(OPERATOR_BOOLXOR))) { + node = create(new ASTNodeMathematicalExpression(std::move(node), this->parseBooleanAnd(), Token::Operator::BoolXor)); + } + + return node; + } + + // (parseBooleanXor) || (parseBooleanXor) + std::unique_ptr Parser::parseBooleanOr() { + auto node = this->parseBooleanXor(); + + while (MATCHES(sequence(OPERATOR_BOOLOR))) { + node = create(new ASTNodeMathematicalExpression(std::move(node), this->parseBooleanXor(), Token::Operator::BoolOr)); + } + + return node; + } + + // (parseBooleanOr) ? (parseBooleanOr) : (parseBooleanOr) + std::unique_ptr Parser::parseTernaryConditional() { + auto node = this->parseBooleanOr(); while (MATCHES(sequence(OPERATOR_TERNARYCONDITIONAL))) { - auto second = this->parseEqualityExpression(); + auto second = this->parseBooleanOr(); if (!MATCHES(sequence(OPERATOR_INHERIT))) throwParserError("expected ':' in ternary expression"); - auto third = this->parseEqualityExpression(); - node = create(new ASTNodeTernaryExpression(node, second, third, Token::Operator::TernaryConditional)); + auto third = this->parseBooleanOr(); + node = create(new ASTNodeTernaryExpression(std::move(node), std::move(second), std::move(third), Token::Operator::TernaryConditional)); } - nodeCleanup.release(); - return node; } // (parseTernaryConditional) - ASTNode *Parser::parseMathematicalExpression() { + std::unique_ptr Parser::parseMathematicalExpression() { return this->parseTernaryConditional(); } @@ -428,9 +398,9 @@ namespace hex::pl { /* Functions */ - ASTNode *Parser::parseFunctionDefinition() { + std::unique_ptr Parser::parseFunctionDefinition() { const auto &functionName = getValue(-2).get(); - std::vector> params; + std::vector>> params; std::optional parameterPack; // Parse parameter list @@ -448,9 +418,9 @@ namespace hex::pl { auto type = parseType(true); if (MATCHES(sequence(IDENTIFIER))) - params.emplace_back(getValue(-1).get(), type); + params.emplace_back(getValue(-1).get(), std::move(type)); else { - params.emplace_back(std::to_string(unnamedParamCount), type); + params.emplace_back(std::to_string(unnamedParamCount), std::move(type)); unnamedParamCount++; } @@ -468,32 +438,33 @@ namespace hex::pl { // Parse function body - std::vector body; - auto bodyCleanup = SCOPE_GUARD { - for (auto &node : body) - delete node; - }; + std::vector> body; while (!MATCHES(sequence(SEPARATOR_CURLYBRACKETCLOSE))) { body.push_back(this->parseFunctionStatement()); } - bodyCleanup.release(); - return create(new ASTNodeFunctionDefinition(getNamespacePrefixedName(functionName), params, body, parameterPack)); + return create(new ASTNodeFunctionDefinition(getNamespacePrefixedName(functionName), std::move(params), std::move(body), parameterPack)); } - ASTNode *Parser::parseFunctionVariableDecl() { - ASTNode *statement; + std::unique_ptr Parser::parseFunctionVariableDecl() { + std::unique_ptr statement; auto type = parseType(true); if (MATCHES(sequence(IDENTIFIER))) { auto identifier = getValue(-1).get(); - statement = parseMemberVariable(type); + statement = parseMemberVariable(std::move(type)); if (MATCHES(sequence(OPERATOR_ASSIGNMENT))) { auto expression = parseMathematicalExpression(); - statement = create(new ASTNodeCompoundStatement({ statement, create(new ASTNodeAssignment(identifier, expression)) })); + std::vector> compoundStatement; + { + compoundStatement.push_back(std::move(statement)); + compoundStatement.push_back(create(new ASTNodeAssignment(identifier, std::move(expression)))); + } + + statement = create(new ASTNodeCompoundStatement(std::move(compoundStatement))); } } else throwParserError("invalid variable declaration"); @@ -501,9 +472,9 @@ namespace hex::pl { return statement; } - ASTNode *Parser::parseFunctionStatement() { + std::unique_ptr Parser::parseFunctionStatement() { bool needsSemicolon = true; - ASTNode *statement; + std::unique_ptr statement; if (MATCHES(sequence(IDENTIFIER, OPERATOR_ASSIGNMENT))) statement = parseFunctionVariableAssignment(getValue(-2).get()); @@ -541,10 +512,8 @@ namespace hex::pl { } else throwParserError("invalid sequence", 0); - if (needsSemicolon && !MATCHES(sequence(SEPARATOR_ENDOFEXPRESSION))) { - delete statement; + if (needsSemicolon && !MATCHES(sequence(SEPARATOR_ENDOFEXPRESSION))) throwParserError("missing ';' at end of expression", -1); - } // Consume superfluous semicolons while (needsSemicolon && MATCHES(sequence(SEPARATOR_ENDOFEXPRESSION))) @@ -553,21 +522,21 @@ namespace hex::pl { return statement; } - ASTNode *Parser::parseFunctionVariableAssignment(const std::string &lvalue) { + std::unique_ptr Parser::parseFunctionVariableAssignment(const std::string &lvalue) { auto rvalue = this->parseMathematicalExpression(); - return create(new ASTNodeAssignment(lvalue, rvalue)); + return create(new ASTNodeAssignment(lvalue, std::move(rvalue))); } - ASTNode *Parser::parseFunctionVariableCompoundAssignment(const std::string &lvalue) { + std::unique_ptr Parser::parseFunctionVariableCompoundAssignment(const std::string &lvalue) { const auto &op = getValue(-2); auto rvalue = this->parseMathematicalExpression(); - return create(new ASTNodeAssignment(lvalue, create(new ASTNodeMathematicalExpression(create(new ASTNodeRValue({ lvalue })), rvalue, op)))); + return create(new ASTNodeAssignment(lvalue, create(new ASTNodeMathematicalExpression(create(new ASTNodeRValue(hex::moveToVector(lvalue))), std::move(rvalue), op)))); } - ASTNode *Parser::parseFunctionControlFlowStatement() { + std::unique_ptr Parser::parseFunctionControlFlowStatement() { ControlFlowStatement type; if (peek(KEYWORD_RETURN, -1)) type = ControlFlowStatement::Return; @@ -584,13 +553,8 @@ namespace hex::pl { return create(new ASTNodeControlFlowStatement(type, this->parseMathematicalExpression())); } - std::vector Parser::parseStatementBody() { - std::vector body; - - auto bodyCleanup = SCOPE_GUARD { - for (auto &node : body) - delete node; - }; + std::vector> Parser::parseStatementBody() { + std::vector> body; if (MATCHES(sequence(SEPARATOR_CURLYBRACKETOPEN))) { while (!MATCHES(sequence(SEPARATOR_CURLYBRACKETCLOSE))) { @@ -600,22 +564,12 @@ namespace hex::pl { body.push_back(parseFunctionStatement()); } - bodyCleanup.release(); - return body; } - ASTNode *Parser::parseFunctionConditional() { + std::unique_ptr Parser::parseFunctionConditional() { auto condition = parseMathematicalExpression(); - std::vector trueBody, falseBody; - - auto cleanup = SCOPE_GUARD { - delete condition; - for (auto &statement : trueBody) - delete statement; - for (auto &statement : falseBody) - delete statement; - }; + std::vector> trueBody, falseBody; if (!MATCHES(sequence(SEPARATOR_ROUNDBRACKETCLOSE))) throwParserError("expected closing ')' after statement head"); @@ -625,45 +579,33 @@ namespace hex::pl { if (MATCHES(sequence(KEYWORD_ELSE))) falseBody = parseStatementBody(); - cleanup.release(); - - return create(new ASTNodeConditionalStatement(condition, trueBody, falseBody)); + return create(new ASTNodeConditionalStatement(std::move(condition), std::move(trueBody), std::move(falseBody))); } - ASTNode *Parser::parseFunctionWhileLoop() { + std::unique_ptr Parser::parseFunctionWhileLoop() { auto condition = parseMathematicalExpression(); - std::vector body; - - auto cleanup = SCOPE_GUARD { - delete condition; - for (auto &statement : body) - delete statement; - }; + std::vector> body; if (!MATCHES(sequence(SEPARATOR_ROUNDBRACKETCLOSE))) throwParserError("expected closing ')' after statement head"); body = parseStatementBody(); - cleanup.release(); - - return create(new ASTNodeWhileStatement(condition, body)); + return create(new ASTNodeWhileStatement(std::move(condition), std::move(body))); } - ASTNode *Parser::parseFunctionForLoop() { - auto variable = parseFunctionVariableDecl(); - auto variableCleanup = SCOPE_GUARD { delete variable; }; + std::unique_ptr Parser::parseFunctionForLoop() { + auto variable = parseFunctionVariableDecl(); if (!MATCHES(sequence(SEPARATOR_COMMA))) throwParserError("expected ',' after for loop variable declaration"); - auto condition = parseMathematicalExpression(); - auto conditionCleanup = SCOPE_GUARD { delete condition; }; + auto condition = parseMathematicalExpression(); if (!MATCHES(sequence(SEPARATOR_COMMA))) throwParserError("expected ',' after for loop condition"); - ASTNode *postExpression = nullptr; + std::unique_ptr postExpression = nullptr; if (MATCHES(sequence(IDENTIFIER, OPERATOR_ASSIGNMENT))) postExpression = parseFunctionVariableAssignment(getValue(-2).get()); else if (MATCHES(sequence(OPERATOR_DOLLAR, OPERATOR_ASSIGNMENT))) @@ -675,42 +617,29 @@ namespace hex::pl { else throwParserError("expected variable assignment in for loop post expression"); - auto postExpressionCleanup = SCOPE_GUARD { delete postExpression; }; + std::vector> body; - std::vector body; - - auto bodyCleanup = SCOPE_GUARD { - for (auto &statement : body) - delete statement; - }; if (!MATCHES(sequence(SEPARATOR_ROUNDBRACKETCLOSE))) throwParserError("expected closing ')' after statement head"); body = parseStatementBody(); - variableCleanup.release(); - conditionCleanup.release(); - postExpressionCleanup.release(); - bodyCleanup.release(); + std::vector> compoundStatement; + { + compoundStatement.push_back(std::move(variable)); + compoundStatement.push_back(create(new ASTNodeWhileStatement(std::move(condition), std::move(body), std::move(postExpression)))); + } - return create(new ASTNodeCompoundStatement({ variable, create(new ASTNodeWhileStatement(condition, body, postExpression)) }, true)); + return create(new ASTNodeCompoundStatement(std::move(compoundStatement), true)); } /* Control flow */ // if ((parseMathematicalExpression)) { (parseMember) } - ASTNode *Parser::parseConditional() { + std::unique_ptr Parser::parseConditional() { auto condition = parseMathematicalExpression(); - std::vector trueBody, falseBody; - - auto cleanup = SCOPE_GUARD { - delete condition; - for (auto &statement : trueBody) - delete statement; - for (auto &statement : falseBody) - delete statement; - }; + std::vector> trueBody, falseBody; if (MATCHES(sequence(SEPARATOR_ROUNDBRACKETCLOSE, SEPARATOR_CURLYBRACKETOPEN))) { while (!MATCHES(sequence(SEPARATOR_CURLYBRACKETCLOSE))) { @@ -729,31 +658,23 @@ namespace hex::pl { falseBody.push_back(parseMember()); } - cleanup.release(); - - return create(new ASTNodeConditionalStatement(condition, trueBody, falseBody)); + return create(new ASTNodeConditionalStatement(std::move(condition), std::move(trueBody), std::move(falseBody))); } // while ((parseMathematicalExpression)) - ASTNode *Parser::parseWhileStatement() { + std::unique_ptr Parser::parseWhileStatement() { auto condition = parseMathematicalExpression(); - auto cleanup = SCOPE_GUARD { - delete condition; - }; - if (!MATCHES(sequence(SEPARATOR_ROUNDBRACKETCLOSE))) throwParserError("expected closing ')' after while head"); - cleanup.release(); - - return create(new ASTNodeWhileStatement(condition, {})); + return create(new ASTNodeWhileStatement(std::move(condition), {})); } /* Type declarations */ // [be|le] - ASTNodeTypeDecl *Parser::parseType(bool allowFunctionTypes) { + std::unique_ptr Parser::parseType(bool allowFunctionTypes) { std::optional endian; if (MATCHES(sequence(KEYWORD_LE))) @@ -779,64 +700,55 @@ namespace hex::pl { throwParserError("cannot use 'auto' in this context"); } - return create(new ASTNodeTypeDecl({}, new ASTNodeBuiltinType(type), endian)); + return create(new ASTNodeTypeDecl({}, create(new ASTNodeBuiltinType(type)), endian)); } else throwParserError("failed to parse type. Expected identifier or builtin type"); } // using Identifier = (parseType) - ASTNode *Parser::parseUsingDeclaration() { + std::shared_ptr Parser::parseUsingDeclaration() { auto name = parseNamespaceResolution(); if (!MATCHES(sequence(OPERATOR_ASSIGNMENT))) throwParserError("expected '=' after type name of using declaration"); - auto *type = dynamic_cast(parseType()); - if (type == nullptr) throwParserError("invalid type used in variable declaration", -1); + auto type = parseType(); - return addType(name, type, type->getEndian()); + auto endian = type->getEndian(); + return addType(name, std::move(type), endian); } // padding[(parseMathematicalExpression)] - ASTNode *Parser::parsePadding() { + std::unique_ptr Parser::parsePadding() { auto size = parseMathematicalExpression(); - if (!MATCHES(sequence(SEPARATOR_SQUAREBRACKETCLOSE))) { - delete size; + if (!MATCHES(sequence(SEPARATOR_SQUAREBRACKETCLOSE))) throwParserError("expected closing ']' at end of array declaration", -1); - } - return create(new ASTNodeArrayVariableDecl({}, new ASTNodeTypeDecl({}, new ASTNodeBuiltinType(Token::ValueType::Padding)), size)); + return create(new ASTNodeArrayVariableDecl({}, create(new ASTNodeTypeDecl({}, create(new ASTNodeBuiltinType(Token::ValueType::Padding)))), std::move(size))); } // (parseType) Identifier - ASTNode *Parser::parseMemberVariable(ASTNodeTypeDecl *type) { + std::unique_ptr Parser::parseMemberVariable(std::unique_ptr &&type) { if (peek(SEPARATOR_COMMA)) { - std::vector variables; - auto variableCleanup = SCOPE_GUARD { - for (auto var : variables) delete var; - }; + std::vector> variables; do { variables.push_back(create(new ASTNodeVariableDecl(getValue(-1).get(), type->clone()))); } while (MATCHES(sequence(SEPARATOR_COMMA, IDENTIFIER))); - delete type; - variableCleanup.release(); - - return create(new ASTNodeMultiVariableDecl(variables)); + return create(new ASTNodeMultiVariableDecl(std::move(variables))); } else if (MATCHES(sequence(OPERATOR_AT))) - return create(new ASTNodeVariableDecl(getValue(-2).get(), type, parseMathematicalExpression())); + return create(new ASTNodeVariableDecl(getValue(-2).get(), std::move(type), parseMathematicalExpression())); else - return create(new ASTNodeVariableDecl(getValue(-1).get(), type)); + return create(new ASTNodeVariableDecl(getValue(-1).get(), std::move(type))); } // (parseType) Identifier[(parseMathematicalExpression)] - ASTNode *Parser::parseMemberArrayVariable(ASTNodeTypeDecl *type) { + std::unique_ptr Parser::parseMemberArrayVariable(std::unique_ptr &&type) { auto name = getValue(-2).get(); - ASTNode *size = nullptr; - auto sizeCleanup = SCOPE_GUARD { delete size; }; + std::unique_ptr size; if (!MATCHES(sequence(SEPARATOR_SQUAREBRACKETCLOSE))) { if (MATCHES(sequence(KEYWORD_WHILE, SEPARATOR_ROUNDBRACKETOPEN))) @@ -848,36 +760,34 @@ namespace hex::pl { throwParserError("expected closing ']' at end of array declaration", -1); } - sizeCleanup.release(); - if (MATCHES(sequence(OPERATOR_AT))) - return create(new ASTNodeArrayVariableDecl(name, type, size, parseMathematicalExpression())); + return create(new ASTNodeArrayVariableDecl(name, std::move(type), std::move(size), parseMathematicalExpression())); else - return create(new ASTNodeArrayVariableDecl(name, type, size)); + return create(new ASTNodeArrayVariableDecl(name, std::move(type), std::move(size))); } // (parseType) *Identifier : (parseType) - ASTNode *Parser::parseMemberPointerVariable(ASTNodeTypeDecl *type) { + std::unique_ptr Parser::parseMemberPointerVariable(std::unique_ptr &&type) { auto name = getValue(-2).get(); auto sizeType = parseType(); { - auto builtinType = dynamic_cast(sizeType->getType()); + auto builtinType = dynamic_cast(sizeType->getType().get()); if (builtinType == nullptr || !Token::isUnsigned(builtinType->getType())) throwParserError("invalid type used for pointer size", -1); } if (MATCHES(sequence(OPERATOR_AT))) - return create(new ASTNodePointerVariableDecl(name, type, sizeType, parseMathematicalExpression())); + return create(new ASTNodePointerVariableDecl(name, std::move(type), std::move(sizeType), parseMathematicalExpression())); else - return create(new ASTNodePointerVariableDecl(name, type, sizeType)); + return create(new ASTNodePointerVariableDecl(name, std::move(type), std::move(sizeType))); } // [(parsePadding)|(parseMemberVariable)|(parseMemberArrayVariable)|(parseMemberPointerVariable)] - ASTNode *Parser::parseMember() { - ASTNode *member; + std::unique_ptr Parser::parseMember() { + std::unique_ptr member; if (MATCHES(sequence(OPERATOR_DOLLAR, OPERATOR_ASSIGNMENT))) member = parseFunctionVariableAssignment("$"); @@ -910,11 +820,11 @@ namespace hex::pl { auto type = parseType(); if (MATCHES(sequence(IDENTIFIER, SEPARATOR_SQUAREBRACKETOPEN) && sequence(SEPARATOR_SQUAREBRACKETOPEN))) - member = parseMemberArrayVariable(type); + member = parseMemberArrayVariable(std::move(type)); else if (MATCHES(sequence(IDENTIFIER))) - member = parseMemberVariable(type); + member = parseMemberVariable(std::move(type)); else if (MATCHES(sequence(OPERATOR_STAR, IDENTIFIER, OPERATOR_INHERIT))) - member = parseMemberPointerVariable(type); + member = parseMemberPointerVariable(std::move(type)); else throwParserError("invalid variable declaration"); } @@ -925,14 +835,14 @@ namespace hex::pl { else if (MATCHES(sequence(SEPARATOR_ENDOFPROGRAM))) throwParserError("unexpected end of program", -2); else if (MATCHES(sequence(KEYWORD_BREAK))) - member = new ASTNodeControlFlowStatement(ControlFlowStatement::Break, nullptr); + member = create(new ASTNodeControlFlowStatement(ControlFlowStatement::Break, nullptr)); else if (MATCHES(sequence(KEYWORD_CONTINUE))) - member = new ASTNodeControlFlowStatement(ControlFlowStatement::Continue, nullptr); + member = create(new ASTNodeControlFlowStatement(ControlFlowStatement::Continue, nullptr)); else throwParserError("invalid struct member", 0); if (MATCHES(sequence(SEPARATOR_SQUAREBRACKETOPEN, SEPARATOR_SQUAREBRACKETOPEN))) - parseAttribute(dynamic_cast(member)); + parseAttribute(dynamic_cast(member.get())); if (!MATCHES(sequence(SEPARATOR_ENDOFEXPRESSION))) throwParserError("missing ';' at end of expression", -1); @@ -945,15 +855,11 @@ namespace hex::pl { } // struct Identifier { <(parseMember)...> } - ASTNode *Parser::parseStruct() { + std::shared_ptr Parser::parseStruct() { const auto &typeName = getValue(-1).get(); - const auto structNode = create(new ASTNodeStruct()); - const auto typeDecl = addType(typeName, structNode); - auto structGuard = SCOPE_GUARD { - delete structNode; - delete typeDecl; - }; + auto typeDecl = addType(typeName, create(new ASTNodeStruct())); + auto structNode = static_cast(typeDecl->getType().get()); if (MATCHES(sequence(OPERATOR_INHERIT, IDENTIFIER))) { // Inheritance @@ -977,65 +883,54 @@ namespace hex::pl { structNode->addMember(parseMember()); } - structGuard.release(); - return typeDecl; } // union Identifier { <(parseMember)...> } - ASTNode *Parser::parseUnion() { + std::shared_ptr Parser::parseUnion() { const auto &typeName = getValue(-2).get(); - const auto unionNode = create(new ASTNodeUnion()); - const auto typeDecl = addType(typeName, unionNode); - auto unionGuard = SCOPE_GUARD { - delete unionNode; - delete typeDecl; - }; + auto typeDecl = addType(typeName, create(new ASTNodeUnion())); + auto unionNode = static_cast(typeDecl->getType().get()); while (!MATCHES(sequence(SEPARATOR_CURLYBRACKETCLOSE))) { unionNode->addMember(parseMember()); } - unionGuard.release(); - return typeDecl; } // enum Identifier : (parseType) { <...> } - ASTNode *Parser::parseEnum() { + std::shared_ptr Parser::parseEnum() { auto typeName = getValue(-2).get(); auto underlyingType = parseType(); if (underlyingType->getEndian().has_value()) throwParserError("underlying type may not have an endian specification", -2); - const auto enumNode = create(new ASTNodeEnum(underlyingType)); - const auto typeDecl = addType(typeName, enumNode); - auto enumGuard = SCOPE_GUARD { - delete enumNode; - delete typeDecl; - }; + auto typeDecl = addType(typeName, create(new ASTNodeEnum(std::move(underlyingType)))); + auto enumNode = static_cast(typeDecl->getType().get()); if (!MATCHES(sequence(SEPARATOR_CURLYBRACKETOPEN))) throwParserError("expected '{' after enum definition", -1); - ASTNode *lastEntry = nullptr; + std::unique_ptr lastEntry; while (!MATCHES(sequence(SEPARATOR_CURLYBRACKETCLOSE))) { if (MATCHES(sequence(IDENTIFIER, OPERATOR_ASSIGNMENT))) { auto name = getValue(-2).get(); auto value = parseMathematicalExpression(); - enumNode->addEntry(name, value); - lastEntry = value; + lastEntry = value->clone(); + enumNode->addEntry(name, std::move(value)); } else if (MATCHES(sequence(IDENTIFIER))) { - ASTNode *valueExpr; + std::unique_ptr valueExpr; auto name = getValue(-1).get(); if (enumNode->getEntries().empty()) - valueExpr = lastEntry = create(new ASTNodeLiteral(u128(0))); + valueExpr = create(new ASTNodeLiteral(u128(0))); else - valueExpr = lastEntry = create(new ASTNodeMathematicalExpression(lastEntry->clone(), new ASTNodeLiteral(u128(1)), Token::Operator::Plus)); + valueExpr = create(new ASTNodeMathematicalExpression(lastEntry->clone(), create(new ASTNodeLiteral(u128(1))), Token::Operator::Plus)); - enumNode->addEntry(name, valueExpr); + lastEntry = valueExpr->clone(); + enumNode->addEntry(name, std::move(valueExpr)); } else if (MATCHES(sequence(SEPARATOR_ENDOFPROGRAM))) throwParserError("unexpected end of program", -2); else @@ -1049,22 +944,15 @@ namespace hex::pl { } } - enumGuard.release(); - return typeDecl; } // bitfield Identifier { } - ASTNode *Parser::parseBitfield() { + std::shared_ptr Parser::parseBitfield() { std::string typeName = getValue(-2).get(); - const auto bitfieldNode = create(new ASTNodeBitfield()); - const auto typeDecl = addType(typeName, bitfieldNode); - - auto enumGuard = SCOPE_GUARD { - delete bitfieldNode; - delete typeDecl; - }; + auto typeDecl = addType(typeName, create(new ASTNodeBitfield())); + auto bitfieldNode = static_cast(typeDecl->getType().get()); while (!MATCHES(sequence(SEPARATOR_CURLYBRACKETCLOSE))) { if (MATCHES(sequence(IDENTIFIER, OPERATOR_INHERIT))) { @@ -1085,19 +973,17 @@ namespace hex::pl { ; } - enumGuard.release(); - return typeDecl; } // (parseType) Identifier @ Integer - ASTNode *Parser::parseVariablePlacement(ASTNodeTypeDecl *type) { + std::unique_ptr Parser::parseVariablePlacement(std::unique_ptr &&type) { bool inVariable = false; bool outVariable = false; auto name = getValue(-1).get(); - ASTNode *placementOffset = nullptr; + std::unique_ptr placementOffset; if (MATCHES(sequence(OPERATOR_AT))) { placementOffset = parseMathematicalExpression(); } else if (MATCHES(sequence(KEYWORD_IN))) { @@ -1106,15 +992,14 @@ namespace hex::pl { outVariable = true; } - return create(new ASTNodeVariableDecl(name, type->clone(), placementOffset, inVariable, outVariable)); + return create(new ASTNodeVariableDecl(name, type->clone(), std::move(placementOffset), inVariable, outVariable)); } // (parseType) Identifier[[(parseMathematicalExpression)]] @ Integer - ASTNode *Parser::parseArrayVariablePlacement(ASTNodeTypeDecl *type) { + std::unique_ptr Parser::parseArrayVariablePlacement(std::unique_ptr &&type) { auto name = getValue(-2).get(); - ASTNode *size = nullptr; - auto sizeCleanup = SCOPE_GUARD { delete size; }; + std::unique_ptr size; if (!MATCHES(sequence(SEPARATOR_SQUAREBRACKETCLOSE))) { if (MATCHES(sequence(KEYWORD_WHILE, SEPARATOR_ROUNDBRACKETOPEN))) @@ -1131,20 +1016,17 @@ namespace hex::pl { auto placementOffset = parseMathematicalExpression(); - sizeCleanup.release(); - - return create(new ASTNodeArrayVariableDecl(name, type->clone(), size, placementOffset)); + return create(new ASTNodeArrayVariableDecl(name, std::move(type), std::move(size), std::move(placementOffset))); } // (parseType) *Identifier : (parseType) @ Integer - ASTNode *Parser::parsePointerVariablePlacement(ASTNodeTypeDecl *type) { + std::unique_ptr Parser::parsePointerVariablePlacement(std::unique_ptr &&type) { auto name = getValue(-2).get(); - auto sizeType = parseType(); - auto sizeCleanup = SCOPE_GUARD { delete sizeType; }; + auto sizeType = parseType(); { - auto builtinType = dynamic_cast(sizeType->getType()); + auto builtinType = dynamic_cast(sizeType->getType().get()); if (builtinType == nullptr || !Token::isUnsigned(builtinType->getType())) throwParserError("invalid type used for pointer size", -1); @@ -1155,13 +1037,11 @@ namespace hex::pl { auto placementOffset = parseMathematicalExpression(); - sizeCleanup.release(); - - return create(new ASTNodePointerVariableDecl(name, type->clone(), sizeType, placementOffset)); + return create(new ASTNodePointerVariableDecl(name, type->clone(), std::move(sizeType), std::move(placementOffset))); } - std::vector Parser::parseNamespace() { - std::vector statements; + std::vector> Parser::parseNamespace() { + std::vector> statements; if (!MATCHES(sequence(IDENTIFIER))) throwParserError("expected namespace identifier"); @@ -1182,7 +1062,7 @@ namespace hex::pl { while (!MATCHES(sequence(SEPARATOR_CURLYBRACKETCLOSE))) { auto newStatements = parseStatements(); - std::copy(newStatements.begin(), newStatements.end(), std::back_inserter(statements)); + std::move(newStatements.begin(), newStatements.end(), std::back_inserter(statements)); } this->m_currNamespace.pop_back(); @@ -1190,27 +1070,23 @@ namespace hex::pl { return statements; } - ASTNode *Parser::parsePlacement() { + std::unique_ptr Parser::parsePlacement() { auto type = parseType(); - ON_SCOPE_EXIT { delete type; }; if (MATCHES(sequence(IDENTIFIER, SEPARATOR_SQUAREBRACKETOPEN))) - return parseArrayVariablePlacement(type); + return parseArrayVariablePlacement(std::move(type)); else if (MATCHES(sequence(IDENTIFIER))) - return parseVariablePlacement(type); + return parseVariablePlacement(std::move(type)); else if (MATCHES(sequence(OPERATOR_STAR, IDENTIFIER, OPERATOR_INHERIT))) - return parsePointerVariablePlacement(type); + return parsePointerVariablePlacement(std::move(type)); else throwParserError("invalid sequence", 0); } /* Program */ // <(parseUsingDeclaration)|(parseVariablePlacement)|(parseStruct)> - std::vector Parser::parseStatements() { - ASTNode *statement = nullptr; - auto statementGuard = SCOPE_GUARD { - delete statement; - }; + std::vector> Parser::parseStatements() { + std::shared_ptr statement; if (MATCHES(sequence(KEYWORD_USING, IDENTIFIER))) statement = parseUsingDeclaration(); @@ -1243,7 +1119,7 @@ namespace hex::pl { else throwParserError("invalid sequence", 0); if (MATCHES(sequence(SEPARATOR_SQUAREBRACKETOPEN, SEPARATOR_SQUAREBRACKETOPEN))) - parseAttribute(dynamic_cast(statement)); + parseAttribute(dynamic_cast(statement.get())); if (!MATCHES(sequence(SEPARATOR_ENDOFEXPRESSION))) throwParserError("missing ';' at end of expression", -1); @@ -1252,25 +1128,23 @@ namespace hex::pl { while (MATCHES(sequence(SEPARATOR_ENDOFEXPRESSION))) ; - statementGuard.release(); - - return { statement }; + return hex::moveToVector(std::move(statement)); } - ASTNodeTypeDecl *Parser::addType(const std::string &name, ASTNode *node, std::optional endian) { + std::shared_ptr Parser::addType(const std::string &name, std::unique_ptr &&node, std::optional endian) { auto typeName = getNamespacePrefixedName(name); if (this->m_types.contains(typeName)) throwParserError(hex::format("redefinition of type '{}'", typeName)); - auto typeDecl = create(new ASTNodeTypeDecl(typeName, node, endian)); + std::shared_ptr typeDecl = create(new ASTNodeTypeDecl(typeName, std::move(node), endian)); this->m_types.insert({ typeName, typeDecl }); return typeDecl; } // <(parseNamespace)...> EndOfProgram - std::optional> Parser::parse(const std::vector &tokens) { + std::optional>> Parser::parse(const std::vector &tokens) { this->m_curr = tokens.begin(); this->m_types.clear(); diff --git a/lib/libimhex/source/pattern_language/pattern_language.cpp b/lib/libimhex/source/pattern_language/pattern_language.cpp index c883fc925..f6ba4d21e 100644 --- a/lib/libimhex/source/pattern_language/pattern_language.cpp +++ b/lib/libimhex/source/pattern_language/pattern_language.cpp @@ -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> PatternLanguage::parseString(const std::string &code) { + std::optional>> PatternLanguage::parseString(const std::string &code) { auto preprocessedCode = this->m_preprocessor->preprocess(code); if (!preprocessedCode.has_value()) { this->m_currError = this->m_preprocessor->getError(); @@ -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 &PatternLanguage::getCurrentAST() const { + const std::vector> &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(); diff --git a/lib/libimhex/source/pattern_language/validator.cpp b/lib/libimhex/source/pattern_language/validator.cpp index 65880a322..3a07eb8bd 100644 --- a/lib/libimhex/source/pattern_language/validator.cpp +++ b/lib/libimhex/source/pattern_language/validator.cpp @@ -1,6 +1,11 @@ #include -#include +#include +#include +#include +#include +#include +#include #include @@ -9,7 +14,7 @@ namespace hex::pl { - bool Validator::validate(const std::vector &ast) { + bool Validator::validate(const std::vector> &ast) { std::unordered_set identifiers; std::unordered_set 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(node); variableDeclNode != nullptr) { + if (auto variableDeclNode = dynamic_cast(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(node); typeDeclNode != nullptr) { + this->validate(hex::moveToVector>(variableDeclNode->getType()->clone())); + } else if (auto typeDeclNode = dynamic_cast(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(node); structNode != nullptr) { + this->validate(hex::moveToVector>(typeDeclNode->getType()->clone())); + } else if (auto structNode = dynamic_cast(node.get()); structNode != nullptr) { this->validate(structNode->getMembers()); - } else if (auto unionNode = dynamic_cast(node); unionNode != nullptr) { + } else if (auto unionNode = dynamic_cast(node.get()); unionNode != nullptr) { this->validate(unionNode->getMembers()); - } else if (auto enumNode = dynamic_cast(node); enumNode != nullptr) { + } else if (auto enumNode = dynamic_cast(node.get()); enumNode != nullptr) { std::unordered_set enumIdentifiers; for (auto &[name, value] : enumNode->getEntries()) { if (!enumIdentifiers.insert(name).second) diff --git a/lib/libimhex/source/providers/provider.cpp b/lib/libimhex/source/providers/provider.cpp index 8fec4340c..98ad48509 100644 --- a/lib/libimhex/source/providers/provider.cpp +++ b/lib/libimhex/source/providers/provider.cpp @@ -2,7 +2,10 @@ #include #include -#include +#include +#include + +#include #include #include @@ -14,6 +17,7 @@ namespace hex::prv { Provider::Provider() { this->m_patches.emplace_back(); + this->m_patternLanguageRuntime = std::make_unique(); if (this->hasLoadInterface()) EventManager::post(View::toWindowName("hex.builtin.view.provider_settings.load_popup")); diff --git a/main/source/init/tasks.cpp b/main/source/init/tasks.cpp index 55fe36908..6d8c9b9cf 100644 --- a/main/source/init/tasks.cpp +++ b/main/source/init/tasks.cpp @@ -3,10 +3,12 @@ #include #include -#include #include -#include +#include +#include +#include #include +#include #include #include diff --git a/plugins/builtin/include/content/providers/gdb_provider.hpp b/plugins/builtin/include/content/providers/gdb_provider.hpp index db0b5c105..819db97e1 100644 --- a/plugins/builtin/include/content/providers/gdb_provider.hpp +++ b/plugins/builtin/include/content/providers/gdb_provider.hpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include diff --git a/plugins/builtin/include/content/views/view_hex_editor.hpp b/plugins/builtin/include/content/views/view_hex_editor.hpp index e6fe091eb..3dc402cac 100644 --- a/plugins/builtin/include/content/views/view_hex_editor.hpp +++ b/plugins/builtin/include/content/views/view_hex_editor.hpp @@ -59,7 +59,7 @@ namespace hex::plugin::builtin { u64 m_resizeSize = 0; std::vector m_dataToSave; - std::set m_highlightedPatterns; + std::set m_highlightedPatterns; std::string m_loaderScriptScriptPath; std::string m_loaderScriptFilePath; diff --git a/plugins/builtin/include/content/views/view_pattern_data.hpp b/plugins/builtin/include/content/views/view_pattern_data.hpp index 8d6bd743e..60ea27957 100644 --- a/plugins/builtin/include/content/views/view_pattern_data.hpp +++ b/plugins/builtin/include/content/views/view_pattern_data.hpp @@ -19,7 +19,7 @@ namespace hex::plugin::builtin { void drawContent() override; private: - std::map> m_sortedPatterns; + std::map>> m_sortedPatterns; }; } \ No newline at end of file diff --git a/plugins/builtin/include/content/views/view_pattern_editor.hpp b/plugins/builtin/include/content/views/view_pattern_editor.hpp index 9c8c616b2..16d9d4a36 100644 --- a/plugins/builtin/include/content/views/view_pattern_editor.hpp +++ b/plugins/builtin/include/content/views/view_pattern_editor.hpp @@ -51,7 +51,8 @@ namespace hex::plugin::builtin { std::map m_patternVariables; std::vector 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); diff --git a/plugins/builtin/source/content/pl_builtin_functions.cpp b/plugins/builtin/source/content/pl_builtin_functions.cpp index 1ab77d037..6cba90b7e 100644 --- a/plugins/builtin/source/content/pl_builtin_functions.cpp +++ b/plugins/builtin/source/content/pl_builtin_functions.cpp @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include @@ -15,7 +15,7 @@ namespace hex::plugin::builtin { - std::string format(pl::Evaluator *ctx, auto params) { + std::string format(pl::Evaluator *ctx, const auto ¶ms) { auto format = pl::Token::literalToString(params[0], true); std::string message; @@ -25,7 +25,7 @@ namespace hex::plugin::builtin { auto ¶m = params[i]; std::visit(overloaded { - [&](pl::PatternData *value) { + [&](const std::shared_ptr &value) { formatArgs.push_back(value->toString(ctx->getProvider())); }, [&](auto &&value) { diff --git a/plugins/builtin/source/content/providers/disk_provider.cpp b/plugins/builtin/source/content/providers/disk_provider.cpp index 08c0f9511..8ce214eeb 100644 --- a/plugins/builtin/source/content/providers/disk_provider.cpp +++ b/plugins/builtin/source/content/providers/disk_provider.cpp @@ -1,8 +1,10 @@ #include "content/providers/disk_provider.hpp" -#include #include +#include +#include + #include #include diff --git a/plugins/builtin/source/content/views/view_pattern_data.cpp b/plugins/builtin/source/content/views/view_pattern_data.cpp index 86472160d..8c66ff241 100644 --- a/plugins/builtin/source/content/views/view_pattern_data.cpp +++ b/plugins/builtin/source/content/views/view_pattern_data.cpp @@ -1,7 +1,9 @@ #include "content/views/view_pattern_data.hpp" #include -#include + +#include +#include namespace hex::plugin::builtin { @@ -18,8 +20,8 @@ namespace hex::plugin::builtin { EventManager::unsubscribe(this); } - static bool beginPatternDataTable(prv::Provider *&provider, const std::vector &patterns, std::vector &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> &patterns, std::vector> &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 &left, const std::shared_ptr &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(); diff --git a/plugins/builtin/source/content/views/view_pattern_editor.cpp b/plugins/builtin/source/content/views/view_pattern_editor.cpp index 8df78d39d..13b5259cb 100644 --- a/plugins/builtin/source/content/views/view_pattern_editor.cpp +++ b/plugins/builtin/source/content/views/view_pattern_editor.cpp @@ -1,13 +1,17 @@ #include "content/views/view_pattern_editor.hpp" -#include + #include -#include -#include +#include +#include +#include +#include +#include + #include #include #include - +#include #include namespace hex::plugin::builtin { @@ -225,7 +229,7 @@ namespace hex::plugin::builtin { ImHexApi::HexEditor::addHighlightingProvider([](u64 address) -> std::optional { - 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(node)) { - auto type = dynamic_cast(variableDecl->getType()); + for (auto &node : *ast) { + if (auto variableDecl = dynamic_cast(node.get())) { + auto type = dynamic_cast(variableDecl->getType().get()); if (type == nullptr) continue; - auto builtinType = dynamic_cast(type->getType()); + auto builtinType = dynamic_cast(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(); diff --git a/plugins/example_rust/Cargo.lock b/plugins/example_rust/Cargo.lock deleted file mode 100644 index 598b4cc7c..000000000 --- a/plugins/example_rust/Cargo.lock +++ /dev/null @@ -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" diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f3656ab16..4761bb8d2 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -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) diff --git a/tests/pattern_language/include/test_patterns/test_pattern.hpp b/tests/pattern_language/include/test_patterns/test_pattern.hpp index 70dc53802..a54a39c14 100644 --- a/tests/pattern_language/include/test_patterns/test_pattern.hpp +++ b/tests/pattern_language/include/test_patterns/test_pattern.hpp @@ -3,7 +3,7 @@ #include #include -#include +#include #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 - static T *create(const std::string &typeName, const std::string &varName, auto... args) { - auto pattern = new T(nullptr, args...); + static std::unique_ptr create(const std::string &typeName, const std::string &varName, auto... args) { + auto pattern = std::make_unique(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 &getPatterns() const final { return this->m_patterns; } - virtual void addPattern(PatternData *pattern) final { - this->m_patterns.push_back(pattern); + [[nodiscard]] virtual const std::vector> &getPatterns() const final { return this->m_patterns; } + virtual void addPattern(std::unique_ptr &&pattern) final { + this->m_patterns.push_back(std::move(pattern)); } [[nodiscard]] auto failing() { @@ -58,7 +56,7 @@ namespace hex::test { } private: - std::vector m_patterns; + std::vector> m_patterns; Mode m_mode; static inline std::map s_tests; diff --git a/tests/pattern_language/include/test_patterns/test_pattern_bitfields.hpp b/tests/pattern_language/include/test_patterns/test_pattern_bitfields.hpp index 01727d4e5..85a3c1ef4 100644 --- a/tests/pattern_language/include/test_patterns/test_pattern_bitfields.hpp +++ b/tests/pattern_language/include/test_patterns/test_pattern_bitfields.hpp @@ -2,19 +2,27 @@ #include "test_pattern.hpp" +#include + namespace hex::test { class TestPatternBitfields : public TestPattern { public: TestPatternBitfields() : TestPattern("Bitfields") { - auto testBitfield = create("TestBitfield", "testBitfield", 0x12, (4 * 4) / 8); + auto testBitfield = create("TestBitfield", "testBitfield", 0x12, (4 * 4) / 8); testBitfield->setEndian(std::endian::big); - testBitfield->setFields({ create("", "a", 0x12, 0, 4, testBitfield), - create("", "b", 0x12, 4, 4, testBitfield), - create("", "c", 0x12, 8, 4, testBitfield), - create("", "d", 0x12, 12, 4, testBitfield) }); - addPattern(testBitfield); + std::vector> bitfieldFields; + { + bitfieldFields.push_back(create("", "a", 0x12, 0, 4, testBitfield.get())); + bitfieldFields.push_back(create("", "b", 0x12, 4, 4, testBitfield.get())); + bitfieldFields.push_back(create("", "c", 0x12, 8, 4, testBitfield.get())); + bitfieldFields.push_back(create("", "d", 0x12, 12, 4, testBitfield.get())); + } + + testBitfield->setFields(std::move(bitfieldFields)); + + addPattern(std::move(testBitfield)); } ~TestPatternBitfields() override = default; diff --git a/tests/pattern_language/include/test_patterns/test_pattern_enums.hpp b/tests/pattern_language/include/test_patterns/test_pattern_enums.hpp index cb9eb303e..eaf448877 100644 --- a/tests/pattern_language/include/test_patterns/test_pattern_enums.hpp +++ b/tests/pattern_language/include/test_patterns/test_pattern_enums.hpp @@ -2,12 +2,14 @@ #include "test_pattern.hpp" +#include + namespace hex::test { class TestPatternEnums : public TestPattern { public: TestPatternEnums() : TestPattern("Enums") { - auto testEnum = create("TestEnum", "testEnum", 0x08, sizeof(u32)); + auto testEnum = create("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; diff --git a/tests/pattern_language/include/test_patterns/test_pattern_padding.hpp b/tests/pattern_language/include/test_patterns/test_pattern_padding.hpp index 50d3793e6..b6a8d995f 100644 --- a/tests/pattern_language/include/test_patterns/test_pattern_padding.hpp +++ b/tests/pattern_language/include/test_patterns/test_pattern_padding.hpp @@ -2,21 +2,34 @@ #include "test_pattern.hpp" +#include +#include +#include +#include +#include + namespace hex::test { class TestPatternPadding : public TestPattern { public: TestPatternPadding() : TestPattern("Padding") { - auto testStruct = create("TestStruct", "testStruct", 0x100, sizeof(i32) + 20 + sizeof(u8[0x10])); + auto testStruct = create("TestStruct", "testStruct", 0x100, sizeof(i32) + 20 + sizeof(u8[0x10])); - auto variable = create("s32", "variable", 0x100, sizeof(i32)); - auto padding = create("padding", "", 0x100 + sizeof(i32), 20); - auto array = create("u8", "array", 0x100 + sizeof(i32) + 20, sizeof(u8[0x10])); - array->setEntries(create("u8", "", 0x100 + sizeof(i32) + 20, sizeof(u8)), 0x10); + auto variable = create("s32", "variable", 0x100, sizeof(i32)); + auto padding = create("padding", "", 0x100 + sizeof(i32), 20); + auto array = create("u8", "array", 0x100 + sizeof(i32) + 20, sizeof(u8[0x10])); + array->setEntries(create("u8", "", 0x100 + sizeof(i32) + 20, sizeof(u8)), 0x10); - testStruct->setMembers({ variable, padding, array }); + std::vector> 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; diff --git a/tests/pattern_language/include/test_patterns/test_pattern_placement.hpp b/tests/pattern_language/include/test_patterns/test_pattern_placement.hpp index d4ba43765..a8eef6051 100644 --- a/tests/pattern_language/include/test_patterns/test_pattern_placement.hpp +++ b/tests/pattern_language/include/test_patterns/test_pattern_placement.hpp @@ -2,6 +2,9 @@ #include "test_pattern.hpp" +#include +#include + namespace hex::test { class TestPatternPlacement : public TestPattern { @@ -9,14 +12,14 @@ namespace hex::test { TestPatternPlacement() : TestPattern("Placement") { // placementVar { - addPattern(create("u32", "placementVar", 0x00, sizeof(u32))); + addPattern(create("u32", "placementVar", 0x00, sizeof(u32))); } // placementArray { - auto placementArray = create("u8", "placementArray", 0x10, sizeof(u8) * 10); - placementArray->setEntries(create("u8", "", 0x10, sizeof(u8)), 10); - addPattern(placementArray); + auto placementArray = create("u8", "placementArray", 0x10, sizeof(u8) * 10); + placementArray->setEntries(std::move(create("u8", "", 0x10, sizeof(u8))), 10); + addPattern(std::move(placementArray)); } } ~TestPatternPlacement() override = default; diff --git a/tests/pattern_language/include/test_patterns/test_pattern_pointers.hpp b/tests/pattern_language/include/test_patterns/test_pattern_pointers.hpp index c8ade6db3..6105e0876 100644 --- a/tests/pattern_language/include/test_patterns/test_pattern_pointers.hpp +++ b/tests/pattern_language/include/test_patterns/test_pattern_pointers.hpp @@ -2,6 +2,9 @@ #include "test_pattern.hpp" +#include +#include + namespace hex::test { class TestPatternPointers : public TestPattern { @@ -9,12 +12,12 @@ namespace hex::test { TestPatternPointers() : TestPattern("Pointers") { // placementPointer { - auto placementPointer = create("", "placementPointer", 0x0C, sizeof(u8)); + auto placementPointer = create("", "placementPointer", 0x0C, sizeof(u8)); placementPointer->setPointedAtAddress(0x49); - auto pointedTo = create("u32", "", 0x49, sizeof(u32)); - placementPointer->setPointedAtPattern(pointedTo); - addPattern(placementPointer); + auto pointedTo = create("u32", "", 0x49, sizeof(u32)); + placementPointer->setPointedAtPattern(std::move(pointedTo)); + addPattern(std::move(placementPointer)); } } ~TestPatternPointers() override = default; diff --git a/tests/pattern_language/include/test_patterns/test_pattern_structs.hpp b/tests/pattern_language/include/test_patterns/test_pattern_structs.hpp index c6de29eb6..6517b17ec 100644 --- a/tests/pattern_language/include/test_patterns/test_pattern_structs.hpp +++ b/tests/pattern_language/include/test_patterns/test_pattern_structs.hpp @@ -2,20 +2,30 @@ #include "test_pattern.hpp" +#include +#include +#include +#include + namespace hex::test { class TestPatternStructs : public TestPattern { public: TestPatternStructs() : TestPattern("Structs") { - auto testStruct = create("TestStruct", "testStruct", 0x100, sizeof(i32) + sizeof(u8[0x10])); + auto testStruct = create("TestStruct", "testStruct", 0x100, sizeof(i32) + sizeof(u8[0x10])); - auto variable = create("s32", "variable", 0x100, sizeof(i32)); - auto array = create("u8", "array", 0x100 + sizeof(i32), sizeof(u8[0x10])); - array->setEntries(create("u8", "", 0x100 + sizeof(i32), sizeof(u8)), 0x10); + auto variable = create("s32", "variable", 0x100, sizeof(i32)); + auto array = create("u8", "array", 0x100 + sizeof(i32), sizeof(u8[0x10])); + array->setEntries(create("u8", "", 0x100 + sizeof(i32), sizeof(u8)), 0x10); - testStruct->setMembers({ variable, array }); + std::vector> 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; diff --git a/tests/pattern_language/include/test_patterns/test_pattern_unions.hpp b/tests/pattern_language/include/test_patterns/test_pattern_unions.hpp index 38ba20a43..2c44cec7b 100644 --- a/tests/pattern_language/include/test_patterns/test_pattern_unions.hpp +++ b/tests/pattern_language/include/test_patterns/test_pattern_unions.hpp @@ -2,20 +2,31 @@ #include "test_pattern.hpp" +#include +#include +#include +#include + namespace hex::test { class TestPatternUnions : public TestPattern { public: TestPatternUnions() : TestPattern("Unions") { - auto testUnion = create("TestUnion", "testUnion", 0x200, sizeof(u128)); + auto testUnion = create("TestUnion", "testUnion", 0x200, sizeof(u128)); - auto array = create("s32", "array", 0x200, sizeof(i32[2])); - array->setEntries(create("s32", "", 0x200, sizeof(i32)), 2); - auto variable = create("u128", "variable", 0x200, sizeof(u128)); + auto array = create("s32", "array", 0x200, sizeof(i32[2])); + array->setEntries(create("s32", "", 0x200, sizeof(i32)), 2); + auto variable = create("u128", "variable", 0x200, sizeof(u128)); - testUnion->setMembers({ array, variable }); + std::vector> 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; diff --git a/tests/pattern_language/source/main.cpp b/tests/pattern_language/source/main.cpp index a8160c102..689f792d7 100644 --- a/tests/pattern_language/source/main.cpp +++ b/tests/pattern_language/source/main.cpp @@ -7,14 +7,43 @@ #include #include #include -#include +#include +#include #include #include "test_provider.hpp" #include "test_patterns/test_pattern.hpp" +#include + using namespace hex::test; +static std::string format(hex::pl::Evaluator *ctx, const auto ¶ms) { + auto format = hex::pl::Token::literalToString(params[0], true); + std::string message; + + fmt::dynamic_format_arg_store formatArgs; + + for (u32 i = 1; i < params.size(); i++) { + auto ¶m = params[i]; + + std::visit(hex::overloaded { + [&](const std::shared_ptr &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 { + 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)