From 48296775ae89ecec1d2c0620a16daf9bd81a3710 Mon Sep 17 00:00:00 2001 From: WerWolv Date: Fri, 20 Nov 2020 21:29:28 +0100 Subject: [PATCH] Implemented union support into the pattern language --- include/lang/ast_node.hpp | 13 ++++++ include/lang/evaluator.hpp | 1 + include/lang/pattern_data.hpp | 63 ++++++++++++++++++++++++++++- include/lang/token.hpp | 1 + source/lang/evaluator.cpp | 74 +++++++++++++++++++++++++++++++++++ source/lang/lexer.cpp | 2 + source/lang/parser.cpp | 33 ++++++++++++++++ source/views/view_pattern.cpp | 2 +- 8 files changed, 186 insertions(+), 3 deletions(-) diff --git a/include/lang/ast_node.hpp b/include/lang/ast_node.hpp index 7c8e3a369..d2a75bf0b 100644 --- a/include/lang/ast_node.hpp +++ b/include/lang/ast_node.hpp @@ -14,6 +14,7 @@ namespace hex::lang { VariableDecl, TypeDecl, Struct, + Union, Enum, Bitfield, Scope, @@ -67,6 +68,18 @@ namespace hex::lang { std::vector m_nodes; }; + class ASTNodeUnion : public ASTNode { + public: + explicit ASTNodeUnion(std::string name, std::vector nodes) + : ASTNode(Type::Union), m_name(name), m_nodes(nodes) { } + + const std::string& getName() const { return this->m_name; } + std::vector &getNodes() { return this->m_nodes; } + private: + std::string m_name; + std::vector m_nodes; + }; + class ASTNodeBitField : public ASTNode { public: explicit ASTNodeBitField(std::string name, std::vector> fields) diff --git a/include/lang/evaluator.hpp b/include/lang/evaluator.hpp index 187af1eca..e723804b9 100644 --- a/include/lang/evaluator.hpp +++ b/include/lang/evaluator.hpp @@ -21,6 +21,7 @@ namespace hex::lang { std::unordered_map m_types; std::pair createStructPattern(ASTNodeVariableDecl *varDeclNode, u64 offset); + std::pair createUnionPattern(ASTNodeVariableDecl *varDeclNode, u64 offset); std::pair createEnumPattern(ASTNodeVariableDecl *varDeclNode, u64 offset); std::pair createBitfieldPattern(ASTNodeVariableDecl *varDeclNode, u64 offset); std::pair createArrayPattern(ASTNodeVariableDecl *varDeclNode, u64 offset); diff --git a/include/lang/pattern_data.hpp b/include/lang/pattern_data.hpp index c3186f6b6..293a40912 100644 --- a/include/lang/pattern_data.hpp +++ b/include/lang/pattern_data.hpp @@ -31,7 +31,7 @@ namespace hex::lang { class PatternData { public: - enum class Type { Unsigned, Signed, Float, Character, String, Struct, Array, Enum }; + enum class Type { Unsigned, Signed, Float, Character, String, Struct, Union, Array, Enum }; PatternData(Type type, u64 offset, size_t size, const std::string &name, u32 color = 0) : m_type(type), m_offset(offset), m_size(size), m_color(color), m_name(name) { @@ -358,6 +358,65 @@ namespace hex::lang { std::vector m_sortedMembers; }; + class PatternDataUnion : public PatternData { + public: + PatternDataUnion(u64 offset, size_t size, const std::string &name, const std::string &unionName, const std::vector & members, u32 color = 0) + : PatternData(Type::Union, offset, size, name, color), m_unionName(unionName), m_members(members), m_sortedMembers(members) { } + + void createEntry(prv::Provider* &provider) override { + ImGui::TableNextRow(ImGuiTableRowFlags_Headers); + ImGui::TableNextColumn(); + ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_AlphaPreview); + ImGui::TableNextColumn(); + bool open = ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth); + ImGui::TableNextColumn(); + ImGui::Text("0x%08lx : 0x%08lx", this->getOffset(), this->getOffset() + this->getSize() - 1); + ImGui::TableNextColumn(); + ImGui::Text("0x%04lx", this->getSize()); + ImGui::TableNextColumn(); + ImGui::Text("%s", this->getTypeName().c_str()); + ImGui::TableNextColumn(); + ImGui::Text("%s", "{ ... }"); + + if (open) { + for (auto &member : this->m_sortedMembers) + member->createEntry(provider); + + ImGui::TreePop(); + } + + } + + std::optional highlightBytes(size_t offset) override{ + for (auto &member : this->m_members) { + if (auto color = member->highlightBytes(offset); color.has_value()) + return color.value(); + } + + return { }; + } + + 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); + } + + std::string getTypeName() override { + return "union " + this->m_unionName; + } + + private: + std::string m_unionName; + std::vector m_members; + std::vector m_sortedMembers; + }; + class PatternDataEnum : public PatternData { public: PatternDataEnum(u64 offset, size_t size, const std::string &name, const std::string &enumName, std::vector> enumValues, u32 color = 0) @@ -414,7 +473,7 @@ namespace hex::lang { ImGui::TableNextColumn(); ImGui::Text("%s", this->getTypeName().c_str()); ImGui::TableNextColumn(); - ImGui::Text("%s", "{ ... }"); + ImGui::Text("{ %llx }", value); if (open) { u16 bitOffset = 0; diff --git a/include/lang/token.hpp b/include/lang/token.hpp index 28f456188..bf08eff39 100644 --- a/include/lang/token.hpp +++ b/include/lang/token.hpp @@ -25,6 +25,7 @@ namespace hex::lang { struct KeywordToken { enum class Keyword { Struct, + Union, Using, Enum, Bitfield diff --git a/source/lang/evaluator.cpp b/source/lang/evaluator.cpp index acd3de974..d7804b5f6 100644 --- a/source/lang/evaluator.cpp +++ b/source/lang/evaluator.cpp @@ -75,6 +75,72 @@ namespace hex::lang { return { new PatternDataStruct(offset, structSize, varDeclNode->getVariableName(), structNode->getName(), members, 0x00FFFFFF), structSize }; } + std::pair Evaluator::createUnionPattern(ASTNodeVariableDecl *varDeclNode, u64 offset) { + std::vector members; + + auto unionNode = static_cast(this->m_types[varDeclNode->getCustomVariableTypeName()]); + + if (unionNode == nullptr) + return { nullptr, 0 }; + + size_t unionSize = 0; + for (const auto &node : unionNode->getNodes()) { + const auto &member = static_cast(node); + + const auto typeDeclNode = static_cast(this->m_types[member->getCustomVariableTypeName()]); + + if (member->getVariableType() == Token::TypeToken::Type::Signed8Bit && member->getArraySize() > 1) { + const auto &[pattern, size] = this->createStringPattern(member, offset); + + if (pattern == nullptr) + return { nullptr, 0 }; + + members.push_back(pattern); + unionSize = std::max(size, unionSize); + } else if (member->getVariableType() == Token::TypeToken::Type::CustomType + && typeDeclNode != nullptr && typeDeclNode->getAssignedType() == Token::TypeToken::Type::Signed8Bit + && member->getArraySize() > 1) { + + const auto &[pattern, size] = this->createStringPattern(member, offset); + + if (pattern == nullptr) + return { nullptr, 0 }; + + members.push_back(pattern); + unionSize = std::max(size, unionSize); + } + else if (member->getArraySize() > 1) { + const auto &[pattern, size] = this->createArrayPattern(member, offset); + + if (pattern == nullptr) + return { nullptr, 0 }; + + members.push_back(pattern); + unionSize = std::max(size, unionSize); + } + else if (member->getVariableType() != Token::TypeToken::Type::CustomType) { + const auto &[pattern, size] = this->createBuiltInTypePattern(member, offset); + + if (pattern == nullptr) + return { nullptr, 0 }; + + members.push_back(pattern); + unionSize = std::max(size, unionSize); + } + else { + const auto &[pattern, size] = this->createCustomTypePattern(member, offset); + + if (pattern == nullptr) + return { nullptr, 0 }; + + members.push_back(pattern); + unionSize = std::max(size, unionSize); + } + } + + return { new PatternDataUnion(offset, unionSize, varDeclNode->getVariableName(), unionNode->getName(), members, 0x00FFFFFF), unionSize }; + } + std::pair Evaluator::createEnumPattern(ASTNodeVariableDecl *varDeclNode, u64 offset) { std::vector> enumValues; @@ -150,6 +216,8 @@ namespace hex::lang { switch (currType->getType()) { case ASTNode::Type::Struct: return this->createStructPattern(varDeclNode, offset); + case ASTNode::Type::Union: + return this->createUnionPattern(varDeclNode, offset); case ASTNode::Type::Enum: return this->createEnumPattern(varDeclNode, offset); case ASTNode::Type::Bitfield: @@ -208,6 +276,12 @@ namespace hex::lang { this->m_types.emplace(structNode->getName(), structNode); } break; + case ASTNode::Type::Union: + { + auto *unionNode = static_cast(node); + this->m_types.emplace(unionNode->getName(), unionNode); + } + break; case ASTNode::Type::Enum: { auto *enumNode = static_cast(node); diff --git a/source/lang/lexer.cpp b/source/lang/lexer.cpp index 3639913aa..fe2913f24 100644 --- a/source/lang/lexer.cpp +++ b/source/lang/lexer.cpp @@ -135,6 +135,8 @@ namespace hex::lang { if (identifier == "struct") tokens.push_back({ .type = Token::Type::Keyword, .keywordToken = { .keyword = Token::KeywordToken::Keyword::Struct } }); + else if (identifier == "union") + tokens.push_back({ .type = Token::Type::Keyword, .keywordToken = { .keyword = Token::KeywordToken::Keyword::Union } }); else if (identifier == "using") tokens.push_back({ .type = Token::Type::Keyword, .keywordToken = { .keyword = Token::KeywordToken::Keyword::Using } }); else if (identifier == "enum") diff --git a/source/lang/parser.cpp b/source/lang/parser.cpp index 5fd4bd54e..bbc66449e 100644 --- a/source/lang/parser.cpp +++ b/source/lang/parser.cpp @@ -75,6 +75,30 @@ namespace hex::lang { return new ASTNodeStruct(structName, nodes); } + ASTNode* parseUnion(TokenIter &curr) { + const std::string &unionName = curr[-2].identifierToken.identifier; + std::vector nodes; + + while (!tryConsume(curr, {Token::Type::ScopeClose})) { + if (tryConsume(curr, {Token::Type::Type, Token::Type::Identifier, Token::Type::EndOfExpression})) + nodes.push_back(parseBuiltinVariableDecl(curr)); + else if (tryConsume(curr, {Token::Type::Identifier, Token::Type::Identifier, Token::Type::EndOfExpression})) + nodes.push_back(parseCustomTypeVariableDecl(curr)); + else if (tryConsume(curr, {Token::Type::Type, Token::Type::Identifier, Token::Type::ArrayOpen, Token::Type::Integer, Token::Type::ArrayClose, Token::Type::EndOfExpression})) + nodes.push_back(parseBuiltinArrayDecl(curr)); + else if (tryConsume(curr, {Token::Type::Identifier, Token::Type::Identifier, Token::Type::ArrayOpen, Token::Type::Integer, Token::Type::ArrayClose, Token::Type::EndOfExpression})) + nodes.push_back(parseCustomTypeArrayDecl(curr)); + else break; + } + + if (!tryConsume(curr, {Token::Type::EndOfExpression})) { + for(auto &node : nodes) delete node; + return nullptr; + } + + return new ASTNodeUnion(unionName, nodes); + } + ASTNode* parseEnum(TokenIter &curr) { const std::string &enumName = curr[-4].identifierToken.identifier; const Token::TypeToken::Type underlyingType = curr[-2].typeToken.type; @@ -183,6 +207,15 @@ namespace hex::lang { } program.push_back(structAst); + } else if (curr[-3].keywordToken.keyword == Token::KeywordToken::Keyword::Union) { + auto unionAst = parseUnion(curr); + + if (unionAst == nullptr) { + for(auto &node : program) delete node; + return { }; + } + + program.push_back(unionAst); } else if (curr[-3].keywordToken.keyword == Token::KeywordToken::Keyword::Bitfield) { auto bitfieldAst = parseBitField(curr); diff --git a/source/views/view_pattern.cpp b/source/views/view_pattern.cpp index 3178e11f3..ab84b6c49 100644 --- a/source/views/view_pattern.cpp +++ b/source/views/view_pattern.cpp @@ -14,7 +14,7 @@ namespace hex { static TextEditor::LanguageDefinition langDef; if (!initialized) { static const char* const keywords[] = { - "using", "struct", "enum", "bitfield" + "using", "struct", "union", "enum", "bitfield" }; for (auto& k : keywords) langDef.mKeywords.insert(k);