1
0
mirror of synced 2024-09-25 03:58:27 +02:00

patterns: Added while statement for array sizing

This commit is contained in:
WerWolv 2021-06-17 23:13:58 +02:00
parent 3cef784f75
commit 21f8fb4090
9 changed files with 156 additions and 76 deletions

View File

@ -126,23 +126,27 @@ namespace hex::plugin::builtin {
std::string message;
for (auto& param : params) {
if (auto integerLiteral = dynamic_cast<ASTNodeIntegerLiteral*>(param); integerLiteral != nullptr) {
switch (integerLiteral->getType()) {
case Token::ValueType::Character: message += std::get<s8>(integerLiteral->getValue()); break;
case Token::ValueType::Unsigned8Bit: message += std::to_string(std::get<u8>(integerLiteral->getValue())); break;
case Token::ValueType::Signed8Bit: message += std::to_string(std::get<s8>(integerLiteral->getValue())); break;
case Token::ValueType::Unsigned16Bit: message += std::to_string(std::get<u16>(integerLiteral->getValue())); break;
case Token::ValueType::Signed16Bit: message += std::to_string(std::get<s16>(integerLiteral->getValue())); break;
case Token::ValueType::Unsigned32Bit: message += std::to_string(std::get<u32>(integerLiteral->getValue())); break;
case Token::ValueType::Signed32Bit: message += std::to_string(std::get<s32>(integerLiteral->getValue())); break;
case Token::ValueType::Unsigned64Bit: message += std::to_string(std::get<u64>(integerLiteral->getValue())); break;
case Token::ValueType::Signed64Bit: message += std::to_string(std::get<s64>(integerLiteral->getValue())); break;
case Token::ValueType::Unsigned128Bit: message += hex::to_string(std::get<u128>(integerLiteral->getValue())); break;
case Token::ValueType::Signed128Bit: message += hex::to_string(std::get<s128>(integerLiteral->getValue())); break;
case Token::ValueType::Float: message += std::to_string(std::get<float>(integerLiteral->getValue())); break;
case Token::ValueType::Double: message += std::to_string(std::get<double>(integerLiteral->getValue())); break;
case Token::ValueType::Boolean: message += std::get<s32>(integerLiteral->getValue()) ? "true" : "false"; break;
case Token::ValueType::CustomType: message += "< Custom Type >"; break;
}
std::visit([&](auto &&value) {
switch (integerLiteral->getType()) {
case lang::Token::ValueType::Character: message += (char)value; break;
case lang::Token::ValueType::Boolean: message += value == 0 ? "false" : "true"; break;
case lang::Token::ValueType::Unsigned8Bit:
case lang::Token::ValueType::Unsigned16Bit:
case lang::Token::ValueType::Unsigned32Bit:
case lang::Token::ValueType::Unsigned64Bit:
case lang::Token::ValueType::Unsigned128Bit:
message += std::to_string(static_cast<u64>(value));
break;
case lang::Token::ValueType::Signed8Bit:
case lang::Token::ValueType::Signed16Bit:
case lang::Token::ValueType::Signed32Bit:
case lang::Token::ValueType::Signed64Bit:
case lang::Token::ValueType::Signed128Bit:
message += std::to_string(static_cast<s64>(value));
break;
default: message += "< Custom Type >";
}
}, integerLiteral->getValue());
}
else if (auto stringLiteral = dynamic_cast<ASTNodeStringLiteral*>(param); stringLiteral != nullptr)
message += stringLiteral->getString();

View File

@ -108,6 +108,9 @@ namespace hex {
template<typename T>
struct always_false : std::false_type {};
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
template<typename T>
constexpr T changeEndianess(T value, std::endian endian) {
if (endian == std::endian::native)

View File

@ -482,6 +482,30 @@ namespace hex::lang {
std::vector<ASTNode*> m_trueBody, m_falseBody;
};
class ASTNodeWhileStatement : public ASTNode {
public:
explicit ASTNodeWhileStatement(ASTNode *condition) : ASTNode(), m_condition(condition) { }
~ASTNodeWhileStatement() override {
delete this->m_condition;
}
ASTNodeWhileStatement(const ASTNodeWhileStatement &other) : ASTNode(other) {
this->m_condition = other.m_condition->clone();
}
[[nodiscard]] ASTNode* clone() const override {
return new ASTNodeWhileStatement(*this);
}
[[nodiscard]] ASTNode* getCondition() {
return this->m_condition;
}
private:
ASTNode *m_condition;
};
class ASTNodeFunctionCall : public ASTNode {
public:
explicit ASTNodeFunctionCall(std::string_view functionName, std::vector<ASTNode*> params)

View File

@ -73,6 +73,7 @@ namespace hex::lang {
void parseAttribute(Attributable *currNode);
ASTNode* parseConditional();
ASTNode* parseWhileStatement();
ASTNode* parseType(s32 startIndex);
ASTNode* parseUsingDeclaration();
ASTNode* parsePadding();

View File

@ -32,7 +32,8 @@ namespace hex::lang {
BigEndian,
If,
Else,
Parent
Parent,
While
};
enum class Operator {
@ -225,6 +226,7 @@ namespace hex::lang {
#define KEYWORD_IF COMPONENT(Keyword, If)
#define KEYWORD_ELSE COMPONENT(Keyword, Else)
#define KEYWORD_PARENT COMPONENT(Keyword, Parent)
#define KEYWORD_WHILE COMPONENT(Keyword, While)
#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, ""

View File

@ -141,9 +141,6 @@ namespace hex::lang {
}
ASTNodeIntegerLiteral* Evaluator::evaluateRValue(ASTNodeRValue *node) {
if (this->m_currMembers.empty() && this->m_globalMembers.empty())
this->getConsole().abortEvaluation("no variables available");
if (node->getPath().size() == 1) {
if (auto part = std::get_if<std::string>(&node->getPath()[0]); part != nullptr && *part == "$")
return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned64Bit, this->m_currOffset });
@ -201,7 +198,7 @@ namespace hex::lang {
for (auto &param : node->getParams()) {
if (auto numericExpression = dynamic_cast<ASTNodeNumericExpression*>(param); numericExpression != nullptr)
evaluatedParams.push_back(this->evaluateMathematicalExpression(numericExpression));
else if (auto typeOperatorExpression = dynamic_cast<ASTNodeTypeOperator*>(param))
else if (auto typeOperatorExpression = dynamic_cast<ASTNodeTypeOperator*>(param); typeOperatorExpression != nullptr)
evaluatedParams.push_back(this->evaluateTypeOperator(typeOperatorExpression));
else if (auto stringLiteral = dynamic_cast<ASTNodeStringLiteral*>(param); stringLiteral != nullptr)
evaluatedParams.push_back(stringLiteral->clone());
@ -486,6 +483,8 @@ namespace hex::lang {
pattern = new PatternDataSigned(this->m_currOffset, typeSize);
else if (Token::isFloatingPoint(type))
pattern = new PatternDataFloat(this->m_currOffset, typeSize);
else if (type == Token::ValueType::Padding)
pattern = new PatternDataPadding(this->m_currOffset, 1);
else
this->getConsole().abortEvaluation("invalid builtin type");
@ -734,6 +733,7 @@ namespace hex::lang {
PatternData* Evaluator::evaluateArray(ASTNodeArrayVariableDecl *node) {
// Evaluate placement of array
if (auto offset = dynamic_cast<ASTNodeNumericExpression*>(node->getPlacementOffset()); offset != nullptr) {
auto valueNode = evaluateMathematicalExpression(offset);
ON_SCOPE_EXIT { delete valueNode; };
@ -745,6 +745,7 @@ namespace hex::lang {
}, valueNode->getValue());
}
// Check if placed in range of the data
if (this->m_currOffset < this->m_provider->getBaseAddress() || this->m_currOffset >= this->m_provider->getActualSize() + this->m_provider->getBaseAddress()) {
if (node->getPlacementOffset() != nullptr)
this->getConsole().abortEvaluation("variable placed out of range");
@ -754,59 +755,19 @@ namespace hex::lang {
auto startOffset = this->m_currOffset;
ASTNodeIntegerLiteral *valueNode;
u64 arraySize = 0;
if (node->getSize() != nullptr) {
if (auto sizeNumericExpression = dynamic_cast<ASTNodeNumericExpression*>(node->getSize()); sizeNumericExpression != nullptr)
valueNode = evaluateMathematicalExpression(sizeNumericExpression);
else
this->getConsole().abortEvaluation("array size not a numeric expression");
ON_SCOPE_EXIT { delete valueNode; };
arraySize = std::visit([this, node, type = valueNode->getType()] (auto &&value) {
if (Token::isFloatingPoint(type))
this->getConsole().abortEvaluation("array size must be an integer value");
return static_cast<u64>(value);
}, valueNode->getValue());
if (auto typeDecl = dynamic_cast<ASTNodeTypeDecl*>(node->getType()); typeDecl != nullptr) {
if (auto builtinType = dynamic_cast<ASTNodeBuiltinType*>(typeDecl->getType()); builtinType != nullptr) {
if (builtinType->getType() == Token::ValueType::Padding) {
this->m_currOffset += arraySize;
return new PatternDataPadding(startOffset, arraySize);
}
}
}
} else {
if (auto typeDecl = dynamic_cast<ASTNodeTypeDecl*>(node->getType()); typeDecl != nullptr) {
if (auto builtinType = dynamic_cast<ASTNodeBuiltinType*>(typeDecl->getType()); builtinType != nullptr) {
std::vector<u8> bytes(Token::getTypeSize(builtinType->getType()), 0x00);
u64 offset = startOffset;
do {
this->m_provider->read(offset, bytes.data(), bytes.size());
offset += bytes.size();
arraySize++;
} while (!std::all_of(bytes.begin(), bytes.end(), [](u8 byte){ return byte == 0x00; }) && offset < this->m_provider->getSize());
}
}
}
std::vector<PatternData*> entries;
std::optional<u32> color;
for (s128 i = 0; i < arraySize; i++) {
auto addEntry = [this, node, &entries, &color](u64 index) {
PatternData *entry;
if (auto typeDecl = dynamic_cast<ASTNodeTypeDecl*>(node->getType()); typeDecl != nullptr)
entry = this->evaluateType(typeDecl);
else if (auto builtinTypeDecl = dynamic_cast<ASTNodeBuiltinType*>(node->getType()); builtinTypeDecl != nullptr) {
else if (auto builtinTypeDecl = dynamic_cast<ASTNodeBuiltinType*>(node->getType()); builtinTypeDecl != nullptr)
entry = this->evaluateBuiltinType(builtinTypeDecl);
}
else
this->getConsole().abortEvaluation("ASTNodeVariableDecl had an invalid type. This is a bug!");
entry->setVariableName(hex::format("[{0}]", (u64)i));
entry->setVariableName(hex::format("[{0}]", index));
entry->setEndian(this->getCurrentEndian());
if (!color.has_value())
@ -815,17 +776,76 @@ namespace hex::lang {
if (this->m_currOffset > this->m_provider->getActualSize() + this->m_provider->getBaseAddress()) {
delete entry;
break;
return;
}
entries.push_back(entry);
};
auto sizeNode = node->getSize();
if (auto numericExpression = dynamic_cast<ASTNodeNumericExpression*>(sizeNode); numericExpression != nullptr) {
// Parse explicit size of array
auto valueNode = this->evaluateMathematicalExpression(numericExpression);
ON_SCOPE_EXIT { delete valueNode; };
auto arraySize = std::visit([this, node, type = valueNode->getType()] (auto &&value) {
if (Token::isFloatingPoint(type))
this->getConsole().abortEvaluation("array size must be an integer value");
return static_cast<u64>(value);
}, valueNode->getValue());
for (u64 i = 0; i < arraySize; i++) {
addEntry(i);
}
} else if (auto whileLoopExpression = dynamic_cast<ASTNodeWhileStatement*>(sizeNode); whileLoopExpression != nullptr) {
// Parse while loop based size of array
auto conditionNode = this->evaluateMathematicalExpression(static_cast<ASTNodeNumericExpression*>(whileLoopExpression->getCondition()));
ON_SCOPE_EXIT { delete conditionNode; };
u64 index = 0;
while (std::visit([](auto &&value) { return value != 0; }, conditionNode->getValue())) {
addEntry(index);
index++;
delete conditionNode;
conditionNode = this->evaluateMathematicalExpression(static_cast<ASTNodeNumericExpression*>(whileLoopExpression->getCondition()));
}
} else {
// Parse unsized array
if (auto typeDecl = dynamic_cast<ASTNodeTypeDecl*>(node->getType()); typeDecl != nullptr) {
if (auto builtinType = dynamic_cast<ASTNodeBuiltinType*>(typeDecl->getType()); builtinType != nullptr) {
std::vector<u8> bytes(Token::getTypeSize(builtinType->getType()), 0x00);
u64 offset = startOffset;
u64 index = 0;
do {
this->m_provider->read(offset, bytes.data(), bytes.size());
offset += bytes.size();
addEntry(index);
index++;
} while (!std::all_of(bytes.begin(), bytes.end(), [](u8 byte){ return byte == 0x00; }) && offset < this->m_provider->getSize());
}
}
}
auto deleteEntries = SCOPE_GUARD {
for (auto &entry : entries)
delete entry;
};
if (auto typeDecl = dynamic_cast<ASTNodeTypeDecl*>(node->getType()); typeDecl != nullptr) {
if (auto builtinType = dynamic_cast<ASTNodeBuiltinType *>(typeDecl->getType()); builtinType != nullptr) {
if (builtinType->getType() == Token::ValueType::Padding)
return new PatternDataPadding(startOffset, this->m_currOffset - startOffset);
}
}
PatternData *pattern;
if (entries.empty()) {
if (entries.empty())
pattern = new PatternDataPadding(startOffset, 0);
}
else if (dynamic_cast<PatternDataCharacter*>(entries[0]) != nullptr)
pattern = new PatternDataString(startOffset, (this->m_currOffset - startOffset), color.value_or(0));
else if (dynamic_cast<PatternDataCharacter16*>(entries[0]) != nullptr)
@ -836,6 +856,8 @@ namespace hex::lang {
auto arrayPattern = new PatternDataArray(startOffset, (this->m_currOffset - startOffset), color.value_or(0));
arrayPattern->setEntries(entries);
deleteEntries.release();
pattern = arrayPattern;
}

View File

@ -223,7 +223,7 @@ namespace hex::lang {
if (string.empty())
return { };
if (!string[0] != '\'')
if (string[0] != '\'')
return { };
@ -234,7 +234,7 @@ namespace hex::lang {
auto &[c, charSize] = character.value();
if (string.length() >= charSize || string[charSize] != '\'')
if (string.length() >= charSize + 2 && string[charSize + 1] != '\'')
return { };
return {{ c, charSize + 2 }};
@ -422,6 +422,8 @@ namespace hex::lang {
tokens.emplace_back(VALUE_TOKEN(Integer, Token::IntegerLiteral(Token::ValueType::Boolean, s32(1))));
else if (identifier == "parent")
tokens.emplace_back(TOKEN(Keyword, Parent));
else if (identifier == "while")
tokens.emplace_back(TOKEN(Keyword, While));
// Check for built-in types
else if (identifier == "u8")

View File

@ -84,7 +84,7 @@ namespace hex::lang {
else
throwParseError("expected member name or 'parent' keyword", -1);
} else
return new ASTNodeRValue(path);
return TO_NUMERIC_EXPRESSION(new ASTNodeRValue(path));
}
// <Integer|((parseMathematicalExpression))>
@ -108,7 +108,7 @@ namespace hex::lang {
ASTNodeRValue::Path path;
return TO_NUMERIC_EXPRESSION(this->parseRValue(path));
} else if (MATCHES(sequence(OPERATOR_DOLLAR))) {
return new ASTNodeRValue({ "$" });
return TO_NUMERIC_EXPRESSION(new ASTNodeRValue({ "$" }));
} else if (MATCHES(oneOf(OPERATOR_ADDRESSOF, OPERATOR_SIZEOF) && sequence(SEPARATOR_ROUNDBRACKETOPEN))) {
auto op = getValue<Token::Operator>(-2);
@ -395,6 +395,22 @@ namespace hex::lang {
return new ASTNodeConditionalStatement(condition, trueBody, falseBody);
}
// while ((parseMathematicalExpression))
ASTNode* Parser::parseWhileStatement() {
auto condition = parseMathematicalExpression();
auto cleanup = SCOPE_GUARD {
delete condition;
};
if (!MATCHES(sequence(SEPARATOR_ROUNDBRACKETCLOSE)))
throwParseError("expected closing ')' after while head");
cleanup.release();
return new ASTNodeWhileStatement(condition);
}
/* Type declarations */
// [be|le] <Identifier|u8|u16|u32|u64|u128|s8|s16|s32|s64|s128|float|double>
@ -459,7 +475,10 @@ namespace hex::lang {
auto sizeCleanup = SCOPE_GUARD { delete size; };
if (!MATCHES(sequence(SEPARATOR_SQUAREBRACKETCLOSE))) {
size = parseMathematicalExpression();
if (MATCHES(sequence(KEYWORD_WHILE, SEPARATOR_ROUNDBRACKETOPEN)))
size = parseWhileStatement();
else
size = parseMathematicalExpression();
if (!MATCHES(sequence(SEPARATOR_SQUAREBRACKETCLOSE)))
throwParseError("expected closing ']' at end of array declaration", -1);
@ -662,7 +681,10 @@ namespace hex::lang {
auto sizeCleanup = SCOPE_GUARD { delete size; };
if (!MATCHES(sequence(SEPARATOR_SQUAREBRACKETCLOSE))) {
size = parseMathematicalExpression();
if (MATCHES(sequence(KEYWORD_WHILE, SEPARATOR_ROUNDBRACKETOPEN)))
size = parseWhileStatement();
else
size = parseMathematicalExpression();
if (!MATCHES(sequence(SEPARATOR_SQUAREBRACKETCLOSE)))
throwParseError("expected closing ']' at end of array declaration", -1);

View File

@ -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", "parent", "addressof", "sizeof", "$"
"using", "struct", "union", "enum", "bitfield", "be", "le", "if", "else", "false", "true", "parent", "addressof", "sizeof", "$", "while"
};
for (auto& k : keywords)
langDef.mKeywords.insert(k);