Implemented bitfield support into the pattern language

This commit is contained in:
WerWolv 2020-11-20 20:26:19 +01:00
parent 2f78a10e4c
commit e3cb078306
11 changed files with 205 additions and 34 deletions

View file

@ -15,6 +15,7 @@ namespace hex::lang {
TypeDecl,
Struct,
Enum,
Bitfield,
Scope,
};
@ -66,6 +67,18 @@ namespace hex::lang {
std::vector<ASTNode*> m_nodes;
};
class ASTNodeBitField : public ASTNode {
public:
explicit ASTNodeBitField(std::string name, std::vector<std::pair<std::string, size_t>> fields)
: ASTNode(Type::Bitfield), m_name(name), m_fields(fields) { }
const std::string& getName() const { return this->m_name; }
std::vector<std::pair<std::string, size_t>> &getFields() { return this->m_fields; }
private:
std::string m_name;
std::vector<std::pair<std::string, size_t>> m_fields;
};
class ASTNodeTypeDecl : public ASTNode {
public:
explicit ASTNodeTypeDecl(const Token::TypeToken::Type &type, const std::string &name, const std::string& customTypeName = "")

View file

@ -22,6 +22,7 @@ namespace hex::lang {
std::pair<PatternData*, size_t> createStructPattern(ASTNodeVariableDecl *varDeclNode, u64 offset);
std::pair<PatternData*, size_t> createEnumPattern(ASTNodeVariableDecl *varDeclNode, u64 offset);
std::pair<PatternData*, size_t> createBitfieldPattern(ASTNodeVariableDecl *varDeclNode, u64 offset);
std::pair<PatternData*, size_t> createArrayPattern(ASTNodeVariableDecl *varDeclNode, u64 offset);
std::pair<PatternData*, size_t> createStringPattern(ASTNodeVariableDecl *varDeclNode, u64 offset);
std::pair<PatternData*, size_t> createCustomTypePattern(ASTNodeVariableDecl *varDeclNode, u64 offset);

View file

@ -117,7 +117,6 @@ namespace hex::lang {
protected:
void createDefaultEntry(std::string value) {
static u64 id = 0;
ImGui::TableNextRow();
ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_SpanFullWidth);
ImGui::TableNextColumn();
@ -125,7 +124,7 @@ namespace hex::lang {
ImGui::TableNextColumn();
ImGui::Text("%s", this->getName().c_str());
ImGui::TableNextColumn();
ImGui::Text("0x%08lx : 0x%08lx", this->getOffset(), this->getOffset() + this->getSize());
ImGui::Text("0x%08lx : 0x%08lx", this->getOffset(), this->getOffset() + this->getSize() - 1);
ImGui::TableNextColumn();
ImGui::Text("0x%04lx", this->getSize());
ImGui::TableNextColumn();
@ -267,7 +266,7 @@ namespace hex::lang {
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());
ImGui::Text("0x%08lx : 0x%08lx", this->getOffset(), this->getOffset() + this->getSize() - 1);
ImGui::TableNextColumn();
ImGui::Text("0x%04lx", this->getSize());
ImGui::TableNextColumn();
@ -312,7 +311,7 @@ namespace hex::lang {
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());
ImGui::Text("0x%08lx : 0x%08lx", this->getOffset(), this->getOffset() + this->getSize() - 1);
ImGui::TableNextColumn();
ImGui::Text("0x%04lx", this->getSize());
ImGui::TableNextColumn();
@ -394,5 +393,65 @@ namespace hex::lang {
std::vector<std::pair<u64, std::string>> m_enumValues;
};
class PatternDataBitfield : public PatternData {
public:
PatternDataBitfield(u64 offset, size_t size, const std::string &name, const std::string &bitfieldName, std::vector<std::pair<std::string, size_t>> fields, u32 color = 0)
: PatternData(Type::Enum, offset, size, name, color), m_bitfieldName(bitfieldName), m_fields(fields) { }
void createEntry(prv::Provider* &provider) override {
u64 value = 0;
provider->read(this->getOffset(), &value, this->getSize());
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
ImGui::TableNextColumn();
ImGui::ColorButton("color", ImColor(0x00FFFFFF), 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) {
u16 bitOffset = 0;
for (auto &[entryName, entrySize] : this->m_fields) {
ImGui::TableNextRow();
ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_SpanFullWidth);
ImGui::TableNextColumn();
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip);
ImGui::TableNextColumn();
ImGui::Text("%s", entryName.c_str());
ImGui::TableNextColumn();
ImGui::Text("0x%08lx : 0x%08lx", this->getOffset() + (bitOffset >> 3), this->getOffset() + ((bitOffset + entrySize) >> 3) - 1);
ImGui::TableNextColumn();
if (entrySize == 1)
ImGui::Text("%llu bit", entrySize);
else
ImGui::Text("%llu bits", entrySize);
ImGui::TableNextColumn();
ImGui::Text("%s", entryName.c_str());
ImGui::TableNextColumn();
ImGui::Text("%llx", hex::extract((bitOffset + entrySize) - 1, bitOffset, value));
bitOffset += entrySize;
}
ImGui::TreePop();
}
}
std::string getTypeName() override {
return "bitfield " + this->m_bitfieldName;
}
private:
std::string m_bitfieldName;
std::vector<std::pair<std::string, size_t>> m_fields;
};
}

View file

@ -26,7 +26,8 @@ namespace hex::lang {
enum class Keyword {
Struct,
Using,
Enum
Enum,
Bitfield
} keyword;
} keywordToken;
struct IdentifierToken {

View file

@ -75,6 +75,11 @@ namespace hex {
return result;
}
[[nodiscard]] constexpr inline u64 extract(u8 from, u8 to, const u64 &value) {
u64 mask = (std::numeric_limits<u64>::max() >> (63 - (from - to))) << to;
return (value & mask) >> to;
}
class ScopeExit {
public:

View file

@ -1,5 +1,6 @@
#include "lang/evaluator.hpp"
#include <bit>
#include <unordered_map>
namespace hex::lang {
@ -87,6 +88,22 @@ namespace hex::lang {
return { new PatternDataEnum(offset, size, varDeclNode->getVariableName(), enumType->getName(), enumType->getValues()), size };
}
std::pair<PatternData*, size_t> Evaluator::createBitfieldPattern(ASTNodeVariableDecl *varDeclNode, u64 offset) {
auto *bitfieldType = static_cast<ASTNodeBitField*>(this->m_types[varDeclNode->getCustomVariableTypeName()]);
if (bitfieldType == nullptr)
return { nullptr, 0 };
size_t size = 0;
for (auto &[fieldName, fieldSize] : bitfieldType->getFields())
size += fieldSize;
size = std::bit_ceil(size) / 8;
return { new PatternDataBitfield(offset, size, varDeclNode->getVariableName(), bitfieldType->getName(), bitfieldType->getFields()), size };
}
std::pair<PatternData*, size_t> Evaluator::createArrayPattern(ASTNodeVariableDecl *varDeclNode, u64 offset) {
std::vector<PatternData*> entries;
@ -135,6 +152,8 @@ namespace hex::lang {
return this->createStructPattern(varDeclNode, offset);
case ASTNode::Type::Enum:
return this->createEnumPattern(varDeclNode, offset);
case ASTNode::Type::Bitfield:
return this->createBitfieldPattern(varDeclNode, offset);
case ASTNode::Type::TypeDecl:
return this->createBuiltInTypePattern(varDeclNode, offset);
}
@ -195,6 +214,12 @@ namespace hex::lang {
this->m_types.emplace(enumNode->getName(), enumNode);
}
break;
case ASTNode::Type::Bitfield:
{
auto *bitfieldNode = static_cast<ASTNodeBitField*>(node);
this->m_types.emplace(bitfieldNode->getName(), bitfieldNode);
}
break;
case ASTNode::Type::TypeDecl:
{
auto *typeDeclNode = static_cast<ASTNodeTypeDecl*>(node);

View file

@ -102,31 +102,31 @@ namespace hex::lang {
if (std::isblank(c) || std::isspace(c)) {
offset += 1;
} else if (c == ';') {
tokens.push_back({.type = Token::Type::EndOfExpression});
tokens.push_back({ .type = Token::Type::EndOfExpression });
offset += 1;
} else if (c == '{') {
tokens.push_back({.type = Token::Type::ScopeOpen});
tokens.push_back({ .type = Token::Type::ScopeOpen });
offset += 1;
} else if (c == '}') {
tokens.push_back({.type = Token::Type::ScopeClose});
tokens.push_back({ .type = Token::Type::ScopeClose });
offset += 1;
} else if (c == '[') {
tokens.push_back({.type = Token::Type::ArrayOpen});
tokens.push_back({ .type = Token::Type::ArrayOpen });
offset += 1;
} else if (c == ']') {
tokens.push_back({.type = Token::Type::ArrayClose});
offset += 1;
} else if (c == ',') {
tokens.push_back({.type = Token::Type::Separator});
tokens.push_back({ .type = Token::Type::Separator });
offset += 1;
} else if (c == '@') {
tokens.push_back({.type = Token::Type::Operator, .operatorToken = { .op = Token::OperatorToken::Operator::AtDeclaration}});
tokens.push_back({ .type = Token::Type::Operator, .operatorToken = { .op = Token::OperatorToken::Operator::AtDeclaration } });
offset += 1;
} else if (c == '=') {
tokens.push_back({.type = Token::Type::Operator, .operatorToken = { .op = Token::OperatorToken::Operator::Assignment}});
tokens.push_back({ .type = Token::Type::Operator, .operatorToken = { .op = Token::OperatorToken::Operator::Assignment } });
offset += 1;
} else if (c == ':') {
tokens.push_back({.type = Token::Type::Operator, .operatorToken = { .op = Token::OperatorToken::Operator::Inherit}});
tokens.push_back({ .type = Token::Type::Operator, .operatorToken = { .op = Token::OperatorToken::Operator::Inherit } });
offset += 1;
} else if (std::isalpha(c)) {
std::string identifier = matchTillInvalid(&code[offset], [](char c) -> bool { return std::isalnum(c) || c == '_'; });
@ -134,42 +134,44 @@ namespace hex::lang {
// Check for reserved keywords
if (identifier == "struct")
tokens.push_back({.type = Token::Type::Keyword, .keywordToken = { .keyword = Token::KeywordToken::Keyword::Struct}});
tokens.push_back({ .type = Token::Type::Keyword, .keywordToken = { .keyword = Token::KeywordToken::Keyword::Struct } });
else if (identifier == "using")
tokens.push_back({.type = Token::Type::Keyword, .keywordToken = { .keyword = Token::KeywordToken::Keyword::Using}});
tokens.push_back({ .type = Token::Type::Keyword, .keywordToken = { .keyword = Token::KeywordToken::Keyword::Using } });
else if (identifier == "enum")
tokens.push_back({.type = Token::Type::Keyword, .keywordToken = { .keyword = Token::KeywordToken::Keyword::Enum}});
tokens.push_back({ .type = Token::Type::Keyword, .keywordToken = { .keyword = Token::KeywordToken::Keyword::Enum } });
else if (identifier == "bitfield")
tokens.push_back({ .type = Token::Type::Keyword, .keywordToken = { .keyword = Token::KeywordToken::Keyword::Bitfield } });
// Check for built-in types
else if (identifier == "u8")
tokens.push_back({.type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Unsigned8Bit }});
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Unsigned8Bit } });
else if (identifier == "s8")
tokens.push_back({.type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Signed8Bit }});
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Signed8Bit } });
else if (identifier == "u16")
tokens.push_back({.type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Unsigned16Bit }});
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Unsigned16Bit } });
else if (identifier == "s16")
tokens.push_back({.type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Signed16Bit }});
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Signed16Bit } });
else if (identifier == "u32")
tokens.push_back({.type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Unsigned32Bit }});
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Unsigned32Bit } });
else if (identifier == "s32")
tokens.push_back({.type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Signed32Bit }});
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Signed32Bit } });
else if (identifier == "u64")
tokens.push_back({.type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Unsigned64Bit }});
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Unsigned64Bit } });
else if (identifier == "s64")
tokens.push_back({.type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Signed64Bit }});
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Signed64Bit } });
else if (identifier == "u128")
tokens.push_back({.type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Unsigned128Bit }});
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Unsigned128Bit } });
else if (identifier == "s128")
tokens.push_back({.type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Signed128Bit }});
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Signed128Bit } });
else if (identifier == "float")
tokens.push_back({.type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Float }});
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Float } });
else if (identifier == "double")
tokens.push_back({.type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Double }});
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Double } });
// If it's not a keyword and a builtin type, it has to be an identifier
else
tokens.push_back({.type = Token::Type::Identifier, .identifierToken = { .identifier = identifier}});
tokens.push_back({.type = Token::Type::Identifier, .identifierToken = { .identifier = identifier } });
offset += identifier.length();
} else if (std::isdigit(c)) {
@ -181,12 +183,12 @@ namespace hex::lang {
if (!integer.has_value())
return { ResultLexicalError, {}};
tokens.push_back({.type = Token::Type::Integer, .integerToken = { .integer = integer.value() }});
tokens.push_back({ .type = Token::Type::Integer, .integerToken = { .integer = integer.value() } });
offset += (end - &code[offset]);
} else return { ResultLexicalError, {}};
}
tokens.push_back({.type = Token::Type::EndOfProgram});
tokens.push_back({ .type = Token::Type::EndOfProgram });
return { ResultSuccess, tokens };
}

