diff --git a/CMakeLists.txt b/CMakeLists.txt index ba7e9da10..6b532c9d9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,7 +31,6 @@ add_subdirectory(main) add_custom_target(imhex ALL DEPENDS main) # Add unit tests -enable_testing() add_subdirectory(tests EXCLUDE_FROM_ALL) # Configure packaging diff --git a/lib/libimhex/include/hex/helpers/concepts.hpp b/lib/libimhex/include/hex/helpers/concepts.hpp index 100147034..ead4c3dad 100644 --- a/lib/libimhex/include/hex/helpers/concepts.hpp +++ b/lib/libimhex/include/hex/helpers/concepts.hpp @@ -159,3 +159,14 @@ namespace hex { concept has_size = sizeof(T) == Size; } + + +namespace hex { + + template + class Cloneable { + public: + [[nodiscard]] virtual T* clone() const = 0; + }; + +} \ No newline at end of file diff --git a/lib/libimhex/include/hex/pattern_language/ast_node.hpp b/lib/libimhex/include/hex/pattern_language/ast_node.hpp index 46dbd6ae5..945146df8 100644 --- a/lib/libimhex/include/hex/pattern_language/ast_node.hpp +++ b/lib/libimhex/include/hex/pattern_language/ast_node.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -18,14 +19,7 @@ namespace hex::pl { class PatternData; class Evaluator; - class ASTNode; - - class Clonable { - public: - [[nodiscard]] virtual ASTNode *clone() const = 0; - }; - - class ASTNode : public Clonable { + class ASTNode : public Cloneable { public: constexpr ASTNode() = default; @@ -42,7 +36,7 @@ namespace hex::pl { [[nodiscard]] virtual std::vector createPatterns(Evaluator *evaluator) const { return {}; } using FunctionResult = std::optional; - virtual FunctionResult execute(Evaluator *evaluator) const { evaluator->getConsole().abortEvaluation("cannot execute non-function statement", this); } + virtual FunctionResult execute(Evaluator *evaluator) const { LogConsole::abortEvaluation("cannot execute non-function statement", this); } private: u32 m_lineNumber = 1; @@ -86,8 +80,12 @@ namespace hex::pl { } Attributable(const Attributable &other) { - for (auto &attribute : other.m_attributes) - this->m_attributes.push_back(static_cast(attribute->clone())); + for (auto &attribute : other.m_attributes) { + if (auto node = dynamic_cast(attribute->clone())) + this->m_attributes.push_back(node); + else + delete node; + } } public: @@ -203,17 +201,17 @@ namespace hex::pl { [this](double left, PatternData *const &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); }, [this](char left, PatternData *const &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); }, [this](bool left, PatternData *const &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); }, - [this](std::string left, PatternData *const &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); }, + [this](const std::string &left, PatternData *const &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); }, [this](PatternData *const &left, u128 right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); }, [this](PatternData *const &left, i128 right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); }, [this](PatternData *const &left, double right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); }, [this](PatternData *const &left, char right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); }, [this](PatternData *const &left, bool right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); }, - [this](PatternData *const &left, std::string right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); }, - [this](PatternData *const &left, PatternData *right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); }, + [this](PatternData *const &left, const std::string &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); }, + [this](PatternData *const &left, PatternData *const &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); }, - [this](auto &&left, std::string right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); }, - [this](std::string left, auto &&right) -> ASTNode * { + [this](auto &&left, const std::string &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); }, + [this](const std::string &left, auto &&right) -> ASTNode * { switch (this->getOperator()) { case Token::Operator::Star: { @@ -226,7 +224,7 @@ namespace hex::pl { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); } }, - [this](std::string left, std::string right) -> ASTNode * { + [this](const std::string &left, const std::string &right) -> ASTNode * { switch (this->getOperator()) { case Token::Operator::Plus: return new ASTNodeLiteral(left + right); @@ -246,7 +244,7 @@ namespace hex::pl { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); } }, - [this](std::string left, char right) -> ASTNode * { + [this](const std::string &left, char right) -> ASTNode * { switch (this->getOperator()) { case Token::Operator::Plus: return new ASTNodeLiteral(left + right); @@ -254,7 +252,7 @@ namespace hex::pl { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); } }, - [this](char left, std::string right) -> ASTNode * { + [this](char left, const std::string &right) -> ASTNode * { switch (this->getOperator()) { case Token::Operator::Plus: return new ASTNodeLiteral(left + right); @@ -361,7 +359,7 @@ namespace hex::pl { }; auto condition = std::visit(overloaded { - [this](std::string value) -> bool { return !value.empty(); }, + [](const std::string &value) -> bool { return !value.empty(); }, [this](PatternData *const &) -> bool { LogConsole::abortEvaluation("cannot cast custom type to bool", this); }, [](auto &&value) -> bool { return bool(value); } }, first->getValue()); @@ -456,8 +454,12 @@ namespace hex::pl { auto type = this->m_type->evaluate(evaluator); if (auto attributable = dynamic_cast(type)) { - for (auto &attribute : this->getAttributes()) - attributable->addAttribute(static_cast(attribute->clone())); + for (auto &attribute : this->getAttributes()) { + if (auto node = dynamic_cast(attribute->clone())) + attributable->addAttribute(node); + else + delete node; + } } return type; @@ -489,7 +491,7 @@ namespace hex::pl { class ASTNodeCast : public ASTNode { public: ASTNodeCast(ASTNode *value, ASTNode *type) : m_value(value), m_type(type) { } - ASTNodeCast(const ASTNodeCast &other) { + ASTNodeCast(const ASTNodeCast &other) : ASTNode(other) { this->m_value = other.m_value->clone(); this->m_type = other.m_type->clone(); } @@ -623,7 +625,7 @@ namespace hex::pl { auto parameterPack = evaluator->getScope(0).parameterPack; u32 startVariableCount = variables.size(); ON_SCOPE_EXIT { - i64 stackSize = evaluator->getStack().size(); + ssize_t stackSize = evaluator->getStack().size(); for (u32 i = startVariableCount; i < variables.size(); i++) { stackSize--; delete variables[i]; @@ -671,7 +673,7 @@ namespace hex::pl { ON_SCOPE_EXIT { delete literal; }; return std::visit(overloaded { - [](std::string value) -> bool { return !value.empty(); }, + [](const std::string &value) -> bool { return !value.empty(); }, [this](PatternData *const &) -> bool { LogConsole::abortEvaluation("cannot cast custom type to bool", this); }, [](auto &&value) -> bool { return value != 0; } }, literal->getValue()); @@ -784,6 +786,9 @@ namespace hex::pl { this->m_placementOffset = other.m_placementOffset->clone(); else this->m_placementOffset = nullptr; + + this->m_inVariable = other.m_inVariable; + this->m_outVariable = other.m_outVariable; } ~ASTNodeVariableDecl() override { @@ -808,7 +813,7 @@ namespace hex::pl { ON_SCOPE_EXIT { delete offset; }; evaluator->dataOffset() = std::visit(overloaded { - [this](std::string) -> u64 { LogConsole::abortEvaluation("placement offset cannot be a string", this); }, + [this](const std::string &) -> u64 { LogConsole::abortEvaluation("placement offset cannot be a string", this); }, [this](PatternData *const &) -> u64 { LogConsole::abortEvaluation("placement offset cannot be a custom type", this); }, [](auto &&offset) -> u64 { return offset; } }, offset->getValue()); @@ -833,7 +838,7 @@ namespace hex::pl { ASTNode *m_type; ASTNode *m_placementOffset; - bool m_inVariable, m_outVariable; + bool m_inVariable = false, m_outVariable = false; }; class ASTNodeArrayVariableDecl : public ASTNode, @@ -872,7 +877,7 @@ namespace hex::pl { ON_SCOPE_EXIT { delete offset; }; evaluator->dataOffset() = std::visit(overloaded { - [this](std::string) -> u64 { LogConsole::abortEvaluation("placement offset cannot be a string", this); }, + [this](const std::string &) -> u64 { LogConsole::abortEvaluation("placement offset cannot be a string", this); }, [this](PatternData *const &) -> u64 { LogConsole::abortEvaluation("placement offset cannot be a custom type", this); }, [](auto &&offset) -> u64 { return offset; } }, offset->getValue()); @@ -930,7 +935,7 @@ namespace hex::pl { if (auto literal = dynamic_cast(sizeNode)) { entryCount = std::visit(overloaded { - [this](std::string) -> u128 { LogConsole::abortEvaluation("cannot use string to index array", this); }, + [this](const std::string &) -> u128 { LogConsole::abortEvaluation("cannot use string to index array", this); }, [this](PatternData *) -> u128 { LogConsole::abortEvaluation("cannot use custom type to index array", this); }, [](auto &&size) -> u128 { return size; } }, literal->getValue()); @@ -1029,7 +1034,7 @@ namespace hex::pl { if (auto literal = dynamic_cast(sizeNode)) { auto entryCount = std::visit(overloaded { - [this](std::string) -> u128 { LogConsole::abortEvaluation("cannot use string to index array", this); }, + [this](const std::string &) -> u128 { LogConsole::abortEvaluation("cannot use string to index array", this); }, [this](PatternData *) -> u128 { LogConsole::abortEvaluation("cannot use custom type to index array", this); }, [](auto &&size) -> u128 { return size; } }, literal->getValue()); @@ -1176,7 +1181,7 @@ namespace hex::pl { ON_SCOPE_EXIT { delete offset; }; evaluator->dataOffset() = std::visit(overloaded { - [this](std::string) -> u64 { LogConsole::abortEvaluation("placement offset cannot be a string", this); }, + [this](const std::string &) -> u64 { LogConsole::abortEvaluation("placement offset cannot be a string", this); }, [this](PatternData *) -> u64 { LogConsole::abortEvaluation("placement offset cannot be a custom type", this); }, [](auto &&offset) -> u64 { return u64(offset); } }, offset->getValue()); @@ -1499,7 +1504,7 @@ namespace hex::pl { ON_SCOPE_EXIT { delete literal; }; u8 bitSize = std::visit(overloaded { - [this](std::string) -> u8 { LogConsole::abortEvaluation("bitfield field size cannot be a string", this); }, + [this](const std::string &) -> u8 { LogConsole::abortEvaluation("bitfield field size cannot be a string", this); }, [this](PatternData *) -> u8 { LogConsole::abortEvaluation("bitfield field size cannot be a custom type", this); }, [](auto &&offset) -> u8 { return static_cast(offset); } }, dynamic_cast(literal)->getValue()); @@ -1531,13 +1536,13 @@ namespace hex::pl { class ASTNodeParameterPack : public ASTNode { public: - ASTNodeParameterPack(const std::vector &values) : m_values(values) {} + explicit ASTNodeParameterPack(std::vector values) : m_values(std::move(values)) {} [[nodiscard]] ASTNode *clone() const override { return new ASTNodeParameterPack(*this); } - const std::vector &getValues() const { + [[nodiscard]] const std::vector &getValues() const { return this->m_values; } @@ -1622,7 +1627,7 @@ namespace hex::pl { std::visit(overloaded { [&](char assignmentValue) { if (assignmentValue != 0x00) value = std::string({ assignmentValue }); }, - [&](std::string assignmentValue) { value = assignmentValue; }, + [&](const std::string &assignmentValue) { value = assignmentValue; }, [&, this](PatternData *const &assignmentValue) { if (!dynamic_cast(assignmentValue) && !dynamic_cast(assignmentValue)) LogConsole::abortEvaluation(hex::format("cannot assign '{}' to string", pattern->getTypeName()), this); @@ -1646,7 +1651,7 @@ namespace hex::pl { literal = pattern->clone(); } - if (auto transformFunc = pattern->getTransformFunction(); transformFunc.has_value() && pattern->getEvaluator() != nullptr) { + if (auto transformFunc = pattern->getTransformFunction(); transformFunc.has_value()) { auto result = transformFunc->func(evaluator, { literal }); if (!result.has_value()) @@ -1730,8 +1735,8 @@ namespace hex::pl { ON_SCOPE_EXIT { delete index; }; std::visit(overloaded { - [](std::string) { throw std::string("cannot use string to index array"); }, - [](PatternData *const &) { throw std::string("cannot use custom type to index array"); }, + [this](const std::string &) { LogConsole::abortEvaluation("cannot use string to index array", this); }, + [this](PatternData *const &) { LogConsole::abortEvaluation("cannot use custom type to index array", this); }, [&, this](auto &&index) { if (auto dynamicArrayPattern = dynamic_cast(currPattern)) { if (index >= searchScope.size() || index < 0) @@ -1825,7 +1830,7 @@ namespace hex::pl { public: explicit ASTNodeScopeResolution(ASTNode *type, std::string name) : ASTNode(), m_type(type), m_name(std::move(name)) { } - ASTNodeScopeResolution(const ASTNodeScopeResolution &other) { + ASTNodeScopeResolution(const ASTNodeScopeResolution &other) : ASTNode(other) { this->m_type = other.m_type->clone(); this->m_name = other.m_name; } @@ -1904,14 +1909,6 @@ namespace hex::pl { return this->m_condition; } - [[nodiscard]] const std::vector &getTrueBody() const { - return this->m_trueBody; - } - - [[nodiscard]] const std::vector &getFalseBody() const { - return this->m_falseBody; - } - FunctionResult execute(Evaluator *evaluator) const override { auto &body = evaluateCondition(evaluator) ? this->m_trueBody : this->m_falseBody; @@ -1948,7 +1945,7 @@ namespace hex::pl { ON_SCOPE_EXIT { delete literal; }; return std::visit(overloaded { - [](std::string value) -> bool { return !value.empty(); }, + [](const std::string &value) -> bool { return !value.empty(); }, [this](PatternData *const &) -> bool { LogConsole::abortEvaluation("cannot cast custom type to bool", this); }, [](auto &&value) -> bool { return value != 0; } }, literal->getValue()); @@ -2308,9 +2305,9 @@ namespace hex::pl { if (ctx->getCurrentControlFlowStatement() != ControlFlowStatement::None) { switch (ctx->getCurrentControlFlowStatement()) { case ControlFlowStatement::Break: - ctx->getConsole().abortEvaluation("break statement not within a loop", statement); + LogConsole::abortEvaluation("break statement not within a loop", statement); case ControlFlowStatement::Continue: - ctx->getConsole().abortEvaluation("continue statement not within a loop", statement); + LogConsole::abortEvaluation("continue statement not within a loop", statement); default: break; } @@ -2336,13 +2333,15 @@ namespace hex::pl { class ASTNodeCompoundStatement : public ASTNode { public: - ASTNodeCompoundStatement(std::vector statements, bool newScope = false) : m_statements(std::move(statements)), m_newScope(newScope) { + explicit ASTNodeCompoundStatement(std::vector statements, bool newScope = false) : m_statements(std::move(statements)), m_newScope(newScope) { } ASTNodeCompoundStatement(const ASTNodeCompoundStatement &other) : ASTNode(other) { for (const auto &statement : other.m_statements) { this->m_statements.push_back(statement->clone()); } + + this->m_newScope = other.m_newScope; } [[nodiscard]] ASTNode *clone() const override { @@ -2410,7 +2409,7 @@ namespace hex::pl { public: std::vector m_statements; - bool m_newScope; + bool m_newScope = false; }; }; \ No newline at end of file diff --git a/lib/libimhex/include/hex/pattern_language/error.hpp b/lib/libimhex/include/hex/pattern_language/error.hpp new file mode 100644 index 000000000..17aa798ff --- /dev/null +++ b/lib/libimhex/include/hex/pattern_language/error.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include + +#include +#include + +namespace hex::pl { + + class PatternLanguageError : public std::exception { + public: + PatternLanguageError(u32 lineNumber, std::string message) : m_lineNumber(lineNumber), m_message(std::move(message)) { } + + [[nodiscard]] const char *what() const noexcept override { + return this->m_message.c_str(); + } + + [[nodiscard]] u32 getLineNumber() const { + return this->m_lineNumber; + } + + private: + u32 m_lineNumber; + std::string m_message; + }; + +} \ No newline at end of file diff --git a/lib/libimhex/include/hex/pattern_language/lexer.hpp b/lib/libimhex/include/hex/pattern_language/lexer.hpp index 1e480f96a..9a86c7ad9 100644 --- a/lib/libimhex/include/hex/pattern_language/lexer.hpp +++ b/lib/libimhex/include/hex/pattern_language/lexer.hpp @@ -12,18 +12,16 @@ namespace hex::pl { class Lexer { public: - using LexerError = std::pair; - Lexer() = default; std::optional> lex(const std::string &code); - const std::optional &getError() { return this->m_error; } + const std::optional &getError() { return this->m_error; } private: - std::optional m_error; + std::optional m_error; - [[noreturn]] void throwLexerError(const std::string &error, u32 lineNumber) const { - throw LexerError(lineNumber, "Lexer: " + error); + [[noreturn]] static void throwLexerError(const std::string &error, u32 lineNumber) { + throw PatternLanguageError(lineNumber, "Lexer: " + error); } }; diff --git a/lib/libimhex/include/hex/pattern_language/log_console.hpp b/lib/libimhex/include/hex/pattern_language/log_console.hpp index 503a7972f..e956ae041 100644 --- a/lib/libimhex/include/hex/pattern_language/log_console.hpp +++ b/lib/libimhex/include/hex/pattern_language/log_console.hpp @@ -8,6 +8,8 @@ #include #include +#include + namespace hex::pl { class ASTNode; @@ -23,8 +25,6 @@ namespace hex::pl { [[nodiscard]] const auto &getLog() const { return this->m_consoleLog; } - using EvaluateError = std::pair; - void log(Level level, const std::string &message); [[noreturn]] static void abortEvaluation(const std::string &message); @@ -33,13 +33,13 @@ namespace hex::pl { void clear(); - void setHardError(const EvaluateError &error) { this->m_lastHardError = error; } + void setHardError(const PatternLanguageError &error) { this->m_lastHardError = error; } - [[nodiscard]] const std::optional &getLastHardError() { return this->m_lastHardError; }; + [[nodiscard]] const std::optional &getLastHardError() { return this->m_lastHardError; }; private: std::vector> m_consoleLog; - std::optional m_lastHardError; + std::optional m_lastHardError; }; } \ No newline at end of file diff --git a/lib/libimhex/include/hex/pattern_language/parser.hpp b/lib/libimhex/include/hex/pattern_language/parser.hpp index 14cccc42b..c10a1e140 100644 --- a/lib/libimhex/include/hex/pattern_language/parser.hpp +++ b/lib/libimhex/include/hex/pattern_language/parser.hpp @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -16,16 +17,15 @@ namespace hex::pl { class Parser { public: using TokenIter = std::vector::const_iterator; - using ParseError = std::pair; Parser() = default; ~Parser() = default; std::optional> parse(const std::vector &tokens); - const std::optional &getError() { return this->m_error; } + const std::optional &getError() { return this->m_error; } private: - std::optional m_error; + std::optional m_error; TokenIter m_curr; TokenIter m_originalPosition, m_partOriginalPosition; @@ -47,7 +47,7 @@ namespace hex::pl { auto value = std::get_if(&this->m_curr[index].value); if (value == nullptr) - throwParseError("failed to decode token. Invalid type.", getLineNumber(index)); + throwParserError("failed to decode token. Invalid type.", getLineNumber(index)); return *value; } @@ -143,8 +143,8 @@ namespace hex::pl { return program; } - [[noreturn]] void throwParseError(const std::string &error, i32 token = -1) const { - throw ParseError(this->m_curr[token].lineNumber, "Parser: " + error); + [[noreturn]] void throwParserError(const std::string &error, i32 token = -1) const { + throw PatternLanguageError(this->m_curr[token].lineNumber, "Parser: " + error); } /* Token consuming */ diff --git a/lib/libimhex/include/hex/pattern_language/pattern_data.hpp b/lib/libimhex/include/hex/pattern_language/pattern_data.hpp index 2157e4ee9..ea9dad343 100644 --- a/lib/libimhex/include/hex/pattern_language/pattern_data.hpp +++ b/lib/libimhex/include/hex/pattern_language/pattern_data.hpp @@ -73,11 +73,16 @@ namespace hex::pl { PatternCreationLimiter::s_evaluator->patternDestroyed(); } + [[nodiscard]] + static Evaluator *getEvaluator() { + return PatternCreationLimiter::s_evaluator; + } + public: static Evaluator *s_evaluator; }; - class PatternData : public PatternCreationLimiter { + class PatternData : public PatternCreationLimiter, public Cloneable { public: PatternData(u64 offset, size_t size, u32 color = 0) : PatternCreationLimiter(), m_offset(offset), m_size(size), m_color(color) { @@ -95,9 +100,7 @@ namespace hex::pl { PatternData(const PatternData &other) = default; - virtual ~PatternData() = default; - - virtual PatternData *clone() = 0; + ~PatternData() override = default; [[nodiscard]] u64 getOffset() const { return this->m_offset; } virtual void setOffset(u64 offset) { this->m_offset = offset; } @@ -122,8 +125,8 @@ namespace hex::pl { [[nodiscard]] bool hasOverriddenColor() const { return this->m_manualColor; } [[nodiscard]] std::endian getEndian() const { - if (this->getEvaluator() == nullptr) return std::endian::native; - else return this->m_endian.value_or(this->getEvaluator()->getDefaultEndian()); + if (PatternData::getEvaluator() == nullptr) return std::endian::native; + else return this->m_endian.value_or(PatternData::getEvaluator()->getDefaultEndian()); } virtual void setEndian(std::endian endian) { this->m_endian = endian; } [[nodiscard]] bool hasOverriddenEndian() const { return this->m_endian.has_value(); } @@ -131,8 +134,6 @@ namespace hex::pl { [[nodiscard]] std::string getDisplayName() const { return this->m_displayName.value_or(this->m_variableName); } void setDisplayName(const std::string &name) { this->m_displayName = name; } - [[nodiscard]] Evaluator *getEvaluator() const { return PatternCreationLimiter::s_evaluator; } - [[nodiscard]] const auto &getTransformFunction() const { return this->m_transformFunction; } void setTransformFunction(const ContentRegistry::PatternLanguage::Function &function) { this->m_transformFunction = function; } [[nodiscard]] const auto &getFormatterFunction() const { return this->m_formatterFunction; } @@ -154,7 +155,7 @@ namespace hex::pl { for (u64 i = 0; i < this->getSize(); i++) highlight.insert({ this->getOffset() + i, this->getColor() }); - this->getEvaluator()->handleAbort(); + PatternData::getEvaluator()->handleAbort(); } virtual void sort(ImGuiTableSortSpecs *sortSpecs, prv::Provider *provider) { } @@ -255,7 +256,7 @@ namespace hex::pl { return value; else { try { - auto result = this->m_formatterFunction->func(this->getEvaluator(), { literal }); + auto result = this->m_formatterFunction->func(PatternData::getEvaluator(), { literal }); if (result.has_value()) { if (auto displayValue = std::get_if(&result.value()); displayValue != nullptr) @@ -265,8 +266,8 @@ namespace hex::pl { } else { return "???"; } - } catch (LogConsole::EvaluateError &error) { - return "Error: " + error.second; + } catch (PatternLanguageError &error) { + return "Error: "s + error.what(); } } } @@ -307,10 +308,10 @@ namespace hex::pl { bool m_hidden = false; private: - u64 m_offset; - size_t m_size; + u64 m_offset = 0x00; + size_t m_size = 0x00; - u32 m_color; + u32 m_color = 0x00; std::optional m_displayName; std::string m_variableName; std::optional m_comment; @@ -327,7 +328,8 @@ namespace hex::pl { public: PatternDataPadding(u64 offset, size_t size) : PatternData(offset, size, 0xFF000000) { } - PatternData *clone() override { + [[nodiscard]] + PatternData *clone() const override { return new PatternDataPadding(*this); } @@ -355,7 +357,8 @@ namespace hex::pl { delete this->m_pointedAt; } - PatternData *clone() override { + [[nodiscard]] + PatternData *clone() const override { return new PatternDataPointer(*this); } @@ -476,7 +479,8 @@ namespace hex::pl { PatternDataUnsigned(u64 offset, size_t size, u32 color = 0) : PatternData(offset, size, color) { } - PatternData *clone() override { + [[nodiscard]] + PatternData *clone() const override { return new PatternDataUnsigned(*this); } @@ -513,7 +517,8 @@ namespace hex::pl { PatternDataSigned(u64 offset, size_t size, u32 color = 0) : PatternData(offset, size, color) { } - PatternData *clone() override { + [[nodiscard]] + PatternData *clone() const override { return new PatternDataSigned(*this); } @@ -551,7 +556,8 @@ namespace hex::pl { PatternDataFloat(u64 offset, size_t size, u32 color = 0) : PatternData(offset, size, color) { } - PatternData *clone() override { + [[nodiscard]] + PatternData *clone() const override { return new PatternDataFloat(*this); } @@ -590,7 +596,8 @@ namespace hex::pl { explicit PatternDataBoolean(u64 offset, u32 color = 0) : PatternData(offset, 1, color) { } - PatternData *clone() override { + [[nodiscard]] + PatternData *clone() const override { return new PatternDataBoolean(*this); } @@ -618,7 +625,8 @@ namespace hex::pl { explicit PatternDataCharacter(u64 offset, u32 color = 0) : PatternData(offset, 1, color) { } - PatternData *clone() override { + [[nodiscard]] + PatternData *clone() const override { return new PatternDataCharacter(*this); } @@ -641,7 +649,8 @@ namespace hex::pl { explicit PatternDataCharacter16(u64 offset, u32 color = 0) : PatternData(offset, 2, color) { } - PatternData *clone() override { + [[nodiscard]] + PatternData *clone() const override { return new PatternDataCharacter16(*this); } @@ -674,7 +683,8 @@ namespace hex::pl { PatternDataString(u64 offset, size_t size, u32 color = 0) : PatternData(offset, size, color) { } - PatternData *clone() override { + [[nodiscard]] + PatternData *clone() const override { return new PatternDataString(*this); } @@ -714,7 +724,8 @@ namespace hex::pl { PatternDataString16(u64 offset, size_t size, u32 color = 0) : PatternData(offset, size, color) { } - PatternData *clone() override { + [[nodiscard]] + PatternData *clone() const override { return new PatternDataString16(*this); } @@ -780,7 +791,8 @@ namespace hex::pl { delete entry; } - PatternData *clone() override { + [[nodiscard]] + PatternData *clone() const override { return new PatternDataDynamicArray(*this); } @@ -893,7 +905,7 @@ namespace hex::pl { [[nodiscard]] const PatternData *getPattern(u64 offset) const override { if (this->isHidden()) return nullptr; - auto iter = std::find_if(this->m_entries.begin(), this->m_entries.end(), [this, offset](PatternData *pattern) { + auto iter = std::find_if(this->m_entries.begin(), this->m_entries.end(), [offset](PatternData *pattern) { return offset >= pattern->getOffset() && offset < (pattern->getOffset() + pattern->getSize()); }); @@ -932,7 +944,8 @@ namespace hex::pl { delete this->m_highlightTemplate; } - PatternData *clone() override { + [[nodiscard]] + PatternData *clone() const override { return new PatternDataStaticArray(*this); } @@ -1067,9 +1080,9 @@ namespace hex::pl { } private: - PatternData *m_template; - mutable PatternData *m_highlightTemplate; - size_t m_entryCount; + PatternData *m_template = nullptr; + mutable PatternData *m_highlightTemplate = nullptr; + size_t m_entryCount = 0; u64 m_displayEnd = 50; }; @@ -1091,7 +1104,8 @@ namespace hex::pl { delete member; } - PatternData *clone() override { + [[nodiscard]] + PatternData *clone() const override { return new PatternDataStruct(*this); } @@ -1238,7 +1252,8 @@ namespace hex::pl { delete member; } - PatternData *clone() override { + [[nodiscard]] + PatternData *clone() const override { return new PatternDataUnion(*this); } @@ -1375,7 +1390,8 @@ namespace hex::pl { : PatternData(offset, size, color) { } - PatternData *clone() override { + [[nodiscard]] + PatternData *clone() const override { return new PatternDataEnum(*this); } @@ -1398,7 +1414,7 @@ namespace hex::pl { return false; }, - [](std::string) { return false; }, + [](std::string&) { return false; }, [](PatternData *) { return false; } }, entryValueLiteral); if (matches) @@ -1470,7 +1486,8 @@ namespace hex::pl { : m_bitOffset(bitOffset), m_bitSize(bitSize), m_bitField(bitField), PatternData(offset, 0, color) { } - PatternData *clone() override { + [[nodiscard]] + PatternData *clone() const override { return new PatternDataBitfieldField(*this); } @@ -1549,7 +1566,8 @@ namespace hex::pl { delete field; } - PatternData *clone() override { + [[nodiscard]] + PatternData *clone() const override { return new PatternDataBitfield(*this); } diff --git a/lib/libimhex/include/hex/pattern_language/pattern_language.hpp b/lib/libimhex/include/hex/pattern_language/pattern_language.hpp index d2152bec0..59fb55854 100644 --- a/lib/libimhex/include/hex/pattern_language/pattern_language.hpp +++ b/lib/libimhex/include/hex/pattern_language/pattern_language.hpp @@ -40,7 +40,7 @@ namespace hex::pl { void abort(); [[nodiscard]] const std::vector> &getConsoleLog(); - [[nodiscard]] const std::optional> &getError(); + [[nodiscard]] const std::optional &getError(); [[nodiscard]] std::map getOutVariables() const; [[nodiscard]] u32 getCreatedPatternCount(); @@ -58,7 +58,7 @@ namespace hex::pl { std::vector m_currAST; - std::optional> m_currError; + std::optional m_currError; }; } \ No newline at end of file diff --git a/lib/libimhex/include/hex/pattern_language/preprocessor.hpp b/lib/libimhex/include/hex/pattern_language/preprocessor.hpp index ee8153bc9..1c732ef84 100644 --- a/lib/libimhex/include/hex/pattern_language/preprocessor.hpp +++ b/lib/libimhex/include/hex/pattern_language/preprocessor.hpp @@ -11,24 +11,24 @@ #include +#include + namespace hex::pl { class Preprocessor { public: - Preprocessor(); + Preprocessor() = default; std::optional preprocess(const std::string &code, bool initialRun = true); void addPragmaHandler(const std::string &pragmaType, const std::function &function); void addDefaultPragmaHandlers(); - const std::pair &getError() { return this->m_error; } + const std::optional &getError() { return this->m_error; } private: - using PreprocessorError = std::pair; - - [[noreturn]] void throwPreprocessorError(const std::string &error, u32 lineNumber) const { - throw PreprocessorError(lineNumber, "Preprocessor: " + error); + [[noreturn]] static void throwPreprocessorError(const std::string &error, u32 lineNumber) { + throw PatternLanguageError(lineNumber, "Preprocessor: " + error); } std::unordered_map> m_pragmaHandlers; @@ -38,7 +38,7 @@ namespace hex::pl { std::set m_onceIncludedFiles; - std::pair m_error; + std::optional m_error; }; } \ No newline at end of file diff --git a/lib/libimhex/include/hex/pattern_language/token.hpp b/lib/libimhex/include/hex/pattern_language/token.hpp index 1422215fc..cab4f7870 100644 --- a/lib/libimhex/include/hex/pattern_language/token.hpp +++ b/lib/libimhex/include/hex/pattern_language/token.hpp @@ -7,6 +7,7 @@ #include #include +#include namespace hex::pl { @@ -156,48 +157,48 @@ namespace hex::pl { static u128 literalToUnsigned(const pl::Token::Literal &literal) { return std::visit(overloaded { - [](std::string) -> u128 { throw std::string("expected integral type, got string"); }, - [](PatternData *) -> u128 { throw std::string("expected integral type, got custom type"); }, - [](auto &&value) -> u128 { return value; } }, + [](const std::string &) -> u128 { LogConsole::abortEvaluation("expected integral type, got string"); }, + [](PatternData *) -> u128 { LogConsole::abortEvaluation("expected integral type, got custom type"); }, + [](auto &&result) -> u128 { return result; } }, literal); } static i128 literalToSigned(const pl::Token::Literal &literal) { return std::visit(overloaded { - [](std::string) -> i128 { throw std::string("expected integral type, got string"); }, - [](PatternData *) -> i128 { throw std::string("expected integral type, got custom type"); }, - [](auto &&value) -> i128 { return value; } }, + [](const std::string &) -> i128 { LogConsole::abortEvaluation("expected integral type, got string"); }, + [](PatternData *) -> i128 { LogConsole::abortEvaluation("expected integral type, got custom type"); }, + [](auto &&result) -> i128 { return result; } }, literal); } static double literalToFloatingPoint(const pl::Token::Literal &literal) { return std::visit(overloaded { - [](std::string) -> double { throw std::string("expected integral type, got string"); }, - [](PatternData *) -> double { throw std::string("expected integral type, got custom type"); }, - [](auto &&value) -> double { return value; } }, + [](const std::string &) -> double { LogConsole::abortEvaluation("expected integral type, got string"); }, + [](PatternData *) -> double { LogConsole::abortEvaluation("expected integral type, got custom type"); }, + [](auto &&result) -> double { return result; } }, literal); } static bool literalToBoolean(const pl::Token::Literal &literal) { return std::visit(overloaded { - [](std::string) -> bool { throw std::string("expected integral type, got string"); }, - [](PatternData *) -> bool { throw std::string("expected integral type, got custom type"); }, - [](auto &&value) -> bool { return value != 0; } }, + [](const std::string &) -> bool { LogConsole::abortEvaluation("expected integral type, got string"); }, + [](PatternData *) -> bool { LogConsole::abortEvaluation("expected integral type, got custom type"); }, + [](auto &&result) -> bool { return result != 0; } }, literal); } static std::string literalToString(const pl::Token::Literal &literal, bool cast) { if (!cast && std::get_if(&literal) == nullptr) - throw std::string("expected string type, got integral"); + LogConsole::abortEvaluation("expected string type, got integral"); return std::visit(overloaded { - [](std::string value) -> std::string { return value; }, - [](u128 value) -> std::string { return std::to_string(u64(value)); }, - [](i128 value) -> std::string { return std::to_string(i64(value)); }, - [](bool value) -> std::string { return value ? "true" : "false"; }, - [](char value) -> std::string { return std::string() + value; }, - [](PatternData *) -> std::string { throw std::string("expected integral type, got custom type"); }, - [](auto &&value) -> std::string { return std::to_string(value); } }, + [](std::string result) -> std::string { return result; }, + [](u128 result) -> std::string { return hex::to_string(result); }, + [](i128 result) -> std::string { return hex::to_string(result); }, + [](bool result) -> std::string { return result ? "true" : "false"; }, + [](char result) -> std::string { return { 1, result }; }, + [](PatternData *) -> std::string { LogConsole::abortEvaluation("expected integral type, got custom type"); }, + [](auto &&result) -> std::string { return std::to_string(result); } }, literal); } diff --git a/lib/libimhex/include/hex/pattern_language/validator.hpp b/lib/libimhex/include/hex/pattern_language/validator.hpp index 2c0863f5a..52d7cd0cb 100644 --- a/lib/libimhex/include/hex/pattern_language/validator.hpp +++ b/lib/libimhex/include/hex/pattern_language/validator.hpp @@ -2,28 +2,29 @@ #include +#include #include #include +#include + namespace hex::pl { class ASTNode; class Validator { public: - Validator(); + Validator() = default; bool validate(const std::vector &ast); - const std::pair &getError() { return this->m_error; } + const std::optional &getError() { return this->m_error; } private: - std::pair m_error; + std::optional m_error; - using ValidatorError = std::pair; - - [[noreturn]] void throwValidateError(std::string_view error, u32 lineNumber) const { - throw ValidatorError(lineNumber, error); + [[noreturn]] static void throwValidatorError(const std::string &error, u32 lineNumber) { + throw PatternLanguageError(lineNumber, "Validator: " + error); } }; diff --git a/lib/libimhex/source/pattern_language/evaluator.cpp b/lib/libimhex/source/pattern_language/evaluator.cpp index cc9f97961..42320fb31 100644 --- a/lib/libimhex/source/pattern_language/evaluator.cpp +++ b/lib/libimhex/source/pattern_language/evaluator.cpp @@ -206,10 +206,10 @@ namespace hex::pl { } popScope(); - } catch (const LogConsole::EvaluateError &error) { - this->m_console.log(LogConsole::Level::Error, error.second); + } catch (PatternLanguageError &error) { + this->m_console.log(LogConsole::Level::Error, error.what()); - if (error.first != 0) + if (error.getLineNumber() != 0) this->m_console.setHardError(error); for (auto &pattern : patterns) diff --git a/lib/libimhex/source/pattern_language/lexer.cpp b/lib/libimhex/source/pattern_language/lexer.cpp index 39ca698de..d4d46a040 100644 --- a/lib/libimhex/source/pattern_language/lexer.cpp +++ b/lib/libimhex/source/pattern_language/lexer.cpp @@ -10,7 +10,7 @@ namespace hex::pl { #define TOKEN(type, value) Token::Type::type, Token::type::value, lineNumber #define VALUE_TOKEN(type, value) Token::Type::type, value, lineNumber - std::string matchTillInvalid(const char *characters, std::function predicate) { + std::string matchTillInvalid(const char *characters, const std::function &predicate) { std::string ret; while (*characters != 0x00) { @@ -403,14 +403,14 @@ namespace hex::pl { tokens.emplace_back(TOKEN(Operator, SizeOf)); offset += 6; } else if (c == '\'') { - auto character = getCharacterLiteral(code.substr(offset)); + auto lexedCharacter = getCharacterLiteral(code.substr(offset)); - if (!character.has_value()) + if (!lexedCharacter.has_value()) throwLexerError("invalid character literal", lineNumber); - auto [c, charSize] = character.value(); + auto [character, charSize] = lexedCharacter.value(); - tokens.emplace_back(VALUE_TOKEN(Integer, Token::Literal(c))); + tokens.emplace_back(VALUE_TOKEN(Integer, Token::Literal(character))); offset += charSize; } else if (c == '\"') { auto string = getStringLiteral(code.substr(offset)); @@ -530,9 +530,10 @@ namespace hex::pl { } tokens.emplace_back(TOKEN(Separator, EndOfProgram)); - } catch (LexerError &e) { + } catch (PatternLanguageError &e) { this->m_error = e; - return {}; + + return std::nullopt; } diff --git a/lib/libimhex/source/pattern_language/log_console.cpp b/lib/libimhex/source/pattern_language/log_console.cpp index e5a2be189..1a28bbfa4 100644 --- a/lib/libimhex/source/pattern_language/log_console.cpp +++ b/lib/libimhex/source/pattern_language/log_console.cpp @@ -9,14 +9,14 @@ namespace hex::pl { } [[noreturn]] void LogConsole::abortEvaluation(const std::string &message) { - throw EvaluateError(0, message); + throw PatternLanguageError(0, message); } [[noreturn]] void LogConsole::abortEvaluation(const std::string &message, const ASTNode *node) { if (node == nullptr) abortEvaluation(message); else - throw EvaluateError(node->getLineNumber(), message); + throw PatternLanguageError(node->getLineNumber(), message); } void LogConsole::clear() { diff --git a/lib/libimhex/source/pattern_language/parser.cpp b/lib/libimhex/source/pattern_language/parser.cpp index a4d237646..56dd019e9 100644 --- a/lib/libimhex/source/pattern_language/parser.cpp +++ b/lib/libimhex/source/pattern_language/parser.cpp @@ -20,7 +20,7 @@ namespace hex::pl { std::string functionName = parseNamespaceResolution(); if (!MATCHES(sequence(SEPARATOR_ROUNDBRACKETOPEN))) - throwParseError("expected '(' after function name"); + throwParserError("expected '(' after function name"); std::vector params; auto paramCleanup = SCOPE_GUARD { @@ -32,11 +32,11 @@ namespace hex::pl { params.push_back(parseMathematicalExpression()); if (MATCHES(sequence(SEPARATOR_COMMA, SEPARATOR_ROUNDBRACKETCLOSE))) - throwParseError("unexpected ',' at end of function parameter list", -1); + throwParserError("unexpected ',' at end of function parameter list", -1); else if (MATCHES(sequence(SEPARATOR_ROUNDBRACKETCLOSE))) break; else if (!MATCHES(sequence(SEPARATOR_COMMA))) - throwParseError("missing ',' between parameters", -1); + throwParserError("missing ',' between parameters", -1); } paramCleanup.release(); @@ -76,7 +76,7 @@ namespace hex::pl { continue; } else { if (!this->m_types.contains(typeName)) - throwParseError(hex::format("cannot access scope of invalid type '{}'", typeName), -1); + throwParserError(hex::format("cannot access scope of invalid type '{}'", typeName), -1); return create(new ASTNodeScopeResolution(this->m_types[typeName]->clone(), getValue(-1).get())); } @@ -84,7 +84,7 @@ namespace hex::pl { break; } - throwParseError("failed to parse scope resolution. Expected 'TypeName::Identifier'"); + throwParserError("failed to parse scope resolution. Expected 'TypeName::Identifier'"); } ASTNode *Parser::parseRValue() { @@ -104,14 +104,14 @@ namespace hex::pl { if (MATCHES(sequence(SEPARATOR_SQUAREBRACKETOPEN))) { path.push_back(parseMathematicalExpression()); if (!MATCHES(sequence(SEPARATOR_SQUAREBRACKETCLOSE))) - throwParseError("expected closing ']' at end of array indexing"); + throwParserError("expected closing ']' at end of array indexing"); } if (MATCHES(sequence(SEPARATOR_DOT))) { if (MATCHES(oneOf(IDENTIFIER, KEYWORD_PARENT))) return this->parseRValue(path); else - throwParseError("expected member name or 'parent' keyword", -1); + throwParserError("expected member name or 'parent' keyword", -1); } else return create(new ASTNodeRValue(path)); } @@ -126,7 +126,7 @@ namespace hex::pl { auto node = this->parseMathematicalExpression(); if (!MATCHES(sequence(SEPARATOR_ROUNDBRACKETCLOSE))) { delete node; - throwParseError("expected closing parenthesis"); + throwParserError("expected closing parenthesis"); } return node; } else if (MATCHES(sequence(IDENTIFIER))) { @@ -159,17 +159,17 @@ namespace hex::pl { result = new ASTNodeLiteral(u128(Token::getTypeSize(type))); } else { - throwParseError("expected rvalue identifier or built-in type"); + throwParserError("expected rvalue identifier or built-in type"); } if (!MATCHES(sequence(SEPARATOR_ROUNDBRACKETCLOSE))) { delete result; - throwParseError("expected closing parenthesis"); + throwParserError("expected closing parenthesis"); } return result; } else - throwParseError("expected value or parenthesis"); + throwParserError("expected value or parenthesis"); } ASTNode *Parser::parseCastExpression() { @@ -178,10 +178,10 @@ namespace hex::pl { auto builtinType = dynamic_cast(type->getType()); if (builtinType == nullptr) - throwParseError("invalid type used for pointer size", -1); + throwParserError("invalid type used for pointer size", -1); if (!peek(SEPARATOR_ROUNDBRACKETOPEN)) - throwParseError("expected '(' before cast expression", -1); + throwParserError("expected '(' before cast expression", -1); auto node = parseFactor(); @@ -382,7 +382,7 @@ namespace hex::pl { auto second = this->parseBooleanOr(); if (!MATCHES(sequence(OPERATOR_INHERIT))) - throwParseError("expected ':' in ternary expression"); + throwParserError("expected ':' in ternary expression"); auto third = this->parseBooleanOr(); node = create(new ASTNodeTernaryExpression(node, second, third, Token::Operator::TernaryConditional)); @@ -401,11 +401,11 @@ namespace hex::pl { // [[ ]] void Parser::parseAttribute(Attributable *currNode) { if (currNode == nullptr) - throwParseError("tried to apply attribute to invalid statement"); + throwParserError("tried to apply attribute to invalid statement"); do { if (!MATCHES(sequence(IDENTIFIER))) - throwParseError("expected attribute expression"); + throwParserError("expected attribute expression"); auto attribute = getValue(-1).get(); @@ -414,7 +414,7 @@ namespace hex::pl { auto string = std::get_if(&value); if (string == nullptr) - throwParseError("expected string attribute argument"); + throwParserError("expected string attribute argument"); currNode->addAttribute(create(new ASTNodeAttribute(attribute, *string))); } else @@ -423,7 +423,7 @@ namespace hex::pl { } while (MATCHES(sequence(SEPARATOR_COMMA))); if (!MATCHES(sequence(SEPARATOR_SQUAREBRACKETCLOSE, SEPARATOR_SQUAREBRACKETCLOSE))) - throwParseError("unfinished attribute. Expected ']]'"); + throwParserError("unfinished attribute. Expected ']]'"); } /* Functions */ @@ -441,7 +441,7 @@ namespace hex::pl { parameterPack = getValue(-1).get(); if (MATCHES(sequence(SEPARATOR_COMMA))) - throwParseError("parameter pack can only appear at end of parameter list"); + throwParserError("parameter pack can only appear at end of parameter list"); break; } else { @@ -461,10 +461,10 @@ namespace hex::pl { } if (!MATCHES(sequence(SEPARATOR_ROUNDBRACKETCLOSE))) - throwParseError("expected closing ')' after parameter list"); + throwParserError("expected closing ')' after parameter list"); if (!MATCHES(sequence(SEPARATOR_CURLYBRACKETOPEN))) - throwParseError("expected opening '{' after function definition"); + throwParserError("expected opening '{' after function definition"); // Parse function body @@ -496,7 +496,7 @@ namespace hex::pl { statement = create(new ASTNodeCompoundStatement({ statement, create(new ASTNodeAssignment(identifier, expression)) })); } } else - throwParseError("invalid variable declaration"); + throwParserError("invalid variable declaration"); return statement; } @@ -539,11 +539,11 @@ namespace hex::pl { } else if (peek(KEYWORD_BE) || peek(KEYWORD_LE) || peek(VALUETYPE_ANY)) { statement = parseFunctionVariableDecl(); } else - throwParseError("invalid sequence", 0); + throwParserError("invalid sequence", 0); if (needsSemicolon && !MATCHES(sequence(SEPARATOR_ENDOFEXPRESSION))) { delete statement; - throwParseError("missing ';' at end of expression", -1); + throwParserError("missing ';' at end of expression", -1); } // Consume superfluous semicolons @@ -576,7 +576,7 @@ namespace hex::pl { else if (peek(KEYWORD_CONTINUE, -1)) type = ControlFlowStatement::Continue; else - throwParseError("invalid control flow statement. Expected 'return', 'break' or 'continue'"); + throwParserError("invalid control flow statement. Expected 'return', 'break' or 'continue'"); if (peek(SEPARATOR_ENDOFEXPRESSION)) return create(new ASTNodeControlFlowStatement(type, nullptr)); @@ -618,7 +618,7 @@ namespace hex::pl { }; if (!MATCHES(sequence(SEPARATOR_ROUNDBRACKETCLOSE))) - throwParseError("expected closing ')' after statement head"); + throwParserError("expected closing ')' after statement head"); trueBody = parseStatementBody(); @@ -641,7 +641,7 @@ namespace hex::pl { }; if (!MATCHES(sequence(SEPARATOR_ROUNDBRACKETCLOSE))) - throwParseError("expected closing ')' after statement head"); + throwParserError("expected closing ')' after statement head"); body = parseStatementBody(); @@ -655,16 +655,16 @@ namespace hex::pl { auto variableCleanup = SCOPE_GUARD { delete variable; }; if (!MATCHES(sequence(SEPARATOR_COMMA))) - throwParseError("expected ',' after for loop variable declaration"); + throwParserError("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"); + throwParserError("expected ',' after for loop condition"); if (!MATCHES(sequence(IDENTIFIER, OPERATOR_ASSIGNMENT))) - throwParseError("expected for loop variable assignment"); + throwParserError("expected for loop variable assignment"); auto postExpression = parseFunctionVariableAssignment(getValue(-2).get()); auto postExpressionCleanup = SCOPE_GUARD { delete postExpression; }; @@ -677,7 +677,7 @@ namespace hex::pl { }; if (!MATCHES(sequence(SEPARATOR_ROUNDBRACKETCLOSE))) - throwParseError("expected closing ')' after statement head"); + throwParserError("expected closing ')' after statement head"); body = parseStatementBody(); @@ -711,7 +711,7 @@ namespace hex::pl { } else if (MATCHES(sequence(SEPARATOR_ROUNDBRACKETCLOSE))) { trueBody.push_back(parseMember()); } else - throwParseError("expected body of conditional statement"); + throwParserError("expected body of conditional statement"); if (MATCHES(sequence(KEYWORD_ELSE, SEPARATOR_CURLYBRACKETOPEN))) { while (!MATCHES(sequence(SEPARATOR_CURLYBRACKETCLOSE))) { @@ -735,7 +735,7 @@ namespace hex::pl { }; if (!MATCHES(sequence(SEPARATOR_ROUNDBRACKETCLOSE))) - throwParseError("expected closing ')' after while head"); + throwParserError("expected closing ')' after while head"); cleanup.release(); @@ -761,18 +761,18 @@ namespace hex::pl { else if (this->m_types.contains(getNamespacePrefixedName(typeName))) return create(new ASTNodeTypeDecl({}, this->m_types[getNamespacePrefixedName(typeName)]->clone(), endian)); else - throwParseError(hex::format("unknown type '{}'", typeName)); + throwParserError(hex::format("unknown type '{}'", typeName)); } else if (MATCHES(sequence(VALUETYPE_ANY))) { // Builtin type auto type = getValue(-1); if (!allowFunctionTypes) { if (type == Token::ValueType::String) - throwParseError("cannot use 'str' in this context. Use a character array instead"); + throwParserError("cannot use 'str' in this context. Use a character array instead"); else if (type == Token::ValueType::Auto) - throwParseError("cannot use 'auto' in this context"); + throwParserError("cannot use 'auto' in this context"); } return create(new ASTNodeTypeDecl({}, new ASTNodeBuiltinType(type), endian)); - } else throwParseError("failed to parse type. Expected identifier or builtin type"); + } else throwParserError("failed to parse type. Expected identifier or builtin type"); } // using Identifier = (parseType) @@ -780,10 +780,10 @@ namespace hex::pl { auto name = parseNamespaceResolution(); if (!MATCHES(sequence(OPERATOR_ASSIGNMENT))) - throwParseError("expected '=' after type name of using declaration"); + throwParserError("expected '=' after type name of using declaration"); auto *type = dynamic_cast(parseType()); - if (type == nullptr) throwParseError("invalid type used in variable declaration", -1); + if (type == nullptr) throwParserError("invalid type used in variable declaration", -1); return addType(name, type, type->getEndian()); } @@ -794,7 +794,7 @@ namespace hex::pl { if (!MATCHES(sequence(SEPARATOR_SQUAREBRACKETCLOSE))) { delete size; - throwParseError("expected closing ']' at end of array declaration", -1); + throwParserError("expected closing ']' at end of array declaration", -1); } return create(new ASTNodeArrayVariableDecl({}, new ASTNodeTypeDecl({}, new ASTNodeBuiltinType(Token::ValueType::Padding)), size)); @@ -835,7 +835,7 @@ namespace hex::pl { size = parseMathematicalExpression(); if (!MATCHES(sequence(SEPARATOR_SQUAREBRACKETCLOSE))) - throwParseError("expected closing ']' at end of array declaration", -1); + throwParserError("expected closing ']' at end of array declaration", -1); } sizeCleanup.release(); @@ -853,7 +853,7 @@ namespace hex::pl { auto builtinType = dynamic_cast(sizeType->getType()); if (builtinType == nullptr || !Token::isUnsigned(builtinType->getType())) - throwParseError("invalid type used for pointer size", -1); + throwParserError("invalid type used for pointer size", -1); } return create(new ASTNodePointerVariableDecl(name, type, sizeType)); @@ -893,14 +893,14 @@ namespace hex::pl { else if (MATCHES(sequence(OPERATOR_STAR, IDENTIFIER, OPERATOR_INHERIT))) member = parseMemberPointerVariable(type); else - throwParseError("invalid variable declaration"); + throwParserError("invalid variable declaration"); } } else if (MATCHES(sequence(VALUETYPE_PADDING, SEPARATOR_SQUAREBRACKETOPEN))) member = parsePadding(); else if (MATCHES(sequence(KEYWORD_IF, SEPARATOR_ROUNDBRACKETOPEN))) return parseConditional(); else if (MATCHES(sequence(SEPARATOR_ENDOFPROGRAM))) - throwParseError("unexpected end of program", -2); + throwParserError("unexpected end of program", -2); else if (MATCHES(sequence(KEYWORD_BREAK))) member = new ASTNodeControlFlowStatement(ControlFlowStatement::Break, nullptr); else if (MATCHES(sequence(KEYWORD_CONTINUE))) @@ -910,13 +910,13 @@ namespace hex::pl { else if (MATCHES(oneOf(OPERATOR_DOLLAR) && oneOf(OPERATOR_PLUS, OPERATOR_MINUS, OPERATOR_STAR, OPERATOR_SLASH, OPERATOR_PERCENT, OPERATOR_SHIFTLEFT, OPERATOR_SHIFTRIGHT, OPERATOR_BITOR, OPERATOR_BITAND, OPERATOR_BITXOR) && sequence(OPERATOR_ASSIGNMENT))) member = parseFunctionVariableCompoundAssignment("$"); else - throwParseError("invalid struct member", 0); + throwParserError("invalid struct member", 0); if (MATCHES(sequence(SEPARATOR_SQUAREBRACKETOPEN, SEPARATOR_SQUAREBRACKETOPEN))) parseAttribute(dynamic_cast(member)); if (!MATCHES(sequence(SEPARATOR_ENDOFEXPRESSION))) - throwParseError("missing ';' at end of expression", -1); + throwParserError("missing ';' at end of expression", -1); // Consume superfluous semicolons while (MATCHES(sequence(SEPARATOR_ENDOFEXPRESSION))) @@ -942,17 +942,17 @@ namespace hex::pl { do { auto inheritedTypeName = getValue(-1).get(); if (!this->m_types.contains(inheritedTypeName)) - throwParseError(hex::format("cannot inherit from unknown type '{}'", inheritedTypeName), -1); + throwParserError(hex::format("cannot inherit from unknown type '{}'", inheritedTypeName), -1); structNode->addInheritance(this->m_types[inheritedTypeName]->clone()); } while (MATCHES(sequence(SEPARATOR_COMMA, IDENTIFIER))); } else if (MATCHES(sequence(OPERATOR_INHERIT, VALUETYPE_ANY))) { - throwParseError("cannot inherit from builtin type"); + throwParserError("cannot inherit from builtin type"); } if (!MATCHES(sequence(SEPARATOR_CURLYBRACKETOPEN))) - throwParseError("expected '{' after struct definition", -1); + throwParserError("expected '{' after struct definition", -1); while (!MATCHES(sequence(SEPARATOR_CURLYBRACKETCLOSE))) { structNode->addMember(parseMember()); @@ -988,7 +988,7 @@ namespace hex::pl { auto typeName = getValue(-2).get(); auto underlyingType = parseType(); - if (underlyingType->getEndian().has_value()) throwParseError("underlying type may not have an endian specification", -2); + if (underlyingType->getEndian().has_value()) throwParserError("underlying type may not have an endian specification", -2); const auto enumNode = create(new ASTNodeEnum(underlyingType)); const auto typeDecl = addType(typeName, enumNode); @@ -998,7 +998,7 @@ namespace hex::pl { }; if (!MATCHES(sequence(SEPARATOR_CURLYBRACKETOPEN))) - throwParseError("expected '{' after enum definition", -1); + throwParserError("expected '{' after enum definition", -1); ASTNode *lastEntry = nullptr; while (!MATCHES(sequence(SEPARATOR_CURLYBRACKETCLOSE))) { @@ -1018,15 +1018,15 @@ namespace hex::pl { enumNode->addEntry(name, valueExpr); } else if (MATCHES(sequence(SEPARATOR_ENDOFPROGRAM))) - throwParseError("unexpected end of program", -2); + throwParserError("unexpected end of program", -2); else - throwParseError("invalid enum entry", -1); + throwParserError("invalid enum entry", -1); if (!MATCHES(sequence(SEPARATOR_COMMA))) { if (MATCHES(sequence(SEPARATOR_CURLYBRACKETCLOSE))) break; else - throwParseError("missing ',' between enum entries", -1); + throwParserError("missing ',' between enum entries", -1); } } @@ -1054,12 +1054,12 @@ namespace hex::pl { } else if (MATCHES(sequence(VALUETYPE_PADDING, OPERATOR_INHERIT))) { bitfieldNode->addEntry("padding", parseMathematicalExpression()); } else if (MATCHES(sequence(SEPARATOR_ENDOFPROGRAM))) - throwParseError("unexpected end of program", -2); + throwParserError("unexpected end of program", -2); else - throwParseError("invalid bitfield member", 0); + throwParserError("invalid bitfield member", 0); if (!MATCHES(sequence(SEPARATOR_ENDOFEXPRESSION))) - throwParseError("missing ';' at end of expression", -1); + throwParserError("missing ';' at end of expression", -1); // Consume superfluous semicolons while (MATCHES(sequence(SEPARATOR_ENDOFEXPRESSION))) @@ -1104,11 +1104,11 @@ namespace hex::pl { size = parseMathematicalExpression(); if (!MATCHES(sequence(SEPARATOR_SQUAREBRACKETCLOSE))) - throwParseError("expected closing ']' at end of array declaration", -1); + throwParserError("expected closing ']' at end of array declaration", -1); } if (!MATCHES(sequence(OPERATOR_AT))) - throwParseError("expected placement instruction", -1); + throwParserError("expected placement instruction", -1); auto placementOffset = parseMathematicalExpression(); @@ -1128,11 +1128,11 @@ namespace hex::pl { auto builtinType = dynamic_cast(sizeType->getType()); if (builtinType == nullptr || !Token::isUnsigned(builtinType->getType())) - throwParseError("invalid type used for pointer size", -1); + throwParserError("invalid type used for pointer size", -1); } if (!MATCHES(sequence(OPERATOR_AT))) - throwParseError("expected placement instruction", -1); + throwParserError("expected placement instruction", -1); auto placementOffset = parseMathematicalExpression(); @@ -1145,7 +1145,7 @@ namespace hex::pl { std::vector statements; if (!MATCHES(sequence(IDENTIFIER))) - throwParseError("expected namespace identifier"); + throwParserError("expected namespace identifier"); this->m_currNamespace.push_back(this->m_currNamespace.back()); @@ -1159,7 +1159,7 @@ namespace hex::pl { } if (!MATCHES(sequence(SEPARATOR_CURLYBRACKETOPEN))) - throwParseError("expected '{' at start of namespace"); + throwParserError("expected '{' at start of namespace"); while (!MATCHES(sequence(SEPARATOR_CURLYBRACKETCLOSE))) { auto newStatements = parseStatements(); @@ -1181,7 +1181,7 @@ namespace hex::pl { return parseVariablePlacement(type); else if (MATCHES(sequence(OPERATOR_STAR, IDENTIFIER, OPERATOR_INHERIT))) return parsePointerVariablePlacement(type); - else throwParseError("invalid sequence", 0); + else throwParserError("invalid sequence", 0); } /* Program */ @@ -1221,13 +1221,13 @@ namespace hex::pl { statement = parseFunctionDefinition(); else if (MATCHES(sequence(KEYWORD_NAMESPACE))) return parseNamespace(); - else throwParseError("invalid sequence", 0); + else throwParserError("invalid sequence", 0); if (MATCHES(sequence(SEPARATOR_SQUAREBRACKETOPEN, SEPARATOR_SQUAREBRACKETOPEN))) parseAttribute(dynamic_cast(statement)); if (!MATCHES(sequence(SEPARATOR_ENDOFEXPRESSION))) - throwParseError("missing ';' at end of expression", -1); + throwParserError("missing ';' at end of expression", -1); // Consume superfluous semicolons while (MATCHES(sequence(SEPARATOR_ENDOFEXPRESSION))) @@ -1242,7 +1242,7 @@ namespace hex::pl { auto typeName = getNamespacePrefixedName(name); if (this->m_types.contains(typeName)) - throwParseError(hex::format("redefinition of type '{}'", typeName)); + throwParserError(hex::format("redefinition of type '{}'", typeName)); auto typeDecl = create(new ASTNodeTypeDecl(typeName, node, endian)); this->m_types.insert({ typeName, typeDecl }); @@ -1263,14 +1263,14 @@ namespace hex::pl { auto program = parseTillToken(SEPARATOR_ENDOFPROGRAM); if (program.empty() || this->m_curr != tokens.end()) - throwParseError("program is empty!", -1); + throwParserError("program is empty!", -1); return program; - } catch (ParseError &e) { + } catch (PatternLanguageError &e) { this->m_error = e; - } - return {}; + return std::nullopt; + } } } \ No newline at end of file diff --git a/lib/libimhex/source/pattern_language/pattern_language.cpp b/lib/libimhex/source/pattern_language/pattern_language.cpp index 0455bc6d6..2a5dc0d6a 100644 --- a/lib/libimhex/source/pattern_language/pattern_language.cpp +++ b/lib/libimhex/source/pattern_language/pattern_language.cpp @@ -10,8 +10,6 @@ #include #include -#include - namespace hex::pl { class PatternData; @@ -113,6 +111,15 @@ namespace hex::pl { return std::nullopt; } + if (!this->m_validator->validate(*ast)) { + this->m_currError = this->m_validator->getError(); + + for (auto &node : *ast) + delete node; + + return std::nullopt; + } + return ast; } @@ -172,7 +179,7 @@ namespace hex::pl { return this->m_evaluator->getConsole().getLog(); } - const std::optional> &PatternLanguage::getError() { + const std::optional &PatternLanguage::getError() { return this->m_currError; } diff --git a/lib/libimhex/source/pattern_language/preprocessor.cpp b/lib/libimhex/source/pattern_language/preprocessor.cpp index 899590e38..05c2726b5 100644 --- a/lib/libimhex/source/pattern_language/preprocessor.cpp +++ b/lib/libimhex/source/pattern_language/preprocessor.cpp @@ -8,9 +8,6 @@ namespace hex::pl { - Preprocessor::Preprocessor() { - } - std::optional Preprocessor::preprocess(const std::string &code, bool initialRun) { u32 offset = 0; u32 lineNumber = 1; @@ -220,8 +217,9 @@ namespace hex::pl { throwPreprocessorError(hex::format("no #pragma handler registered for type {0}", type.c_str()), pragmaLine); } } - } catch (PreprocessorError &e) { + } catch (PatternLanguageError &e) { this->m_error = e; + return std::nullopt; } diff --git a/lib/libimhex/source/pattern_language/validator.cpp b/lib/libimhex/source/pattern_language/validator.cpp index 9e4893568..72310a9d3 100644 --- a/lib/libimhex/source/pattern_language/validator.cpp +++ b/lib/libimhex/source/pattern_language/validator.cpp @@ -9,9 +9,6 @@ namespace hex::pl { - Validator::Validator() { - } - bool Validator::validate(const std::vector &ast) { std::unordered_set identifiers; @@ -19,16 +16,16 @@ namespace hex::pl { for (const auto &node : ast) { if (node == nullptr) - throwValidateError("nullptr in AST. This is a bug!", 1); + throwValidatorError("nullptr in AST. This is a bug!", 1); if (auto variableDeclNode = dynamic_cast(node); variableDeclNode != nullptr) { if (!identifiers.insert(variableDeclNode->getName().data()).second) - throwValidateError(hex::format("redefinition of identifier '{0}'", variableDeclNode->getName().data()), variableDeclNode->getLineNumber()); + throwValidatorError(hex::format("redefinition of identifier '{0}'", variableDeclNode->getName().data()), variableDeclNode->getLineNumber()); this->validate({ variableDeclNode->getType() }); } else if (auto typeDeclNode = dynamic_cast(node); typeDeclNode != nullptr) { if (!identifiers.insert(typeDeclNode->getName().data()).second) - throwValidateError(hex::format("redefinition of identifier '{0}'", typeDeclNode->getName().data()), typeDeclNode->getLineNumber()); + throwValidatorError(hex::format("redefinition of identifier '{0}'", typeDeclNode->getName().data()), typeDeclNode->getLineNumber()); this->validate({ typeDeclNode->getType() }); } else if (auto structNode = dynamic_cast(node); structNode != nullptr) { @@ -39,12 +36,12 @@ namespace hex::pl { std::unordered_set enumIdentifiers; for (auto &[name, value] : enumNode->getEntries()) { if (!enumIdentifiers.insert(name).second) - throwValidateError(hex::format("redefinition of enum constant '{0}'", name.c_str()), value->getLineNumber()); + throwValidatorError(hex::format("redefinition of enum constant '{0}'", name.c_str()), value->getLineNumber()); } } } - } catch (ValidatorError &e) { + } catch (PatternLanguageError &e) { this->m_error = e; return false; } diff --git a/plugins/builtin/source/content/views/view_pattern_editor.cpp b/plugins/builtin/source/content/views/view_pattern_editor.cpp index a901c2fe7..3e25b7344 100644 --- a/plugins/builtin/source/content/views/view_pattern_editor.cpp +++ b/plugins/builtin/source/content/views/view_pattern_editor.cpp @@ -9,9 +9,6 @@ #include #include -#include - -#include namespace hex::plugin::builtin { @@ -640,7 +637,8 @@ namespace hex::plugin::builtin { auto error = this->m_evaluatorRuntime->getError(); if (error.has_value()) { - this->m_textEditor.SetErrorMarkers({ error.value() }); + TextEditor::ErrorMarkers errorMarkers = { { error->getLineNumber(), error->what() } }; + this->m_textEditor.SetErrorMarkers(errorMarkers); } this->m_console = this->m_evaluatorRuntime->getConsoleLog(); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index fe3c39ea3..32400d103 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,5 +1,7 @@ project(unit_tests) +enable_testing() + add_custom_target(unit_tests) add_subdirectory(common) diff --git a/tests/pattern_language/source/main.cpp b/tests/pattern_language/source/main.cpp index 88b79439c..c70e9fc7c 100644 --- a/tests/pattern_language/source/main.cpp +++ b/tests/pattern_language/source/main.cpp @@ -63,7 +63,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->getLineNumber(), error->what()); for (auto &[level, message] : language.getConsoleLog()) hex::log::info("Evaluate error: {}", message);