diff --git a/plugins/libimhex/include/hex/pattern_language/ast_node.hpp b/plugins/libimhex/include/hex/pattern_language/ast_node.hpp index e21362241..88e59d370 100644 --- a/plugins/libimhex/include/hex/pattern_language/ast_node.hpp +++ b/plugins/libimhex/include/hex/pattern_language/ast_node.hpp @@ -2051,7 +2051,7 @@ namespace hex::pl { class ASTNodeCompoundStatement : public ASTNode { public: - ASTNodeCompoundStatement(std::vector statements) : m_statements(std::move(statements)) { + ASTNodeCompoundStatement(std::vector statements, bool newScope = false) : m_statements(std::move(statements)), m_newScope(newScope) { } @@ -2096,17 +2096,37 @@ namespace hex::pl { FunctionResult execute(Evaluator *evaluator) override { FunctionResult result; + auto variables = *evaluator->getScope(0).scope; + u32 startVariableCount = variables.size(); + + if (this->m_newScope) { + evaluator->pushScope(nullptr, variables); + } + for (const auto &statement : this->m_statements) { result = statement->execute(evaluator); if (result.first) return result; } + if (this->m_newScope) { + s64 stackSize = evaluator->getStack().size(); + for (u32 i = startVariableCount; i < variables.size(); i++) { + stackSize--; + delete variables[i]; + } + if (stackSize < 0) LogConsole::abortEvaluation("stack pointer underflow!", this); + evaluator->getStack().resize(stackSize); + + evaluator->popScope(); + } + return result; } public: std::vector m_statements; + bool m_newScope; }; }; \ No newline at end of file diff --git a/plugins/libimhex/include/hex/pattern_language/parser.hpp b/plugins/libimhex/include/hex/pattern_language/parser.hpp index 0011d7697..c40b63692 100644 --- a/plugins/libimhex/include/hex/pattern_language/parser.hpp +++ b/plugins/libimhex/include/hex/pattern_language/parser.hpp @@ -90,12 +90,14 @@ namespace hex::pl { ASTNode* parseMathematicalExpression(); ASTNode* parseFunctionDefinition(); + ASTNode* parseFunctionVariableDecl(); ASTNode* parseFunctionStatement(); ASTNode* parseFunctionVariableAssignment(); ASTNode* parseFunctionReturnStatement(); std::vector parseStatementBody(); ASTNode* parseFunctionConditional(); ASTNode* parseFunctionWhileLoop(); + ASTNode* parseFunctionForLoop(); void parseAttribute(Attributable *currNode); ASTNode* parseConditional(); diff --git a/plugins/libimhex/include/hex/pattern_language/token.hpp b/plugins/libimhex/include/hex/pattern_language/token.hpp index 80181aa58..c74d54688 100644 --- a/plugins/libimhex/include/hex/pattern_language/token.hpp +++ b/plugins/libimhex/include/hex/pattern_language/token.hpp @@ -37,6 +37,7 @@ namespace hex::pl { Parent, This, While, + For, Function, Return, Namespace @@ -280,6 +281,7 @@ namespace hex::pl { #define KEYWORD_PARENT COMPONENT(Keyword, Parent) #define KEYWORD_THIS COMPONENT(Keyword, This) #define KEYWORD_WHILE COMPONENT(Keyword, While) +#define KEYWORD_FOR COMPONENT(Keyword, For) #define KEYWORD_FUNCTION COMPONENT(Keyword, Function) #define KEYWORD_RETURN COMPONENT(Keyword, Return) #define KEYWORD_NAMESPACE COMPONENT(Keyword, Namespace) diff --git a/plugins/libimhex/source/pattern_language/lexer.cpp b/plugins/libimhex/source/pattern_language/lexer.cpp index 53e53daf5..fabdaeccc 100644 --- a/plugins/libimhex/source/pattern_language/lexer.cpp +++ b/plugins/libimhex/source/pattern_language/lexer.cpp @@ -408,6 +408,8 @@ namespace hex::pl { tokens.emplace_back(TOKEN(Keyword, This)); else if (identifier == "while") tokens.emplace_back(TOKEN(Keyword, While)); + else if (identifier == "for") + tokens.emplace_back(TOKEN(Keyword, For)); else if (identifier == "fn") tokens.emplace_back(TOKEN(Keyword, Function)); else if (identifier == "return") diff --git a/plugins/libimhex/source/pattern_language/parser.cpp b/plugins/libimhex/source/pattern_language/parser.cpp index d2e0ba477..8839a4035 100644 --- a/plugins/libimhex/source/pattern_language/parser.cpp +++ b/plugins/libimhex/source/pattern_language/parser.cpp @@ -470,6 +470,26 @@ namespace hex::pl { return create(new ASTNodeFunctionDefinition(getNamespacePrefixedName(functionName), params, body)); } + ASTNode* Parser::parseFunctionVariableDecl() { + ASTNode *statement; + auto type = parseType(true); + + if (MATCHES(sequence(IDENTIFIER))) { + auto identifier = getValue(-1).get(); + statement = parseMemberVariable(type); + + if (MATCHES(sequence(OPERATOR_ASSIGNMENT))) { + auto expression = parseMathematicalExpression(); + + statement = create(new ASTNodeCompoundStatement({ statement, create(new ASTNodeAssignment(identifier, expression)) })); + } + } + else + throwParseError("invalid variable declaration"); + + return statement; + } + ASTNode* Parser::parseFunctionStatement() { bool needsSemicolon = true; ASTNode *statement; @@ -484,6 +504,9 @@ namespace hex::pl { } else if (MATCHES(sequence(KEYWORD_WHILE, SEPARATOR_ROUNDBRACKETOPEN))) { statement = parseFunctionWhileLoop(); needsSemicolon = false; + } else if (MATCHES(sequence(KEYWORD_FOR, SEPARATOR_ROUNDBRACKETOPEN))) { + statement = parseFunctionForLoop(); + needsSemicolon = false; } else if (MATCHES(sequence(IDENTIFIER))) { auto originalPos = this->m_curr; parseNamespaceResolution(); @@ -494,24 +517,11 @@ namespace hex::pl { statement = parseFunctionCall(); } else { - statement = parseMemberVariable(parseType(true)); + statement = parseFunctionVariableDecl(); } } else if (peek(KEYWORD_BE) || peek(KEYWORD_LE) || peek(VALUETYPE_ANY)) { - auto type = parseType(true); - - if (MATCHES(sequence(IDENTIFIER))) { - auto identifier = getValue(-1).get(); - statement = parseMemberVariable(type); - - if (MATCHES(sequence(OPERATOR_ASSIGNMENT))) { - auto expression = parseMathematicalExpression(); - - statement = new ASTNodeCompoundStatement({ statement, new ASTNodeAssignment(identifier, expression) }); - } - } - else - throwParseError("invalid variable declaration"); + statement = parseFunctionVariableDecl(); } else throwParseError("invalid sequence", 0); @@ -608,6 +618,48 @@ namespace hex::pl { return create(new ASTNodeWhileStatement(condition, body)); } + ASTNode* Parser::parseFunctionForLoop() { + auto variable = parseFunctionVariableDecl(); + auto variableCleanup = SCOPE_GUARD { delete variable; }; + + if (!MATCHES(sequence(SEPARATOR_COMMA))) + throwParseError("expected ',' after for loop variable declaration"); + + auto condition = parseMathematicalExpression(); + auto conditionCleanup = SCOPE_GUARD { delete condition; }; + + if (!MATCHES(sequence(SEPARATOR_COMMA))) + throwParseError("expected ',' after for loop condition"); + + if (!MATCHES(sequence(IDENTIFIER, OPERATOR_ASSIGNMENT))) + throwParseError("expected for loop variable assignment"); + + auto postExpression = parseFunctionVariableAssignment(); + auto postExpressionCleanup = SCOPE_GUARD { delete postExpression; }; + + std::vector body; + + auto bodyCleanup = SCOPE_GUARD { + delete condition; + for (auto &statement : body) + delete statement; + }; + + if (!MATCHES(sequence(SEPARATOR_ROUNDBRACKETCLOSE))) + throwParseError("expected closing ')' after statement head"); + + body = parseStatementBody(); + + body.push_back(postExpression); + + variableCleanup.release(); + conditionCleanup.release(); + postExpressionCleanup.release(); + bodyCleanup.release(); + + return create(new ASTNodeCompoundStatement({ variable, create(new ASTNodeWhileStatement(condition, body)) }, true)); + } + /* Control flow */ // if ((parseMathematicalExpression)) { (parseMember) } diff --git a/source/views/view_pattern_editor.cpp b/source/views/view_pattern_editor.cpp index ff39d7552..af30cf157 100644 --- a/source/views/view_pattern_editor.cpp +++ b/source/views/view_pattern_editor.cpp @@ -24,7 +24,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", "this", "parent", "addressof", "sizeof", "$", "while", "fn", "return", "namespace" + "using", "struct", "union", "enum", "bitfield", "be", "le", "if", "else", "false", "true", "this", "parent", "addressof", "sizeof", "$", "while", "for", "fn", "return", "namespace" }; for (auto& k : keywords) langDef.mKeywords.insert(k);