View file

@ -121,6 +121,26 @@ namespace hex::lang {
return enumNode;
}
ASTNode *parseBitField(TokenIter &curr) {
const std::string &bitfieldName = curr[-2].identifierToken.identifier;
std::vector<std::pair<std::string, size_t>> fields;
while (!tryConsume(curr, {Token::Type::ScopeClose})) {
if (tryConsume(curr, {Token::Type::Identifier, Token::Type::Operator, Token::Type::Integer, Token::Type::EndOfExpression})) {
if (curr[-3].operatorToken.op != Token::OperatorToken::Operator::Inherit)
return nullptr;
fields.emplace_back(curr[-4].identifierToken.identifier, curr[-2].integerToken.integer);
}
else break;
}
if (!tryConsume(curr, {Token::Type::EndOfExpression}))
return nullptr;
return new ASTNodeBitField(bitfieldName, fields);
}
ASTNode *parseScope(TokenIter &curr) {
return new ASTNodeScope(parseTillToken(curr, Token::Type::ScopeClose));
}
@ -163,12 +183,21 @@ namespace hex::lang {
}
program.push_back(structAst);
} else if (curr[-3].keywordToken.keyword == Token::KeywordToken::Keyword::Bitfield) {
auto bitfieldAst = parseBitField(curr);
if (bitfieldAst == nullptr) {
for(auto &node : program) delete node;
return { };
}
program.push_back(bitfieldAst);
}
return program;
} // Enum
if (tryConsume(curr, { Token::Type::Keyword, Token::Type::Identifier, Token::Type::Operator, Token::Type::Type, Token::Type::ScopeOpen })) {
else if (tryConsume(curr, { Token::Type::Keyword, Token::Type::Identifier, Token::Type::Operator, Token::Type::Type, Token::Type::ScopeOpen })) {
if (curr[-5].keywordToken.keyword == Token::KeywordToken::Keyword::Enum) {
auto enumAst = parseEnum(curr);
@ -181,7 +210,6 @@ namespace hex::lang {
}
return program;
// Scope
} else if (tryConsume(curr, { Token::Type::ScopeOpen })) {
program.push_back(parseScope(curr));

View file

@ -61,6 +61,27 @@ namespace hex::lang {
if (!constantNames.insert(name).second)
return false;
}
case ASTNode::Type::Bitfield:
{
// Check for duplicate type name
auto bitfieldNode = static_cast<ASTNodeBitField*>(node);
if (!typeNames.insert(bitfieldNode->getName()).second)
return false;
size_t bitfieldSize = 0;
// Check for duplicate constant names
std::unordered_set<std::string> flagNames;
for (const auto &[name, size] : bitfieldNode->getFields()) {
if (!flagNames.insert(name).second)
return false;
bitfieldSize += size;
}
if (bitfieldSize > 64)
return false;
}
break;
}
}

View file

@ -109,6 +109,18 @@ namespace hex {
"}"
);
DrawTitle("Bitfields");
ImGui::TextWrapped(
"To decode values that are stored in fields that don't follow the typical 8 bit alignment, bitfields can be used. "
"The size of these fields get specified in numbers of bits.");
DrawCodeSegment("bitfield", 70,
"bitfield Permission {\n"
" r : 1;\n"
" w : 1;\n"
" x : 1;\n"
"}"
);
DrawTitle("Enum");
ImGui::TextWrapped(
"If a value can only be a few specific values with special meaning, an enum can be used. "

View file

@ -14,7 +14,7 @@ namespace hex {
static TextEditor::LanguageDefinition langDef;
if (!initialized) {
static const char* const keywords[] = {
"using", "struct", "enum"
"using", "struct", "enum", "bitfield"
};
for (auto& k : keywords)
langDef.mKeywords.insert(k);
@ -96,6 +96,10 @@ namespace hex {
if (ImGui::Begin("Pattern", &this->m_windowOpen, ImGuiWindowFlags_None)) {
this->m_textEditor.Render("Pattern");
if (this->m_textEditor.IsTextChanged()) {
this->parsePattern(this->m_textEditor.GetText().data());
}
}
ImGui::End();