diff --git a/plugins/libimhex/include/hex/helpers/utils.hpp b/plugins/libimhex/include/hex/helpers/utils.hpp index cdd3fb108..1d57ece9c 100644 --- a/plugins/libimhex/include/hex/helpers/utils.hpp +++ b/plugins/libimhex/include/hex/helpers/utils.hpp @@ -166,6 +166,7 @@ namespace hex { } std::vector splitString(std::string_view string, std::string_view delimiter); + std::string combineStrings(const std::vector &strings, std::string_view delimiter = ""); std::string toEngineeringString(double value); diff --git a/plugins/libimhex/include/hex/lang/evaluator.hpp b/plugins/libimhex/include/hex/lang/evaluator.hpp index 70dc6ac80..efe3660a1 100644 --- a/plugins/libimhex/include/hex/lang/evaluator.hpp +++ b/plugins/libimhex/include/hex/lang/evaluator.hpp @@ -46,6 +46,7 @@ namespace hex::lang { std::vector m_endianStack; std::vector m_globalMembers; std::vector*> m_currMembers; + std::vector m_currMemberScope; LogConsole m_console; u32 m_recursionLimit; @@ -60,6 +61,7 @@ namespace hex::lang { ASTNodeIntegerLiteral* evaluateTernaryExpression(ASTNodeTernaryExpression *node); ASTNodeIntegerLiteral* evaluateMathematicalExpression(ASTNodeNumericExpression *node); + PatternData* findPattern(std::vector currMembers, const std::vector &path); PatternData* evaluateAttributes(ASTNode *currNode, PatternData *currPattern); PatternData* evaluateBuiltinType(ASTNodeBuiltinType *node); void evaluateMember(ASTNode *node, std::vector &currMembers, bool increaseOffset); diff --git a/plugins/libimhex/include/hex/lang/pattern_data.hpp b/plugins/libimhex/include/hex/lang/pattern_data.hpp index 6a78538c7..31497f973 100644 --- a/plugins/libimhex/include/hex/lang/pattern_data.hpp +++ b/plugins/libimhex/include/hex/lang/pattern_data.hpp @@ -25,7 +25,7 @@ namespace hex::lang { if (iscntrl(*c) || *c > 0x7F) result += " "; else - result += *c; + result += static_cast(*c); } if (*(data + size - 1) == '\x00') @@ -39,7 +39,7 @@ namespace hex::lang { class PatternData { public: PatternData(u64 offset, size_t size, u32 color = 0) - : m_offset(offset), m_size(size), m_color(color) { + : m_offset(offset), m_size(size), m_color(color), m_parent(nullptr) { constexpr u32 Palette[] = { 0x70b4771f, 0x700e7fff, 0x702ca02c, 0x702827d6, 0x70bd6794, 0x704b568c, 0x70c277e3, 0x707f7f7f, 0x7022bdbc, 0x70cfbe17 }; if (color != 0) @@ -55,7 +55,10 @@ namespace hex::lang { virtual PatternData* clone() = 0; [[nodiscard]] u64 getOffset() const { return this->m_offset; } + 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); } @@ -72,6 +75,9 @@ namespace hex::lang { [[nodiscard]] std::endian getEndian() const { return this->m_endian; } void setEndian(std::endian endian) { this->m_endian = endian; } + [[nodiscard]] PatternData* getParent() const { return this->m_parent; } + void setParent(PatternData *parent) { this->m_parent = parent; } + virtual void createEntry(prv::Provider* &provider) = 0; [[nodiscard]] virtual std::string getFormattedName() const = 0; @@ -191,6 +197,8 @@ namespace hex::lang { std::string m_variableName; std::optional m_comment; std::string m_typeName; + + PatternData *m_parent; }; class PatternDataPadding : public PatternData { @@ -211,11 +219,15 @@ namespace hex::lang { class PatternDataPointer : public PatternData { public: - PatternDataPointer(u64 offset, size_t size, PatternData *pointedAt, u32 color = 0) - : PatternData(offset, size, color), m_pointedAt(pointedAt) { + PatternDataPointer(u64 offset, size_t size, u32 color = 0) + : PatternData(offset, size, color), m_pointedAt(nullptr) { this->m_pointedAt->setVariableName("*" + this->m_pointedAt->getVariableName()); } + PatternDataPointer(const PatternDataPointer &other) : PatternData(other.getOffset(), other.getSize(), other.getColor()) { + this->m_pointedAt = other.m_pointedAt->clone(); + } + ~PatternDataPointer() override { delete this->m_pointedAt; } @@ -275,6 +287,10 @@ namespace hex::lang { return "Pointer"; } + void setPointedAtPattern(PatternData *pattern) { + this->m_pointedAt = pattern; + } + [[nodiscard]] PatternData* getPointedAtPattern() { return this->m_pointedAt; } @@ -448,16 +464,16 @@ namespace hex::lang { class PatternDataArray : public PatternData { public: - PatternDataArray(u64 offset, size_t size, std::vector entries, u32 color = 0) - : PatternData(offset, size, color), m_entries(std::move(entries)) { - - for (auto &entry : entries) - entry->setColor(color); + PatternDataArray(u64 offset, size_t size, u32 color = 0) + : PatternData(offset, size, color) { } PatternDataArray(const PatternDataArray &other) : PatternData(other.getOffset(), other.getSize(), other.getColor()) { + std::vector entries; for (const auto &entry : other.m_entries) - this->m_entries.push_back(entry->clone()); + entries.push_back(entry->clone()); + + this->setEntries(entries); } ~PatternDataArray() override { @@ -522,18 +538,32 @@ namespace hex::lang { return this->m_highlightedAddresses; } + [[nodiscard]] std::string getFormattedName() const override { return this->m_entries[0]->getTypeName() + "[" + std::to_string(this->m_entries.size()) + "]"; } + [[nodiscard]] const std::vector& getEntries() { + return this->m_entries; + } + + void setEntries(const std::vector &entries) { + this->m_entries = entries; + + for (auto &entry : this->m_entries) { + entry->setColor(this->getColor()); + entry->setParent(this); + } + } + private: std::vector m_entries; }; class PatternDataStruct : public PatternData { public: - PatternDataStruct(u64 offset, size_t size, const std::vector & members, u32 color = 0) - : PatternData(offset, size, color), m_members(members), m_sortedMembers(members) { } + PatternDataStruct(u64 offset, size_t size, u32 color = 0) : PatternData(offset, size, color){ + } PatternDataStruct(const PatternDataStruct &other) : PatternData(other.getOffset(), other.getSize(), other.getColor()) { for (const auto &member : other.m_members) @@ -607,10 +637,18 @@ namespace hex::lang { return "struct " + PatternData::getTypeName(); } - const auto& getMembers() const { + [[nodiscard]] const auto& getMembers() const { return this->m_members; } + void setMembers(const std::vector & members) { + this->m_members = members; + this->m_sortedMembers = members; + + for (auto &member : this->m_members) + member->setParent(this); + } + private: std::vector m_members; std::vector m_sortedMembers; @@ -618,8 +656,9 @@ namespace hex::lang { class PatternDataUnion : public PatternData { public: - PatternDataUnion(u64 offset, size_t size, const std::vector & members, u32 color = 0) - : PatternData(offset, size, color), m_members(members), m_sortedMembers(members) { } + PatternDataUnion(u64 offset, size_t size, u32 color = 0) : PatternData(offset, size, color) { + + } PatternDataUnion(const PatternDataUnion &other) : PatternData(other.getOffset(), other.getSize(), other.getColor()) { for (const auto &member : other.m_members) @@ -694,10 +733,18 @@ namespace hex::lang { return "union " + PatternData::getTypeName();; } - const auto& getMembers() const { + [[nodiscard]] const auto& getMembers() const { return this->m_members; } + void setMembers(const std::vector & members) { + this->m_members = members; + this->m_sortedMembers = members; + + for (auto &member : this->m_members) + member->setParent(this); + } + private: std::vector m_members; std::vector m_sortedMembers; @@ -705,8 +752,9 @@ namespace hex::lang { class PatternDataEnum : public PatternData { public: - PatternDataEnum(u64 offset, size_t size, std::vector> enumValues, u32 color = 0) - : PatternData(offset, size, color), m_enumValues(std::move(enumValues)) { } + PatternDataEnum(u64 offset, size_t size, u32 color = 0) : PatternData(offset, size, color) { + + } PatternData* clone() override { return new PatternDataEnum(*this); @@ -762,18 +810,23 @@ namespace hex::lang { return "enum " + PatternData::getTypeName(); } - const auto& getEnumValues() const { + [[nodiscard]] const auto& getEnumValues() const { return this->m_enumValues; } + void setEnumValues(const std::vector> &enumValues) { + this->m_enumValues = enumValues; + } + private: std::vector> m_enumValues; }; class PatternDataBitfield : public PatternData { public: - PatternDataBitfield(u64 offset, size_t size, std::vector> fields, u32 color = 0) - : PatternData(offset, size, color), m_fields(std::move(fields)) { } + PatternDataBitfield(u64 offset, size_t size, u32 color = 0) : PatternData(offset, size, color) { + + } PatternData* clone() override { return new PatternDataBitfield(*this); @@ -800,8 +853,8 @@ namespace hex::lang { ImGui::TableNextColumn(); std::string valueString = "{ "; - for (u64 i = 0; i < value.size(); i++) - valueString += hex::format("{0:02X} ", value[i]); + for (auto i : value) + valueString += hex::format("{0:02X} ", i); valueString += "}"; ImGui::TextUnformatted(valueString.c_str()); @@ -842,13 +895,16 @@ namespace hex::lang { return "bitfield " + PatternData::getTypeName(); } - const auto& getFields() const { + [[nodiscard]] const auto& getFields() const { return this->m_fields; } + void setFields(const std::vector> &fields) { + this->m_fields = fields; + } + private: std::vector> m_fields; }; - } \ No newline at end of file diff --git a/plugins/libimhex/include/hex/lang/token.hpp b/plugins/libimhex/include/hex/lang/token.hpp index b5a75bcdf..18e860045 100644 --- a/plugins/libimhex/include/hex/lang/token.hpp +++ b/plugins/libimhex/include/hex/lang/token.hpp @@ -31,7 +31,8 @@ namespace hex::lang { LittleEndian, BigEndian, If, - Else + Else, + Parent }; enum class Operator { @@ -218,6 +219,7 @@ namespace hex::lang { #define KEYWORD_BE COMPONENT(Keyword, BigEndian) #define KEYWORD_IF COMPONENT(Keyword, If) #define KEYWORD_ELSE COMPONENT(Keyword, Else) +#define KEYWORD_PARENT COMPONENT(Keyword, Parent) #define INTEGER hex::lang::Token::Type::Integer, hex::lang::Token::IntegerLiteral(hex::lang::Token::ValueType::Any, u64(0)) #define IDENTIFIER hex::lang::Token::Type::Identifier, "" diff --git a/plugins/libimhex/source/helpers/utils.cpp b/plugins/libimhex/source/helpers/utils.cpp index 79191a695..ac5e50bbb 100644 --- a/plugins/libimhex/source/helpers/utils.cpp +++ b/plugins/libimhex/source/helpers/utils.cpp @@ -127,10 +127,20 @@ namespace hex { res.push_back(token); } - res.push_back(std::string(string.substr(start))); + res.emplace_back(string.substr(start)); return res; } + std::string combineStrings(const std::vector &strings, std::string_view delimiter) { + std::string result; + for (const auto &string : strings) { + result += string; + result += delimiter; + } + + return result.substr(0, result.length() - delimiter.length()); + } + std::string toEngineeringString(double value) { constexpr std::array Suffixes = { "a", "f", "p", "n", "u", "m", "", "k", "M", "G", "T", "P", "E" }; diff --git a/plugins/libimhex/source/lang/evaluator.cpp b/plugins/libimhex/source/lang/evaluator.cpp index 3b6b0f7e3..b26c25272 100644 --- a/plugins/libimhex/source/lang/evaluator.cpp +++ b/plugins/libimhex/source/lang/evaluator.cpp @@ -30,8 +30,71 @@ namespace hex::lang { this->getConsole().abortEvaluation("failed to find identifier"); } + PatternData* Evaluator::findPattern(std::vector currMembers, const std::vector &path) { + PatternData *currPattern = nullptr; + for (const auto &identifier : path) { + if (identifier == "parent") { + if (currPattern == nullptr) { + if (!currMembers.empty()) + currPattern = this->m_currMemberScope.back(); + + if (currPattern == nullptr) + this->getConsole().abortEvaluation("attempted to get parent of global namespace"); + } + + auto parent = currPattern->getParent(); + + if (parent == nullptr) { + this->getConsole().abortEvaluation(hex::format("no parent available for identifier '{0}'", currPattern->getVariableName())); + } else { + currPattern = parent; + } + } else { + if (currPattern != nullptr) { + if (auto structPattern = dynamic_cast(currPattern); structPattern != nullptr) + currMembers = structPattern->getMembers(); + else if (auto unionPattern = dynamic_cast(currPattern); unionPattern != nullptr) + currMembers = unionPattern->getMembers(); + else + this->getConsole().abortEvaluation("tried to access member of a non-struct/union type"); + } + + auto candidate = std::find_if(currMembers.begin(), currMembers.end(), [&](auto member) { + return member->getVariableName() == identifier; + }); + + if (candidate != currMembers.end()) + currPattern = *candidate; + else + this->getConsole().abortEvaluation(hex::format("no member found with identifier '{0}'", identifier)); + } + + if (auto pointerPattern = dynamic_cast(currPattern); pointerPattern != nullptr) + currPattern = pointerPattern->getPointedAtPattern(); + } + + return currPattern; + } + PatternData* Evaluator::patternFromName(const std::vector &path) { - std::vector currMembers; + + PatternData *currPattern = nullptr; + + // Local member access + currPattern = this->findPattern(*this->m_currMembers.back(), path); + + // If no local member was found, try globally + if (currPattern == nullptr) { + currPattern = this->findPattern(this->m_globalMembers, path); + } + + // If still no pattern was found, the path is invalid + if (currPattern == nullptr) + this->getConsole().abortEvaluation(hex::format("no identifier with name '{}' was found", hex::combineStrings(path, "."))); + + return currPattern; + + /*std::vector currMembers; if (!this->m_currMembers.empty()) std::copy(this->m_currMembers.back()->begin(), this->m_currMembers.back()->end(), std::back_inserter(currMembers)); @@ -67,7 +130,7 @@ namespace hex::lang { if (auto pointerPattern = dynamic_cast(currPattern); pointerPattern != nullptr) currPattern = pointerPattern->getPointedAtPattern(); - return currPattern; + return currPattern;*/ } ASTNodeIntegerLiteral* Evaluator::evaluateRValue(ASTNodeRValue *node) { @@ -436,8 +499,15 @@ namespace hex::lang { PatternData* Evaluator::evaluateStruct(ASTNodeStruct *node) { std::vector memberPatterns; + auto structPattern = new PatternDataStruct(this->m_currOffset, 0); + structPattern->setParent(this->m_currMemberScope.back()); + this->m_currMembers.push_back(&memberPatterns); - ON_SCOPE_EXIT { this->m_currMembers.pop_back(); }; + this->m_currMemberScope.push_back(structPattern); + ON_SCOPE_EXIT { + this->m_currMembers.pop_back(); + this->m_currMemberScope.pop_back(); + }; this->m_currRecursionDepth++; if (this->m_currRecursionDepth > this->m_recursionLimit) @@ -446,18 +516,31 @@ namespace hex::lang { auto startOffset = this->m_currOffset; for (auto &member : node->getMembers()) { this->evaluateMember(member, memberPatterns, true); + structPattern->setMembers(memberPatterns); + + for (auto &pmember : memberPatterns) + hex::print("{}\n", pmember->getVariableName()); + hex::print("======\n"); } + structPattern->setSize(this->m_currOffset - startOffset); this->m_currRecursionDepth--; - return this->evaluateAttributes(node, new PatternDataStruct(startOffset, this->m_currOffset - startOffset, memberPatterns)); + return this->evaluateAttributes(node, structPattern); } PatternData* Evaluator::evaluateUnion(ASTNodeUnion *node) { std::vector memberPatterns; + auto unionPattern = new PatternDataUnion(this->m_currOffset, 0); + unionPattern->setParent(this->m_currMemberScope.back()); + this->m_currMembers.push_back(&memberPatterns); - ON_SCOPE_EXIT { this->m_currMembers.pop_back(); }; + this->m_currMemberScope.push_back(unionPattern); + ON_SCOPE_EXIT { + this->m_currMembers.pop_back(); + this->m_currMemberScope.pop_back(); + }; auto startOffset = this->m_currOffset; @@ -467,6 +550,7 @@ namespace hex::lang { for (auto &member : node->getMembers()) { this->evaluateMember(member, memberPatterns, false); + unionPattern->setMembers(memberPatterns); } this->m_currRecursionDepth--; @@ -474,10 +558,11 @@ namespace hex::lang { size_t size = 0; for (const auto &pattern : memberPatterns) size = std::max(size, pattern->getSize()); + unionPattern->setSize(size); this->m_currOffset += size; - return this->evaluateAttributes(node, new PatternDataUnion(startOffset, size, memberPatterns)); + return this->evaluateAttributes(node, unionPattern); } PatternData* Evaluator::evaluateEnum(ASTNodeEnum *node) { @@ -503,12 +588,16 @@ namespace hex::lang { auto valueNode = evaluateMathematicalExpression(expression); ON_SCOPE_EXIT { delete valueNode; }; - entryPatterns.push_back({ Token::castTo(builtinUnderlyingType->getType(), valueNode->getValue()), name }); + entryPatterns.emplace_back(Token::castTo(builtinUnderlyingType->getType(), valueNode->getValue()), name); } this->m_currOffset += size; - return this->evaluateAttributes(node, new PatternDataEnum(startOffset, size, entryPatterns)); + auto enumPattern = new PatternDataEnum(startOffset, size); + enumPattern->setSize(size); + enumPattern->setEnumValues(entryPatterns); + + return this->evaluateAttributes(node, enumPattern); } PatternData* Evaluator::evaluateBitfield(ASTNodeBitfield *node) { @@ -541,7 +630,10 @@ namespace hex::lang { size_t size = (bits + 7) / 8; this->m_currOffset += size; - return this->evaluateAttributes(node, new PatternDataBitfield(startOffset, size, entryPatterns)); + auto bitfieldPattern = new PatternDataBitfield(startOffset, size); + bitfieldPattern->setFields(entryPatterns); + + return this->evaluateAttributes(node, bitfieldPattern); } PatternData* Evaluator::evaluateType(ASTNodeTypeDecl *node) { @@ -689,7 +781,10 @@ namespace hex::lang { else { if (node->getSize() == nullptr) this->getConsole().abortEvaluation("no bounds provided for array"); - pattern = new PatternDataArray(startOffset, (this->m_currOffset - startOffset), entries, color.value_or(0)); + auto arrayPattern = new PatternDataArray(startOffset, (this->m_currOffset - startOffset), color.value_or(0)); + + arrayPattern->setEntries(entries); + pattern = arrayPattern; } pattern->setVariableName(node->getName().data()); @@ -746,10 +841,11 @@ namespace hex::lang { this->m_currOffset = pointerOffset + pointerSize; - auto pattern = new PatternDataPointer(pointerOffset, pointerSize, pointedAt); + auto pattern = new PatternDataPointer(pointerOffset, pointerSize); pattern->setVariableName(node->getName().data()); pattern->setEndian(this->getCurrentEndian()); + pattern->setPointedAtPattern(pointedAt); return this->evaluateAttributes(node, pattern); } @@ -757,13 +853,16 @@ namespace hex::lang { std::optional> Evaluator::evaluate(const std::vector &ast) { this->m_globalMembers.clear(); - this->m_currMembers.clear(); this->m_types.clear(); this->m_endianStack.clear(); this->m_currOffset = 0; try { for (const auto& node : ast) { + this->m_currMembers.clear(); + this->m_currMemberScope.clear(); + this->m_currMemberScope.push_back(nullptr); + this->m_endianStack.push_back(this->m_defaultDataEndian); this->m_currRecursionDepth = 0; diff --git a/plugins/libimhex/source/lang/lexer.cpp b/plugins/libimhex/source/lang/lexer.cpp index 7a4f2366f..392f80cf2 100644 --- a/plugins/libimhex/source/lang/lexer.cpp +++ b/plugins/libimhex/source/lang/lexer.cpp @@ -412,6 +412,8 @@ namespace hex::lang { tokens.emplace_back(VALUE_TOKEN(Integer, Token::IntegerLiteral(Token::ValueType::Boolean, s32(0)))); else if (identifier == "true") tokens.emplace_back(VALUE_TOKEN(Integer, Token::IntegerLiteral(Token::ValueType::Boolean, s32(1)))); + else if (identifier == "parent") + tokens.emplace_back(TOKEN(Keyword, Parent)); // Check for built-in types else if (identifier == "u8") diff --git a/plugins/libimhex/source/lang/parser.cpp b/plugins/libimhex/source/lang/parser.cpp index 7e7b3ada0..d50b7f4d1 100644 --- a/plugins/libimhex/source/lang/parser.cpp +++ b/plugins/libimhex/source/lang/parser.cpp @@ -69,12 +69,14 @@ namespace hex::lang { ASTNode* Parser::parseRValue(std::vector &path) { if (peek(IDENTIFIER, -1)) path.push_back(getValue(-1)); + else if (peek(KEYWORD_PARENT, -1)) + path.emplace_back("parent"); if (MATCHES(sequence(SEPARATOR_DOT))) { - if (MATCHES(sequence(IDENTIFIER))) + if (MATCHES(oneOf(IDENTIFIER, KEYWORD_PARENT))) return this->parseRValue(path); else - throwParseError("expected member name", -1); + throwParseError("expected member name or 'parent' keyword", -1); } else return TO_NUMERIC_EXPRESSION(new ASTNodeRValue(path)); } @@ -94,7 +96,7 @@ namespace hex::lang { return this->parseScopeResolution(path); } else if (MATCHES(sequence(IDENTIFIER, SEPARATOR_ROUNDBRACKETOPEN))) { return TO_NUMERIC_EXPRESSION(this->parseFunctionCall()); - } else if (MATCHES(sequence(IDENTIFIER))) { + } else if (MATCHES(oneOf(IDENTIFIER, KEYWORD_PARENT))) { std::vector path; return this->parseRValue(path); } else if (MATCHES(sequence(OPERATOR_DOLLAR))) { diff --git a/source/views/view_pattern.cpp b/source/views/view_pattern.cpp index c8f42309f..26a1bd55c 100644 --- a/source/views/view_pattern.cpp +++ b/source/views/view_pattern.cpp @@ -15,7 +15,7 @@ namespace hex { static TextEditor::LanguageDefinition langDef; if (!initialized) { static const char* const keywords[] = { - "using", "struct", "union", "enum", "bitfield", "be", "le", "if", "else", "false", "true" + "using", "struct", "union", "enum", "bitfield", "be", "le", "if", "else", "false", "true", "parent" }; for (auto& k : keywords) langDef.mKeywords.insert(k);