patterns: Rewrite evaluation engine (#306)

* patterns: Rewrite most of the evaluator to mainly use polymorphism instead of just RTTI

* patterns: Fixed a couple of AST memory leaks

* patterns: Parse string operations correctly

* patterns: Various fixes and cleanup

* patterns: Implement primitive function definitions

Function parameters now need to provide their type in the definition

* patterns: Added function variable definition and assignment

* patterns: Added remaining function statements

* patterns: Added unsized and while-sized arrays

* patterns: Added multi variable declarations to functions

* patterns: Added std::format built-in function

* patterns: Allow passing custom types to functions

* patterns: Added attributes and new "format" attribute

* patterns: Use libfmt for std::print instead of custom version

* patterns: Remove unnecessary string compare function

* pattern: Fix preprocessor directives

* patterns: Fix unit tests

* patterns: Added cast expression

* patterns: Handle endianess in function parameters

* patterns: Added casting to different endian

* patterns: Added 'str' type for functions
This commit is contained in:
WerWolv
2021-09-21 21:29:18 +02:00
committed by GitHub
parent ed9e463550
commit c051f5d3e7
25 changed files with 2000 additions and 1903 deletions

View File

@@ -9,10 +9,10 @@ namespace hex::test {
TestPatternEnums() : TestPattern("Enums"){
auto testEnum = create<PatternDataEnum>("TestEnum", "testEnum", 0x120, sizeof(u32));
testEnum->setEnumValues({
{ s32(0x0000), "A" },
{ s32(0x1234), "B" },
{ s32(0x1235), "C" },
{ s32(0x1236), "D" },
{ u128(0x0000), "A" },
{ s128(0x1234), "B" },
{ u128(0x1235), "C" },
{ u128(0x1236), "D" },
});
addPattern(testEnum);

View File

@@ -19,7 +19,7 @@ namespace hex::test {
std::assert(255 == 0xFF, MSG);
std::assert(0xAA == 0b10101010, MSG);
std::assert(12345 != 67890, MSG);
std::assert(100ULL == 0x64ULL, MSG);
std::assert(100U == 0x64U, MSG);
std::assert(-100 == -0x64, MSG);
std::assert(3.14159F > 1.414D, MSG);
std::assert('A' == 0x41, MSG);

View File

@@ -33,7 +33,7 @@ namespace hex::test {
std::assert(0xFF00FF | 0x00AA00 == 0xFFAAFF, "| operator error");
std::assert(0xFFFFFF & 0x00FF00 == 0x00FF00, "& operator error");
std::assert(0xFFFFFF ^ 0x00AA00 == 0xFF55FF, "^ operator error");
std::assert(~0xFFFFFFFF == 0x00, "~ operator error");
std::assert(~0x00 == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, "~ operator error");
std::assert(0xAA >> 4 == 0x0A, ">> operator error");
std::assert(0xAA << 4 == 0xAA0, "<< operator error");
@@ -46,7 +46,7 @@ namespace hex::test {
// Special operators
std::assert($ == 0, "$ operator error");
std::assert((10 == 20) ? 30 : 40 == 40, "?: operator error");
std::assert(((10 == 20) ? 30 : 40) == 40, "?: operator error");
// Type operators
struct TypeTest { u32 x, y, z; };

View File

@@ -10,7 +10,7 @@ namespace hex::test {
auto testStruct = create<PatternDataStruct>("TestStruct", "testStruct", 0x100, sizeof(s32) + 20 + sizeof(u8[0x10]));
auto variable = create<PatternDataSigned>("s32", "variable", 0x100, sizeof(s32));
auto padding = create<PatternDataPadding>("", "", 0x100 + sizeof(s32), 20);
auto padding = create<PatternDataPadding>("padding", "", 0x100 + sizeof(s32), 20);
auto array = create<PatternDataStaticArray>("u8", "array", 0x100 + sizeof(s32) + 20, sizeof(u8[0x10]));
array->setEntries(create<PatternDataUnsigned>("u8", "", 0x100 + sizeof(s32) + 20, sizeof(u8)), 0x10);

View File

@@ -17,14 +17,14 @@ using namespace hex::test;
void addFunctions() {
hex::ContentRegistry::PatternLanguageFunctions::Namespace nsStd = { "std" };
hex::ContentRegistry::PatternLanguageFunctions::add(nsStd, "assert", 2, [](auto &ctx, auto params) {
auto condition = AS_TYPE(hex::pl::ASTNodeIntegerLiteral, params[0])->getValue();
auto message = AS_TYPE(hex::pl::ASTNodeStringLiteral, params[1])->getString();
hex::ContentRegistry::PatternLanguageFunctions::add(nsStd, "assert", 2, [](Evaluator *ctx, auto params) -> Token::Literal {
auto condition = Token::literalToBoolean(params[0]);
auto message = Token::literalToString(params[1], false);
if (LITERAL_COMPARE(condition, condition == 0))
ctx.getConsole().abortEvaluation(hex::format("assertion failed \"{0}\"", message.data()));
if (!condition)
LogConsole::abortEvaluation(hex::format("assertion failed \"{0}\"", message));
return nullptr;
return { };
});
}
@@ -67,7 +67,7 @@ int test(int argc, char **argv) {
hex::log::fatal("Error during compilation!");
if (auto error = language.getError(); error.has_value())
hex::log::info("Compile error: {}:{}", error->first, error->second);
hex::log::info("Compile error: {} : {}", error->first, error->second);
else
for (auto &[level, message] : language.getConsoleLog())
hex::log::info("Evaluate error: {}", message);
@@ -93,10 +93,10 @@ int test(int argc, char **argv) {
// Check if the produced patterns are the ones expected
for (u32 i = 0; i < currTest->getPatterns().size(); i++) {
auto &left = *patterns->at(i);
auto &right = *currTest->getPatterns().at(i);
auto &evaluatedPattern = *patterns->at(i);
auto &controlPattern = *currTest->getPatterns().at(i);
if (left != right) {
if (evaluatedPattern != controlPattern) {
hex::log::fatal("Pattern with name {}:{} didn't match template", patterns->at(i)->getTypeName(), patterns->at(i)->getVariableName());
return EXIT_FAILURE;
}