1
0
mirror of synced 2025-01-30 19:43:43 +01:00

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
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 2000 additions and 1903 deletions

View File

@ -1,9 +1,8 @@
#pragma once #pragma once
#include <hex/views/view.hpp> #include <hex/views/view.hpp>
#include <hex/pattern_language/evaluator.hpp>
#include <hex/pattern_language/pattern_language.hpp> #include <hex/pattern_language/pattern_language.hpp>
#include <hex/pattern_language/log_console.hpp>
#include <hex/providers/provider.hpp> #include <hex/providers/provider.hpp>
#include <cstring> #include <cstring>

47
libstdpl/math.pl Normal file
View File

@ -0,0 +1,47 @@
namespace std::math {
fn min_u(u128 a, u128 b) {
return (a < b) ? a : b;
};
fn min_i(s128 a, s128 b) {
return (a < b) ? a : b;
};
fn min_f(double a, double b) {
return (a < b) ? a : b;
};
fn max_u(u128 a, u128 b) {
return (a > b) ? a : b;
};
fn max_i(s128 a, s128 b) {
return (a > b) ? a : b;
};
fn max_f(double a, double b) {
return (a > b) ? a : b;
};
fn abs_i(s128 value) {
return value < 0 ? -value : value;
};
fn abs_d(double value) {
return value < 0 ? -value : value;
};
fn ceil(double value) {
s128 cast;
cast = value;
return cast + 1;
};
}
std::print("{}", std::math::ceil(123.6));

View File

@ -3,14 +3,43 @@
#include <hex/helpers/shared_data.hpp> #include <hex/helpers/shared_data.hpp>
#include <hex/helpers/fmt.hpp> #include <hex/helpers/fmt.hpp>
#include <hex/pattern_language/ast_node.hpp> #include <hex/pattern_language/token.hpp>
#include <hex/pattern_language/log_console.hpp> #include <hex/pattern_language/log_console.hpp>
#include <hex/pattern_language/evaluator.hpp> #include <hex/pattern_language/evaluator.hpp>
#include <hex/pattern_language/pattern_data.hpp>
#include <vector> #include <vector>
#include <fmt/args.h>
namespace hex::plugin::builtin { namespace hex::plugin::builtin {
std::string format(pl::Evaluator *, auto params) {
auto format = pl::Token::literalToString(params[0], true);
std::string message;
fmt::dynamic_format_arg_store<fmt::format_context> formatArgs;
for (u32 i = 1; i < params.size(); i++) {
auto &param = params[i];
std::visit(overloaded {
[&](pl::PatternData* value) {
formatArgs.push_back(hex::format("{} {} @ 0x{:X}", value->getTypeName(), value->getVariableName(), value->getOffset()));
},
[&](auto &&value) {
formatArgs.push_back(value);
}
}, param);
}
try {
return fmt::vformat(format, formatArgs);
} catch (fmt::format_error &error) {
hex::pl::LogConsole::abortEvaluation(hex::format("format error: {}", error.what()));
}
}
void registerPatternLanguageFunctions() { void registerPatternLanguageFunctions() {
using namespace hex::pl; using namespace hex::pl;
@ -18,55 +47,37 @@ namespace hex::plugin::builtin {
{ {
/* assert(condition, message) */ /* assert(condition, message) */
ContentRegistry::PatternLanguageFunctions::add(nsStd, "assert", 2, [](auto &ctx, auto params) { ContentRegistry::PatternLanguageFunctions::add(nsStd, "assert", 2, [](Evaluator *ctx, auto params) -> std::optional<Token::Literal> {
auto condition = AS_TYPE(ASTNodeIntegerLiteral, params[0])->getValue(); auto condition = Token::literalToBoolean(params[0]);
auto message = AS_TYPE(ASTNodeStringLiteral, params[1])->getString(); auto message = std::get<std::string>(params[1]);
if (LITERAL_COMPARE(condition, condition == 0)) if (!condition)
ctx.getConsole().abortEvaluation(hex::format("assertion failed \"{0}\"", message.data())); LogConsole::abortEvaluation(hex::format("assertion failed \"{0}\"", message));
return nullptr; return std::nullopt;
}); });
/* assert_warn(condition, message) */ /* assert_warn(condition, message) */
ContentRegistry::PatternLanguageFunctions::add(nsStd, "assert_warn", 2, [](auto ctx, auto params) { ContentRegistry::PatternLanguageFunctions::add(nsStd, "assert_warn", 2, [](auto *ctx, auto params) -> std::optional<Token::Literal> {
auto condition = AS_TYPE(ASTNodeIntegerLiteral, params[0])->getValue(); auto condition = Token::literalToBoolean(params[0]);
auto message = AS_TYPE(ASTNodeStringLiteral, params[1])->getString(); auto message = std::get<std::string>(params[1]);
if (LITERAL_COMPARE(condition, condition == 0)) if (!condition)
ctx.getConsole().log(LogConsole::Level::Warning, hex::format("assertion failed \"{0}\"", message)); ctx->getConsole().log(LogConsole::Level::Warning, hex::format("assertion failed \"{0}\"", message));
return nullptr; return std::nullopt;
}); });
/* print(values...) */ /* print(format, args...) */
ContentRegistry::PatternLanguageFunctions::add(nsStd, "print", ContentRegistry::PatternLanguageFunctions::MoreParametersThan | 0, [](auto &ctx, auto params) { ContentRegistry::PatternLanguageFunctions::add(nsStd, "print", ContentRegistry::PatternLanguageFunctions::MoreParametersThan | 0, [](Evaluator *ctx, auto params) -> std::optional<Token::Literal> {
std::string message; ctx->getConsole().log(LogConsole::Level::Info, format(ctx, params));
for (auto& param : params) {
if (auto integerLiteral = dynamic_cast<ASTNodeIntegerLiteral*>(param); integerLiteral != nullptr) {
std::visit([&](auto &&value) {
using Type = std::remove_cvref_t<decltype(value)>;
if constexpr (std::is_same_v<Type, char>)
message += (char)value;
else if constexpr (std::is_same_v<Type, bool>)
message += value == 0 ? "false" : "true";
else if constexpr (std::is_unsigned_v<Type>)
message += std::to_string(static_cast<u64>(value));
else if constexpr (std::is_signed_v<Type>)
message += std::to_string(static_cast<s64>(value));
else if constexpr (std::is_floating_point_v<Type>)
message += std::to_string(value);
else
message += "< Custom Type >";
}, integerLiteral->getValue());
}
else if (auto stringLiteral = dynamic_cast<ASTNodeStringLiteral*>(param); stringLiteral != nullptr)
message += stringLiteral->getString();
}
ctx.getConsole().log(LogConsole::Level::Info, message); return std::nullopt;
});
return nullptr; /* format(format, args...) */
ContentRegistry::PatternLanguageFunctions::add(nsStd, "format", ContentRegistry::PatternLanguageFunctions::MoreParametersThan | 0, [](Evaluator *ctx, auto params) -> std::optional<Token::Literal> {
return format(ctx, params);
}); });
} }
@ -75,109 +86,82 @@ namespace hex::plugin::builtin {
{ {
/* align_to(alignment, value) */ /* align_to(alignment, value) */
ContentRegistry::PatternLanguageFunctions::add(nsStdMem, "align_to", 2, [](auto &ctx, auto params) -> ASTNode* { ContentRegistry::PatternLanguageFunctions::add(nsStdMem, "align_to", 2, [](Evaluator *ctx, auto params) -> std::optional<Token::Literal> {
auto alignment = AS_TYPE(ASTNodeIntegerLiteral, params[0])->getValue(); auto alignment = Token::literalToUnsigned(params[0]);
auto value = AS_TYPE(ASTNodeIntegerLiteral, params[1])->getValue(); auto value = Token::literalToUnsigned(params[1]);
auto result = std::visit([](auto &&alignment, auto &&value) { u128 remainder = value % alignment;
u64 remainder = u64(value) % u64(alignment);
return remainder != 0 ? u64(value) + (u64(alignment) - remainder) : u64(value);
}, alignment, value);
return new ASTNodeIntegerLiteral(u64(result)); return remainder != 0 ? value + (alignment - remainder) : value;
}); });
/* base_address() */ /* base_address() */
ContentRegistry::PatternLanguageFunctions::add(nsStdMem, "base_address", ContentRegistry::PatternLanguageFunctions::NoParameters, [](auto &ctx, auto params) -> ASTNode* { ContentRegistry::PatternLanguageFunctions::add(nsStdMem, "base_address", ContentRegistry::PatternLanguageFunctions::NoParameters, [](Evaluator *ctx, auto params) -> std::optional<Token::Literal> {
return new ASTNodeIntegerLiteral(u64(ImHexApi::Provider::get()->getBaseAddress())); return u128(ctx->getProvider()->getBaseAddress());
}); });
/* size() */ /* size() */
ContentRegistry::PatternLanguageFunctions::add(nsStdMem, "size", ContentRegistry::PatternLanguageFunctions::NoParameters, [](auto &ctx, auto params) -> ASTNode* { ContentRegistry::PatternLanguageFunctions::add(nsStdMem, "size", ContentRegistry::PatternLanguageFunctions::NoParameters, [](Evaluator *ctx, auto params) -> std::optional<Token::Literal> {
return new ASTNodeIntegerLiteral(u64(ImHexApi::Provider::get()->getActualSize())); return u128(ctx->getProvider()->getActualSize());
}); });
/* find_sequence(occurrence_index, bytes...) */ /* find_sequence(occurrence_index, bytes...) */
ContentRegistry::PatternLanguageFunctions::add(nsStdMem, "find_sequence", ContentRegistry::PatternLanguageFunctions::MoreParametersThan | 1, [](auto &ctx, auto params) { ContentRegistry::PatternLanguageFunctions::add(nsStdMem, "find_sequence", ContentRegistry::PatternLanguageFunctions::MoreParametersThan | 1, [](Evaluator *ctx, auto params) -> std::optional<Token::Literal> {
auto& occurrenceIndex = AS_TYPE(ASTNodeIntegerLiteral, params[0])->getValue(); auto occurrenceIndex = Token::literalToUnsigned(params[0]);
std::vector<u8> sequence; std::vector<u8> sequence;
for (u32 i = 1; i < params.size(); i++) { for (u32 i = 1; i < params.size(); i++) {
sequence.push_back(std::visit([&](auto &&value) -> u8 { auto byte = Token::literalToUnsigned(params[i]);
if (value <= 0xFF)
return value; if (byte > 0xFF)
else LogConsole::abortEvaluation(hex::format("byte #{} value out of range: {} > 0xFF", i, u64(byte)));
ctx.getConsole().abortEvaluation("sequence bytes need to fit into 1 byte");
}, AS_TYPE(ASTNodeIntegerLiteral, params[i])->getValue())); sequence.push_back(u8(byte & 0xFF));
} }
std::vector<u8> bytes(sequence.size(), 0x00); std::vector<u8> bytes(sequence.size(), 0x00);
u32 occurrences = 0; u32 occurrences = 0;
for (u64 offset = 0; offset < ImHexApi::Provider::get()->getSize() - sequence.size(); offset++) { for (u64 offset = 0; offset < ctx->getProvider()->getSize() - sequence.size(); offset++) {
ImHexApi::Provider::get()->read(offset, bytes.data(), bytes.size()); ctx->getProvider()->read(offset, bytes.data(), bytes.size());
if (bytes == sequence) { if (bytes == sequence) {
if (LITERAL_COMPARE(occurrenceIndex, occurrences < occurrenceIndex)) { if (occurrences < occurrenceIndex) {
occurrences++; occurrences++;
continue; continue;
} }
return new ASTNodeIntegerLiteral(offset); return u128(offset);
} }
} }
ctx.getConsole().abortEvaluation("failed to find sequence"); LogConsole::abortEvaluation("failed to find sequence");
}); });
/* read_unsigned(address, size) */ /* read_unsigned(address, size) */
ContentRegistry::PatternLanguageFunctions::add(nsStdMem, "read_unsigned", 2, [](auto &ctx, auto params) { ContentRegistry::PatternLanguageFunctions::add(nsStdMem, "read_unsigned", 2, [](Evaluator *ctx, auto params) -> std::optional<Token::Literal> {
auto address = AS_TYPE(ASTNodeIntegerLiteral, params[0])->getValue(); auto address = Token::literalToUnsigned(params[0]);
auto size = AS_TYPE(ASTNodeIntegerLiteral, params[1])->getValue(); auto size = Token::literalToUnsigned(params[1]);
if (LITERAL_COMPARE(address, address >= ImHexApi::Provider::get()->getActualSize())) if (size > 16)
ctx.getConsole().abortEvaluation("address out of range"); LogConsole::abortEvaluation("read size out of range");
return std::visit([&](auto &&address, auto &&size) { u128 result = 0;
if (size <= 0 || size > 16) ctx->getProvider()->read(address, &result, size);
ctx.getConsole().abortEvaluation("invalid read size");
u8 value[(u8)size]; return result;
ImHexApi::Provider::get()->read(address, value, size);
switch ((u8)size) {
case 1: return new ASTNodeIntegerLiteral(*reinterpret_cast<u8*>(value));
case 2: return new ASTNodeIntegerLiteral(*reinterpret_cast<u16*>(value));
case 4: return new ASTNodeIntegerLiteral(*reinterpret_cast<u32*>(value));
case 8: return new ASTNodeIntegerLiteral(*reinterpret_cast<u64*>(value));
case 16: return new ASTNodeIntegerLiteral(*reinterpret_cast<u128*>(value));
default: ctx.getConsole().abortEvaluation("invalid read size");
}
}, address, size);
}); });
/* read_signed(address, size) */ /* read_signed(address, size) */
ContentRegistry::PatternLanguageFunctions::add(nsStdMem, "read_signed", 2, [](auto &ctx, auto params) { ContentRegistry::PatternLanguageFunctions::add(nsStdMem, "read_signed", 2, [](Evaluator *ctx, auto params) -> std::optional<Token::Literal> {
auto address = AS_TYPE(ASTNodeIntegerLiteral, params[0])->getValue(); auto address = Token::literalToUnsigned(params[0]);
auto size = AS_TYPE(ASTNodeIntegerLiteral, params[1])->getValue(); auto size = Token::literalToUnsigned(params[1]);
if (LITERAL_COMPARE(address, address >= ImHexApi::Provider::get()->getActualSize())) if (size > 16)
ctx.getConsole().abortEvaluation("address out of range"); LogConsole::abortEvaluation("read size out of range");
return std::visit([&](auto &&address, auto &&size) { s128 value;
if (size <= 0 || size > 16) ctx->getProvider()->read(address, &value, size);
ctx.getConsole().abortEvaluation("invalid read size"); return hex::signExtend(size * 8, value);
u8 value[(u8)size];
ImHexApi::Provider::get()->read(address, value, size);
switch ((u8)size) {
case 1: return new ASTNodeIntegerLiteral(*reinterpret_cast<s8*>(value));
case 2: return new ASTNodeIntegerLiteral(*reinterpret_cast<s16*>(value));
case 4: return new ASTNodeIntegerLiteral(*reinterpret_cast<s32*>(value));
case 8: return new ASTNodeIntegerLiteral(*reinterpret_cast<s64*>(value));
case 16: return new ASTNodeIntegerLiteral(*reinterpret_cast<s128*>(value));
default: ctx.getConsole().abortEvaluation("invalid read size");
}
}, address, size);
}); });
} }
@ -185,31 +169,39 @@ namespace hex::plugin::builtin {
ContentRegistry::PatternLanguageFunctions::Namespace nsStdStr = { "std", "str" }; ContentRegistry::PatternLanguageFunctions::Namespace nsStdStr = { "std", "str" };
{ {
/* length(string) */ /* length(string) */
ContentRegistry::PatternLanguageFunctions::add(nsStdStr, "length", 1, [](auto &ctx, auto params) { ContentRegistry::PatternLanguageFunctions::add(nsStdStr, "length", 1, [](Evaluator *ctx, auto params) -> std::optional<Token::Literal> {
auto string = AS_TYPE(ASTNodeStringLiteral, params[1])->getString(); auto string = Token::literalToString(params[0], false);
return new ASTNodeIntegerLiteral(u32(string.length())); return u128(string.length());
}); });
/* at(string, index) */ /* at(string, index) */
ContentRegistry::PatternLanguageFunctions::add(nsStdStr, "at", 2, [](auto &ctx, auto params) { ContentRegistry::PatternLanguageFunctions::add(nsStdStr, "at", 2, [](Evaluator *ctx, auto params) -> std::optional<Token::Literal> {
auto string = AS_TYPE(ASTNodeStringLiteral, params[0])->getString(); auto string = Token::literalToString(params[0], false);
auto index = AS_TYPE(ASTNodeIntegerLiteral, params[1])->getValue(); auto index = Token::literalToSigned(params[1]);
if (LITERAL_COMPARE(index, index >= string.length() || index < 0)) if (std::abs(index) >= string.length())
ctx.getConsole().abortEvaluation("character index out of bounds"); LogConsole::abortEvaluation("character index out of range");
return std::visit([&](auto &&value) { return new ASTNodeIntegerLiteral(char(string[u32(value)])); }, index); if (index >= 0)
return char(string[index]);
else
return char(string[string.length() - -index]);
}); });
/* compare(left, right) */ /* substr(string, pos, count) */
ContentRegistry::PatternLanguageFunctions::add(nsStdStr, "compare", 2, [](auto &ctx, auto params) { ContentRegistry::PatternLanguageFunctions::add(nsStdStr, "substr", 3, [](Evaluator *ctx, auto params) -> std::optional<Token::Literal> {
auto left = AS_TYPE(ASTNodeStringLiteral, params[0])->getString(); auto string = Token::literalToString(params[0], false);
auto right = AS_TYPE(ASTNodeStringLiteral, params[1])->getString(); auto pos = Token::literalToUnsigned(params[1]);
auto size = Token::literalToUnsigned(params[2]);
return new ASTNodeIntegerLiteral(bool(left == right)); if (pos > size)
LogConsole::abortEvaluation("character index out of range");
return string.substr(pos, size);
}); });
} }
} }
} }

View File

@ -2,6 +2,7 @@
#include <hex.hpp> #include <hex.hpp>
#include <hex/helpers/concepts.hpp> #include <hex/helpers/concepts.hpp>
#include <hex/pattern_language/token.hpp>
#include <functional> #include <functional>
#include <map> #include <map>
@ -15,7 +16,7 @@ namespace hex {
class View; class View;
class LanguageDefinition; class LanguageDefinition;
namespace pl { class ASTNode; class Evaluator; } namespace pl { class Evaluator; }
namespace dp { class Node; } namespace dp { class Node; }
/* /*
@ -90,7 +91,7 @@ namespace hex {
constexpr static u32 NoParameters = 0x0000'0000; constexpr static u32 NoParameters = 0x0000'0000;
using Namespace = std::vector<std::string>; using Namespace = std::vector<std::string>;
using Callback = std::function<hex::pl::ASTNode*(hex::pl::Evaluator&, std::vector<hex::pl::ASTNode*>&)>; using Callback = std::function<std::optional<hex::pl::Token::Literal>(hex::pl::Evaluator*, const std::vector<hex::pl::Token::Literal>&)>;
struct Function { struct Function {
u32 parameterCount; u32 parameterCount;

View File

@ -57,6 +57,11 @@ namespace hex {
return (value & mask) >> to; return (value & mask) >> to;
} }
constexpr inline s128 signExtend(size_t numBits, s128 value) {
s128 mask = 1U << (numBits - 1);
return (value ^ mask) - mask;
}
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; }; template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>; template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,65 @@
#pragma once
#include <optional>
#include <vector>
#include <hex/pattern_language/token.hpp>
namespace hex::pl {
class ASTNode;
class ASTNodeAttribute;
class PatternData;
class Evaluator;
class Attributable {
protected:
Attributable() = default;
Attributable(const Attributable &) = default;
public:
void addAttribute(ASTNodeAttribute *attribute) {
this->m_attributes.push_back(attribute);
}
[[nodiscard]] const auto &getAttributes() const {
return this->m_attributes;
}
private:
std::vector<ASTNodeAttribute *> m_attributes;
};
class Clonable {
public:
[[nodiscard]]
virtual ASTNode* clone() const = 0;
};
class ASTNode : public Clonable {
public:
constexpr ASTNode() = default;
constexpr virtual ~ASTNode() = default;
constexpr ASTNode(const ASTNode &) = default;
[[nodiscard]] constexpr u32 getLineNumber() const { return this->m_lineNumber; }
[[maybe_unused]] constexpr void setLineNumber(u32 lineNumber) { this->m_lineNumber = lineNumber; }
[[nodiscard]] virtual ASTNode *evaluate(Evaluator *evaluator) const { return this->clone(); }
[[nodiscard]] virtual std::vector<PatternData *> createPatterns(Evaluator *evaluator) const { return {}; }
using FunctionResult = std::pair<bool, std::optional<Token::Literal>>;
virtual FunctionResult execute(Evaluator *evaluator) { throw std::pair<u32, std::string>(this->getLineNumber(), "cannot execute non-function statement"); }
private:
u32 m_lineNumber = 1;
};
}

View File

@ -1,93 +1,96 @@
#pragma once #pragma once
#include <hex.hpp>
#include <hex/api/content_registry.hpp>
#include <hex/pattern_language/ast_node.hpp>
#include <hex/pattern_language/log_console.hpp>
#include <bit> #include <bit>
#include <string>
#include <map> #include <map>
#include <optional>
#include <vector> #include <vector>
#define LITERAL_COMPARE(literal, cond) std::visit([&](auto &&literal) { return (cond) != 0; }, literal) #include <hex/pattern_language/log_console.hpp>
#define AS_TYPE(type, value) ctx.template asType<type>(value) #include <hex/api/content_registry.hpp>
namespace hex::prv { class Provider; } namespace hex::prv { class Provider; }
namespace hex::pl { namespace hex::pl {
class PatternData; class PatternData;
class ASTNode;
class Evaluator { class Evaluator {
public: public:
Evaluator() = default; Evaluator() = default;
std::optional<std::vector<PatternData*>> evaluate(const std::vector<ASTNode*>& ast); std::optional<std::vector<PatternData*>> evaluate(const std::vector<ASTNode*> &ast);
LogConsole& getConsole() { return this->m_console; } [[nodiscard]]
LogConsole& getConsole() {
void setDefaultEndian(std::endian endian) { this->m_defaultDataEndian = endian; } return this->m_console;
void setRecursionLimit(u32 limit) { this->m_recursionLimit = limit; }
void setProvider(prv::Provider *provider) { this->m_provider = provider; }
[[nodiscard]] std::endian getCurrentEndian() const { return this->m_endianStack.back(); }
PatternData* patternFromName(const ASTNodeRValue::Path &name);
template<typename T>
T* asType(ASTNode *param) {
if (auto evaluatedParam = dynamic_cast<T*>(param); evaluatedParam != nullptr)
return evaluatedParam;
else
this->getConsole().abortEvaluation("function got wrong type of parameter");
} }
struct Scope { PatternData *parent; std::vector<PatternData*>* scope; };
void pushScope(PatternData *parent, std::vector<PatternData*> &scope) { this->m_scopes.push_back({ parent, &scope }); }
void popScope() { this->m_scopes.pop_back(); }
const Scope& getScope(s32 index) {
static Scope empty;
if (index > 0 || -index >= this->m_scopes.size()) return empty;
return this->m_scopes[this->m_scopes.size() - 1 + index];
}
const Scope& getGlobalScope() {
return this->m_scopes.front();
}
void setProvider(prv::Provider *provider) {
this->m_provider = provider;
}
[[nodiscard]]
prv::Provider *getProvider() const {
return this->m_provider;
}
void setDefaultEndian(std::endian endian) {
this->m_defaultEndian = endian;
}
[[nodiscard]]
std::endian getDefaultEndian() const {
return this->m_defaultEndian;
}
u64& dataOffset() { return this->m_currOffset; }
bool addCustomFunction(const std::string &name, u32 numParams, const ContentRegistry::PatternLanguageFunctions::Callback &function) {
const auto [iter, inserted] = this->m_customFunctions.insert({ name, { numParams, function } });
return inserted;
}
[[nodiscard]]
const std::map<std::string, ContentRegistry::PatternLanguageFunctions::Function>& getCustomFunctions() const {
return this->m_customFunctions;
}
[[nodiscard]]
std::vector<Token::Literal>& getStack() {
return this->m_stack;
}
void createVariable(const std::string &name, ASTNode *type);
void setVariable(const std::string &name, const Token::Literal& value);
private: private:
std::map<std::string, ASTNode*> m_types; u64 m_currOffset;
prv::Provider* m_provider = nullptr; prv::Provider *m_provider = nullptr;
std::endian m_defaultDataEndian = std::endian::native;
u64 m_currOffset = 0;
std::vector<std::endian> m_endianStack;
std::vector<PatternData*> m_globalMembers;
std::vector<std::vector<PatternData*>*> m_currMembers;
std::vector<std::vector<PatternData*>*> m_localVariables;
std::vector<PatternData*> m_currMemberScope;
std::vector<u8> m_localStack;
std::map<std::string, ContentRegistry::PatternLanguageFunctions::Function> m_definedFunctions;
LogConsole m_console; LogConsole m_console;
u32 m_recursionLimit; std::endian m_defaultEndian = std::endian::native;
u32 m_currRecursionDepth;
void createLocalVariable(const std::string &varName, PatternData *pattern); std::vector<Scope> m_scopes;
void setLocalVariableValue(const std::string &varName, const void *value, size_t size); std::map<std::string, ContentRegistry::PatternLanguageFunctions::Function> m_customFunctions;
std::vector<ASTNode*> m_customFunctionDefinitions;
ASTNodeIntegerLiteral* evaluateScopeResolution(ASTNodeScopeResolution *node); std::vector<Token::Literal> m_stack;
ASTNodeIntegerLiteral* evaluateRValue(ASTNodeRValue *node);
ASTNode* evaluateFunctionCall(ASTNodeFunctionCall *node);
ASTNodeIntegerLiteral* evaluateTypeOperator(ASTNodeTypeOperator *typeOperatorNode);
ASTNodeIntegerLiteral* evaluateOperator(ASTNodeIntegerLiteral *left, ASTNodeIntegerLiteral *right, Token::Operator op);
ASTNodeIntegerLiteral* evaluateOperand(ASTNode *node);
ASTNodeIntegerLiteral* evaluateTernaryExpression(ASTNodeTernaryExpression *node);
ASTNodeIntegerLiteral* evaluateMathematicalExpression(ASTNodeNumericExpression *node);
void evaluateFunctionDefinition(ASTNodeFunctionDefinition *node);
std::optional<ASTNode*> evaluateFunctionBody(const std::vector<ASTNode*> &body);
PatternData* findPattern(std::vector<PatternData*> currMembers, const ASTNodeRValue::Path &path);
PatternData* evaluateAttributes(ASTNode *currNode, PatternData *currPattern);
PatternData* evaluateBuiltinType(ASTNodeBuiltinType *node);
void evaluateMember(ASTNode *node, std::vector<PatternData*> &currMembers, bool increaseOffset);
PatternData* evaluateStruct(ASTNodeStruct *node);
PatternData* evaluateUnion(ASTNodeUnion *node);
PatternData* evaluateEnum(ASTNodeEnum *node);
PatternData* evaluateBitfield(ASTNodeBitfield *node);
PatternData* evaluateType(ASTNodeTypeDecl *node);
PatternData* evaluateVariable(ASTNodeVariableDecl *node);
PatternData* evaluateArray(ASTNodeArrayVariableDecl *node);
PatternData* evaluateStaticArray(ASTNodeArrayVariableDecl *node);
PatternData* evaluateDynamicArray(ASTNodeArrayVariableDecl *node);
PatternData* evaluatePointer(ASTNodePointerVariableDecl *node);
}; };
} }

View File

@ -7,8 +7,12 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
#include <hex/pattern_language/ast_node_base.hpp>
namespace hex::pl { namespace hex::pl {
class ASTNode;
class LogConsole { class LogConsole {
public: public:
enum Level { enum Level {
@ -18,9 +22,10 @@ namespace hex::pl {
Error Error
}; };
const auto& getLog() { return this->m_consoleLog; } [[nodiscard]]
const auto& getLog() const { return this->m_consoleLog; }
using EvaluateError = std::string; using EvaluateError = std::pair<u32, std::string>;
void log(Level level, const std::string &message) { void log(Level level, const std::string &message) {
switch (level) { switch (level) {
@ -32,16 +37,29 @@ namespace hex::pl {
} }
} }
[[noreturn]] void abortEvaluation(const std::string &message) { [[noreturn]]
throw EvaluateError(message); static void abortEvaluation(const std::string &message) {
throw EvaluateError(0, message);
}
[[noreturn]]
static void abortEvaluation(const std::string &message, const auto *node) {
throw EvaluateError(static_cast<const ASTNode*>(node)->getLineNumber(), message);
} }
void clear() { void clear() {
this->m_consoleLog.clear(); this->m_consoleLog.clear();
this->m_lastHardError = { };
} }
void setHardError(const EvaluateError &error) { this->m_lastHardError = error; }
[[nodiscard]]
const LogConsole::EvaluateError& getLastHardError() { return this->m_lastHardError; };
private: private:
std::vector<std::pair<Level, std::string>> m_consoleLog; std::vector<std::pair<Level, std::string>> m_consoleLog;
EvaluateError m_lastHardError;
}; };
} }

View File

@ -37,6 +37,11 @@ namespace hex::pl {
return this->m_curr[index].lineNumber; return this->m_curr[index].lineNumber;
} }
auto* create(auto *node) {
node->setLineNumber(this->getLineNumber(-1));
return node;
}
template<typename T> template<typename T>
const T& getValue(s32 index) const { const T& getValue(s32 index) const {
auto value = std::get_if<T>(&this->m_curr[index].value); auto value = std::get_if<T>(&this->m_curr[index].value);
@ -68,6 +73,7 @@ namespace hex::pl {
ASTNode* parseScopeResolution(); ASTNode* parseScopeResolution();
ASTNode* parseRValue(ASTNodeRValue::Path &path); ASTNode* parseRValue(ASTNodeRValue::Path &path);
ASTNode* parseFactor(); ASTNode* parseFactor();
ASTNode* parseCastExpression();
ASTNode* parseUnaryExpression(); ASTNode* parseUnaryExpression();
ASTNode* parseMultiplicativeExpression(); ASTNode* parseMultiplicativeExpression();
ASTNode* parseAdditiveExpression(); ASTNode* parseAdditiveExpression();
@ -83,7 +89,7 @@ namespace hex::pl {
ASTNode* parseTernaryConditional(); ASTNode* parseTernaryConditional();
ASTNode* parseMathematicalExpression(); ASTNode* parseMathematicalExpression();
ASTNode* parseFunctionDefintion(); ASTNode* parseFunctionDefinition();
ASTNode* parseFunctionStatement(); ASTNode* parseFunctionStatement();
ASTNode* parseFunctionVariableAssignment(); ASTNode* parseFunctionVariableAssignment();
ASTNode* parseFunctionReturnStatement(); ASTNode* parseFunctionReturnStatement();
@ -93,7 +99,7 @@ namespace hex::pl {
void parseAttribute(Attributable *currNode); void parseAttribute(Attributable *currNode);
ASTNode* parseConditional(); ASTNode* parseConditional();
ASTNode* parseWhileStatement(); ASTNode* parseWhileStatement();
ASTNodeTypeDecl* parseType(); ASTNodeTypeDecl* parseType(bool allowString = false);
ASTNode* parseUsingDeclaration(); ASTNode* parseUsingDeclaration();
ASTNode* parsePadding(); ASTNode* parsePadding();
ASTNode* parseMemberVariable(ASTNodeTypeDecl *type); ASTNode* parseMemberVariable(ASTNodeTypeDecl *type);
@ -238,12 +244,6 @@ namespace hex::pl {
return this->m_curr[index].type == type && this->m_curr[index] == value; return this->m_curr[index].type == type && this->m_curr[index] == value;
} }
bool peekOptional(Token::Type type, auto value, u32 index = 0) {
if (index >= this->m_matchedOptionals.size())
return false;
return peek(type, value, std::distance(this->m_curr, this->m_matchedOptionals[index]));
}
}; };
} }

View File

@ -10,6 +10,7 @@
#include <hex/helpers/utils.hpp> #include <hex/helpers/utils.hpp>
#include <hex/helpers/fmt.hpp> #include <hex/helpers/fmt.hpp>
#include <hex/helpers/concepts.hpp> #include <hex/helpers/concepts.hpp>
#include <hex/helpers/logger.hpp>
#include <cstring> #include <cstring>
#include <codecvt> #include <codecvt>
@ -87,6 +88,13 @@ namespace hex::pl {
[[nodiscard]] PatternData* getParent() const { return this->m_parent; } [[nodiscard]] PatternData* getParent() const { return this->m_parent; }
void setParent(PatternData *parent) { this->m_parent = parent; } void setParent(PatternData *parent) { this->m_parent = parent; }
[[nodiscard]] std::string getDisplayName() const { return this->m_displayName.value_or(this->m_variableName); }
void setDisplayName(const std::string &name) { this->m_displayName = name; }
void setFormatterFunction(const ContentRegistry::PatternLanguageFunctions::Function &function, Evaluator *evaluator) {
this->m_formatterFunction = { function, evaluator };
}
virtual void createEntry(prv::Provider* &provider) = 0; virtual void createEntry(prv::Provider* &provider) = 0;
[[nodiscard]] virtual std::string getFormattedName() const = 0; [[nodiscard]] virtual std::string getFormattedName() const = 0;
@ -117,9 +125,9 @@ namespace hex::pl {
static bool sortPatternDataTable(ImGuiTableSortSpecs *sortSpecs, prv::Provider *provider, pl::PatternData* left, pl::PatternData* right) { static bool sortPatternDataTable(ImGuiTableSortSpecs *sortSpecs, prv::Provider *provider, pl::PatternData* left, pl::PatternData* right) {
if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("name")) { if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("name")) {
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending) if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
return left->getVariableName() > right->getVariableName(); return left->getDisplayName() > right->getDisplayName();
else else
return left->getVariableName() < right->getVariableName(); return left->getDisplayName() < right->getDisplayName();
} }
else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("offset")) { else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("offset")) {
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending) if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
@ -208,16 +216,16 @@ namespace hex::pl {
} }
protected: protected:
void createDefaultEntry(const std::string &value) const { void createDefaultEntry(const std::string &value, const Token::Literal &literal) const {
ImGui::TableNextRow(); ImGui::TableNextRow();
ImGui::TreeNodeEx(this->getVariableName().c_str(), ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_AllowItemOverlap); ImGui::TreeNodeEx(this->getDisplayName().c_str(), ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_AllowItemOverlap);
ImGui::TableNextColumn(); ImGui::TableNextColumn();
if (ImGui::Selectable(("##PatternDataLine"s + std::to_string(this->getOffset())).c_str(), false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowItemOverlap)) { if (ImGui::Selectable(("##PatternDataLine"s + std::to_string(this->getOffset())).c_str(), false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowItemOverlap)) {
EventManager::post<RequestSelectionChange>(Region { this->getOffset(), this->getSize() }); EventManager::post<RequestSelectionChange>(Region { this->getOffset(), this->getSize() });
} }
this->drawCommentTooltip(); this->drawCommentTooltip();
ImGui::SameLine(); ImGui::SameLine();
ImGui::Text("%s", this->getVariableName().c_str()); ImGui::Text("%s", this->getDisplayName().c_str());
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight())); ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight()));
ImGui::TableNextColumn(); ImGui::TableNextColumn();
@ -227,7 +235,20 @@ namespace hex::pl {
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::TextColored(ImColor(0xFF9BC64D), "%s", this->getFormattedName().c_str()); ImGui::TextColored(ImColor(0xFF9BC64D), "%s", this->getFormattedName().c_str());
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::Text("%s", value.c_str());
if (!this->m_formatterFunction.has_value())
ImGui::Text("%s", value.c_str());
else {
auto &[func, evaluator] = this->m_formatterFunction.value();
auto result = func.func(evaluator, { literal });
if (result.has_value()) {
if (auto displayValue = std::get_if<std::string>(&result.value()); displayValue != nullptr)
ImGui::Text("%s", displayValue->c_str());
} else {
ImGui::Text("???");
}
}
} }
void drawCommentTooltip() const { void drawCommentTooltip() const {
@ -248,10 +269,13 @@ namespace hex::pl {
size_t m_size; size_t m_size;
u32 m_color; u32 m_color;
std::optional<std::string> m_displayName;
std::string m_variableName; std::string m_variableName;
std::optional<std::string> m_comment; std::optional<std::string> m_comment;
std::string m_typeName; std::string m_typeName;
std::optional<std::pair<ContentRegistry::PatternLanguageFunctions::Function, Evaluator*>> m_formatterFunction;
PatternData *m_parent; PatternData *m_parent;
bool m_local = false; bool m_local = false;
}; };
@ -299,7 +323,7 @@ namespace hex::pl {
ImGui::TableNextRow(); ImGui::TableNextRow();
ImGui::TableNextColumn(); ImGui::TableNextColumn();
bool open = ImGui::TreeNodeEx(this->getVariableName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_AllowItemOverlap); bool open = ImGui::TreeNodeEx(this->getDisplayName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_AllowItemOverlap);
this->drawCommentTooltip(); this->drawCommentTooltip();
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight())); ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight()));
@ -354,7 +378,7 @@ namespace hex::pl {
void setPointedAtPattern(PatternData *pattern) { void setPointedAtPattern(PatternData *pattern) {
this->m_pointedAt = pattern; this->m_pointedAt = pattern;
this->m_pointedAt->setVariableName("*" + this->getVariableName()); this->m_pointedAt->setVariableName("*" + this->getDisplayName());
} }
[[nodiscard]] PatternData* getPointedAtPattern() { [[nodiscard]] PatternData* getPointedAtPattern() {
@ -380,11 +404,11 @@ namespace hex::pl {
} }
void createEntry(prv::Provider* &provider) override { void createEntry(prv::Provider* &provider) override {
u64 data = 0; u128 data = 0;
provider->read(this->getOffset(), &data, this->getSize()); provider->read(this->getOffset(), &data, this->getSize());
data = hex::changeEndianess(data, this->getSize(), this->getEndian()); data = hex::changeEndianess(data, this->getSize(), this->getEndian());
this->createDefaultEntry(hex::format("{:d} (0x{:0{}X})", data, data, this->getSize() * 2)); this->createDefaultEntry(hex::format("{:d} (0x{:0{}X})", data, data, this->getSize() * 2), data);
} }
[[nodiscard]] std::string getFormattedName() const override { [[nodiscard]] std::string getFormattedName() const override {
@ -411,42 +435,12 @@ namespace hex::pl {
} }
void createEntry(prv::Provider* &provider) override { void createEntry(prv::Provider* &provider) override {
u128 data = 0; s128 data = 0;
provider->read(this->getOffset(), &data, this->getSize()); provider->read(this->getOffset(), &data, this->getSize());
data = hex::changeEndianess(data, this->getSize(), this->getEndian()); data = hex::changeEndianess(data, this->getSize(), this->getEndian());
switch (this->getSize()) { data = hex::signExtend(this->getSize() * 8, data);
case 1: { this->createDefaultEntry(hex::format("{:d} (0x{:0{}X})", data, data, 1 * 2), data);
s8 signedData;
std::memcpy(&signedData, &data, 1);
this->createDefaultEntry(hex::format("{:d} (0x{:0{}X})", signedData, data, 1 * 2));
}
break;
case 2: {
s16 signedData;
std::memcpy(&signedData, &data, 2);
this->createDefaultEntry(hex::format("{:d} (0x{:0{}X})", signedData, data, 2 * 2));
}
break;
case 4: {
s32 signedData;
std::memcpy(&signedData, &data, 4);
this->createDefaultEntry(hex::format("{:d} (0x{:0{}X})", signedData, data, 4 * 2));
}
break;
case 8: {
s64 signedData;
std::memcpy(&signedData, &data, 8);
this->createDefaultEntry(hex::format("{:d} (0x{:0{}X})", signedData, data, 8 * 2));
}
break;
case 16: {
s128 signedData;
std::memcpy(&signedData, &data, 16);
this->createDefaultEntry(hex::format("{:d} (0x{:0{}X})", signedData, data, 16 * 2));
}
break;
}
} }
[[nodiscard]] std::string getFormattedName() const override { [[nodiscard]] std::string getFormattedName() const override {
@ -478,13 +472,13 @@ namespace hex::pl {
provider->read(this->getOffset(), &data, 4); provider->read(this->getOffset(), &data, 4);
data = hex::changeEndianess(data, 4, this->getEndian()); data = hex::changeEndianess(data, 4, this->getEndian());
this->createDefaultEntry(hex::format("{:e} (0x{:0{}X})", *reinterpret_cast<float*>(&data), data, this->getSize() * 2)); this->createDefaultEntry(hex::format("{:e} (0x{:0{}X})", *reinterpret_cast<float*>(&data), data, this->getSize() * 2), *reinterpret_cast<float*>(&data));
} else if (this->getSize() == 8) { } else if (this->getSize() == 8) {
u64 data = 0; u64 data = 0;
provider->read(this->getOffset(), &data, 8); provider->read(this->getOffset(), &data, 8);
data = hex::changeEndianess(data, 8, this->getEndian()); data = hex::changeEndianess(data, 8, this->getEndian());
this->createDefaultEntry(hex::format("{:e} (0x{:0{}X})", *reinterpret_cast<double*>(&data), data, this->getSize() * 2)); this->createDefaultEntry(hex::format("{:e} (0x{:0{}X})", *reinterpret_cast<double*>(&data), data, this->getSize() * 2), *reinterpret_cast<double*>(&data));
} }
} }
@ -513,11 +507,11 @@ namespace hex::pl {
provider->read(this->getOffset(), &boolean, 1); provider->read(this->getOffset(), &boolean, 1);
if (boolean == 0) if (boolean == 0)
this->createDefaultEntry("false"); this->createDefaultEntry("false", false);
else if (boolean == 1) else if (boolean == 1)
this->createDefaultEntry("true"); this->createDefaultEntry("true", true);
else else
this->createDefaultEntry("true*"); this->createDefaultEntry("true*", true);
} }
[[nodiscard]] std::string getFormattedName() const override { [[nodiscard]] std::string getFormattedName() const override {
@ -540,7 +534,7 @@ namespace hex::pl {
char character; char character;
provider->read(this->getOffset(), &character, 1); provider->read(this->getOffset(), &character, 1);
this->createDefaultEntry(hex::format("'{0}'", character)); this->createDefaultEntry(hex::format("'{0}'", character), character);
} }
[[nodiscard]] std::string getFormattedName() const override { [[nodiscard]] std::string getFormattedName() const override {
@ -564,7 +558,8 @@ namespace hex::pl {
provider->read(this->getOffset(), &character, 2); provider->read(this->getOffset(), &character, 2);
character = hex::changeEndianess(character, this->getEndian()); character = hex::changeEndianess(character, this->getEndian());
this->createDefaultEntry(hex::format("'{0}'", std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.to_bytes(character))); u128 literal = character;
this->createDefaultEntry(hex::format("'{0}'", std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.to_bytes(character)), literal);
} }
[[nodiscard]] std::string getFormattedName() const override { [[nodiscard]] std::string getFormattedName() const override {
@ -587,7 +582,7 @@ namespace hex::pl {
std::string buffer(this->getSize(), 0x00); std::string buffer(this->getSize(), 0x00);
provider->read(this->getOffset(), buffer.data(), this->getSize()); provider->read(this->getOffset(), buffer.data(), this->getSize());
this->createDefaultEntry(hex::format("\"{0}\"", makeDisplayable(buffer.data(), this->getSize()).c_str())); this->createDefaultEntry(hex::format("\"{0}\"", makeDisplayable(buffer.data(), this->getSize()).c_str()), buffer);
} }
[[nodiscard]] std::string getFormattedName() const override { [[nodiscard]] std::string getFormattedName() const override {
@ -615,7 +610,7 @@ namespace hex::pl {
auto utf8String = std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.to_bytes(buffer); auto utf8String = std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.to_bytes(buffer);
this->createDefaultEntry(hex::format("\"{0}\"", utf8String)) ; this->createDefaultEntry(hex::format("\"{0}\"", utf8String), utf8String);
} }
[[nodiscard]] std::string getFormattedName() const override { [[nodiscard]] std::string getFormattedName() const override {
@ -662,7 +657,7 @@ namespace hex::pl {
ImGui::TableNextRow(); ImGui::TableNextRow();
ImGui::TableNextColumn(); ImGui::TableNextColumn();
bool open = ImGui::TreeNodeEx(this->getVariableName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_AllowItemOverlap); bool open = ImGui::TreeNodeEx(this->getDisplayName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_AllowItemOverlap);
this->drawCommentTooltip(); this->drawCommentTooltip();
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight())); ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight()));
@ -777,7 +772,7 @@ namespace hex::pl {
ImGui::TableNextRow(); ImGui::TableNextRow();
ImGui::TableNextColumn(); ImGui::TableNextColumn();
bool open = ImGui::TreeNodeEx(this->getVariableName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_AllowItemOverlap); bool open = ImGui::TreeNodeEx(this->getDisplayName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_AllowItemOverlap);
this->drawCommentTooltip(); this->drawCommentTooltip();
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight())); ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight()));
@ -866,7 +861,8 @@ namespace hex::pl {
this->m_template = templ; this->m_template = templ;
this->m_entryCount = count; this->m_entryCount = count;
this->m_template->setColor(this->getColor()); this->setColor(this->m_template->getColor());
this->m_template->setEndian(templ->getEndian());
this->m_template->setParent(this); this->m_template->setParent(this);
} }
@ -914,7 +910,7 @@ namespace hex::pl {
void createEntry(prv::Provider* &provider) override { void createEntry(prv::Provider* &provider) override {
ImGui::TableNextRow(); ImGui::TableNextRow();
ImGui::TableNextColumn(); ImGui::TableNextColumn();
bool open = ImGui::TreeNodeEx(this->getVariableName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth); bool open = ImGui::TreeNodeEx(this->getDisplayName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
this->drawCommentTooltip(); this->drawCommentTooltip();
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::TableNextColumn(); ImGui::TableNextColumn();
@ -1045,7 +1041,7 @@ namespace hex::pl {
void createEntry(prv::Provider* &provider) override { void createEntry(prv::Provider* &provider) override {
ImGui::TableNextRow(); ImGui::TableNextRow();
ImGui::TableNextColumn(); ImGui::TableNextColumn();
bool open = ImGui::TreeNodeEx(this->getVariableName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_AllowItemOverlap); bool open = ImGui::TreeNodeEx(this->getDisplayName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_AllowItemOverlap);
this->drawCommentTooltip(); this->drawCommentTooltip();
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::TableNextColumn(); ImGui::TableNextColumn();
@ -1163,14 +1159,18 @@ namespace hex::pl {
bool foundValue = false; bool foundValue = false;
for (auto &[entryValueLiteral, entryName] : this->m_enumValues) { for (auto &[entryValueLiteral, entryName] : this->m_enumValues) {
bool matches = std::visit([&, name = entryName](auto &&entryValue) { bool matches = std::visit(overloaded {
if (value == entryValue) { [&, name = entryName](auto &&entryValue) {
valueString += name; if (value == entryValue) {
foundValue = true; valueString += name;
return true; foundValue = true;
} return true;
}
return false; return false;
},
[](std::string) { return false; },
[](PatternData*) { return false; }
}, entryValueLiteral); }, entryValueLiteral);
if (matches) if (matches)
break; break;
@ -1180,14 +1180,14 @@ namespace hex::pl {
valueString += "???"; valueString += "???";
ImGui::TableNextRow(); ImGui::TableNextRow();
ImGui::TreeNodeEx(this->getVariableName().c_str(), ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_AllowItemOverlap); ImGui::TreeNodeEx(this->getDisplayName().c_str(), ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_AllowItemOverlap);
this->drawCommentTooltip(); this->drawCommentTooltip();
ImGui::TableNextColumn(); ImGui::TableNextColumn();
if (ImGui::Selectable(("##PatternDataLine"s + std::to_string(this->getOffset())).c_str(), false, ImGuiSelectableFlags_SpanAllColumns)) { if (ImGui::Selectable(("##PatternDataLine"s + std::to_string(this->getOffset())).c_str(), false, ImGuiSelectableFlags_SpanAllColumns)) {
EventManager::post<RequestSelectionChange>(Region { this->getOffset(), this->getSize() }); EventManager::post<RequestSelectionChange>(Region { this->getOffset(), this->getSize() });
} }
ImGui::SameLine(); ImGui::SameLine();
ImGui::Text("%s", this->getVariableName().c_str()); ImGui::Text("%s", this->getDisplayName().c_str());
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight())); ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight()));
ImGui::TableNextColumn(); ImGui::TableNextColumn();
@ -1208,7 +1208,7 @@ namespace hex::pl {
return this->m_enumValues; return this->m_enumValues;
} }
void setEnumValues(const std::vector<std::pair<Token::IntegerLiteral, std::string>> &enumValues) { void setEnumValues(const std::vector<std::pair<Token::Literal, std::string>> &enumValues) {
this->m_enumValues = enumValues; this->m_enumValues = enumValues;
} }
@ -1229,7 +1229,7 @@ namespace hex::pl {
} }
private: private:
std::vector<std::pair<Token::IntegerLiteral, std::string>> m_enumValues; std::vector<std::pair<Token::Literal, std::string>> m_enumValues;
}; };
@ -1252,9 +1252,9 @@ namespace hex::pl {
std::reverse(value.begin(), value.end()); std::reverse(value.begin(), value.end());
ImGui::TableNextRow(); ImGui::TableNextRow();
ImGui::TreeNodeEx(this->getVariableName().c_str(), ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_AllowItemOverlap); ImGui::TreeNodeEx(this->getDisplayName().c_str(), ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_AllowItemOverlap);
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::Text("%s", this->getVariableName().c_str()); ImGui::Text("%s", this->getDisplayName().c_str());
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight())); ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight()));
ImGui::TableNextColumn(); ImGui::TableNextColumn();
@ -1309,6 +1309,11 @@ namespace hex::pl {
} }
PatternDataBitfield(const PatternDataBitfield &other) : PatternData(other) {
for (auto &field : other.m_fields)
this->m_fields.push_back(field->clone());
}
~PatternDataBitfield() override { ~PatternDataBitfield() override {
for (auto field : this->m_fields) for (auto field : this->m_fields)
delete field; delete field;
@ -1327,7 +1332,7 @@ namespace hex::pl {
ImGui::TableNextRow(); ImGui::TableNextRow();
ImGui::TableNextColumn(); ImGui::TableNextColumn();
bool open = ImGui::TreeNodeEx(this->getVariableName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_AllowItemOverlap); bool open = ImGui::TreeNodeEx(this->getDisplayName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_AllowItemOverlap);
this->drawCommentTooltip(); this->drawCommentTooltip();
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::TableNextColumn(); ImGui::TableNextColumn();

View File

@ -21,6 +21,8 @@ namespace hex::pl {
class Evaluator; class Evaluator;
class PatternData; class PatternData;
class ASTNode;
class PatternLanguage { class PatternLanguage {
public: public:
PatternLanguage(); PatternLanguage();
@ -39,6 +41,8 @@ namespace hex::pl {
Validator *m_validator; Validator *m_validator;
Evaluator *m_evaluator; Evaluator *m_evaluator;
std::vector<ASTNode*> m_currAST;
prv::Provider *m_provider = nullptr; prv::Provider *m_provider = nullptr;
std::endian m_defaultEndian = std::endian::native; std::endian m_defaultEndian = std::endian::native;
u32 m_recursionLimit = 32; u32 m_recursionLimit = 32;

View File

@ -6,8 +6,12 @@
#include <string> #include <string>
#include <variant> #include <variant>
#include <hex/helpers/utils.hpp>
namespace hex::pl { namespace hex::pl {
class PatternData;
class Token { class Token {
public: public:
enum class Type : u64 { enum class Type : u64 {
@ -31,6 +35,7 @@ namespace hex::pl {
If, If,
Else, Else,
Parent, Parent,
This,
While, While,
Function, Function,
Return, Return,
@ -85,6 +90,7 @@ namespace hex::pl {
Boolean = 0x14, Boolean = 0x14,
Float = 0x42, Float = 0x42,
Double = 0x82, Double = 0x82,
String = 0x15,
CustomType = 0x00, CustomType = 0x00,
Padding = 0x1F, Padding = 0x1F,
@ -108,8 +114,21 @@ namespace hex::pl {
EndOfProgram EndOfProgram
}; };
using IntegerLiteral = std::variant<char, bool, u8, s8, u16, s16, u32, s32, u64, s64, u128, s128, float, double>; struct Identifier {
using ValueTypes = std::variant<Keyword, std::string, Operator, IntegerLiteral, ValueType, Separator>; explicit Identifier(std::string identifier) : m_identifier(std::move(identifier)) { }
[[nodiscard]]
const std::string &get() const { return this->m_identifier; }
auto operator<=>(const Identifier&) const = default;
bool operator==(const Identifier&) const = default;
private:
std::string m_identifier;
};
using Literal = std::variant<char, bool, u128, s128, double, std::string, PatternData*>;
using ValueTypes = std::variant<Keyword, Identifier, Operator, Literal, ValueType, Separator>;
Token(Type type, auto value, u32 lineNumber) : type(type), value(value), lineNumber(lineNumber) { Token(Type type, auto value, u32 lineNumber) : type(type), value(value), lineNumber(lineNumber) {
@ -131,6 +150,58 @@ namespace hex::pl {
return static_cast<u32>(type) >> 4; return static_cast<u32>(type) >> 4;
} }
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; }
},
literal);
}
static s128 literalToSigned(const pl::Token::Literal &literal) {
return std::visit(overloaded {
[](std::string) -> s128 { throw std::string("expected integral type, got string"); },
[](PatternData*) -> s128 { throw std::string("expected integral type, got custom type"); },
[](auto &&value) -> s128 { return value; }
},
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; }
},
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; }
},
literal);
}
static std::string literalToString(const pl::Token::Literal &literal, bool cast) {
if (!cast && std::get_if<std::string>(&literal) == nullptr)
throw std::string("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)); },
[](s128 value) -> std::string { return std::to_string(s64(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); }
},
literal);
}
[[nodiscard]] constexpr static auto getTypeName(const pl::Token::ValueType type) { [[nodiscard]] constexpr static auto getTypeName(const pl::Token::ValueType type) {
switch (type) { switch (type) {
case ValueType::Signed8Bit: return "s8"; case ValueType::Signed8Bit: return "s8";
@ -147,6 +218,7 @@ namespace hex::pl {
case ValueType::Double: return "double"; case ValueType::Double: return "double";
case ValueType::Character: return "char"; case ValueType::Character: return "char";
case ValueType::Character16: return "char16"; case ValueType::Character16: return "char16";
case ValueType::Padding: return "padding";
default: return "< ??? >"; default: return "< ??? >";
} }
} }
@ -204,14 +276,15 @@ namespace hex::pl {
#define KEYWORD_IF COMPONENT(Keyword, If) #define KEYWORD_IF COMPONENT(Keyword, If)
#define KEYWORD_ELSE COMPONENT(Keyword, Else) #define KEYWORD_ELSE COMPONENT(Keyword, Else)
#define KEYWORD_PARENT COMPONENT(Keyword, Parent) #define KEYWORD_PARENT COMPONENT(Keyword, Parent)
#define KEYWORD_THIS COMPONENT(Keyword, This)
#define KEYWORD_WHILE COMPONENT(Keyword, While) #define KEYWORD_WHILE COMPONENT(Keyword, While)
#define KEYWORD_FUNCTION COMPONENT(Keyword, Function) #define KEYWORD_FUNCTION COMPONENT(Keyword, Function)
#define KEYWORD_RETURN COMPONENT(Keyword, Return) #define KEYWORD_RETURN COMPONENT(Keyword, Return)
#define KEYWORD_NAMESPACE COMPONENT(Keyword, Namespace) #define KEYWORD_NAMESPACE COMPONENT(Keyword, Namespace)
#define INTEGER hex::pl::Token::Type::Integer, hex::pl::Token::IntegerLiteral(u64(0)) #define INTEGER hex::pl::Token::Type::Integer, hex::pl::Token::Literal(u128(0))
#define IDENTIFIER hex::pl::Token::Type::Identifier, "" #define IDENTIFIER hex::pl::Token::Type::Identifier, ""
#define STRING hex::pl::Token::Type::String, "" #define STRING hex::pl::Token::Type::String, hex::pl::Token::Literal("")
#define OPERATOR_AT COMPONENT(Operator, AtDeclaration) #define OPERATOR_AT COMPONENT(Operator, AtDeclaration)
#define OPERATOR_ASSIGNMENT COMPONENT(Operator, Assignment) #define OPERATOR_ASSIGNMENT COMPONENT(Operator, Assignment)

View File

@ -167,7 +167,7 @@ namespace hex {
/* Pattern Language Functions */ /* Pattern Language Functions */
void ContentRegistry::PatternLanguageFunctions::add(const Namespace &ns, const std::string &name, u32 parameterCount, const std::function<hex::pl::ASTNode*(hex::pl::Evaluator&, std::vector<hex::pl::ASTNode*>&)> &func) { void ContentRegistry::PatternLanguageFunctions::add(const Namespace &ns, const std::string &name, u32 parameterCount, const ContentRegistry::PatternLanguageFunctions::Callback &func) {
std::string functionName; std::string functionName;
for (auto &scope : ns) for (auto &scope : ns)
functionName += scope + "::"; functionName += scope + "::";

File diff suppressed because it is too large Load Diff

View File

@ -28,9 +28,9 @@ namespace hex::pl {
return string.find_first_not_of("0123456789ABCDEFabcdef.xUL"); return string.find_first_not_of("0123456789ABCDEFabcdef.xUL");
} }
std::optional<Token::IntegerLiteral> parseIntegerLiteral(const std::string &string) { std::optional<Token::Literal> parseIntegerLiteral(const std::string &string) {
Token::ValueType type = Token::ValueType::Any; Token::ValueType type = Token::ValueType::Any;
Token::IntegerLiteral result; Token::Literal result;
u8 base; u8 base;
@ -38,20 +38,8 @@ namespace hex::pl {
auto numberData = std::string_view(string).substr(0, endPos); auto numberData = std::string_view(string).substr(0, endPos);
if (numberData.ends_with('U')) { if (numberData.ends_with('U')) {
type = Token::ValueType::Unsigned32Bit;
numberData.remove_suffix(1);
} else if (numberData.ends_with("UL")) {
type = Token::ValueType::Unsigned64Bit;
numberData.remove_suffix(2);
} else if (numberData.ends_with("ULL")) {
type = Token::ValueType::Unsigned128Bit; type = Token::ValueType::Unsigned128Bit;
numberData.remove_suffix(3);
} else if (numberData.ends_with("L")) {
type = Token::ValueType::Signed64Bit;
numberData.remove_suffix(1); numberData.remove_suffix(1);
} else if (numberData.ends_with("LL")) {
type = Token::ValueType::Signed128Bit;
numberData.remove_suffix(2);
} else if (!numberData.starts_with("0x") && !numberData.starts_with("0b")) { } else if (!numberData.starts_with("0x") && !numberData.starts_with("0b")) {
if (numberData.ends_with('F')) { if (numberData.ends_with('F')) {
type = Token::ValueType::Float; type = Token::ValueType::Float;
@ -98,7 +86,7 @@ namespace hex::pl {
} else return { }; } else return { };
if (type == Token::ValueType::Any) if (type == Token::ValueType::Any)
type = Token::ValueType::Signed32Bit; type = Token::ValueType::Signed128Bit;
if (numberData.length() == 0) if (numberData.length() == 0)
@ -119,10 +107,6 @@ namespace hex::pl {
} }
switch (type) { switch (type) {
case Token::ValueType::Unsigned32Bit: return { u32(integer) };
case Token::ValueType::Signed32Bit: return { s32(integer) };
case Token::ValueType::Unsigned64Bit: return { u64(integer) };
case Token::ValueType::Signed64Bit: return { s64(integer) };
case Token::ValueType::Unsigned128Bit: return { u128(integer) }; case Token::ValueType::Unsigned128Bit: return { u128(integer) };
case Token::ValueType::Signed128Bit: return { s128(integer) }; case Token::ValueType::Signed128Bit: return { s128(integer) };
default: return { }; default: return { };
@ -379,7 +363,7 @@ namespace hex::pl {
auto [c, charSize] = character.value(); auto [c, charSize] = character.value();
tokens.emplace_back(VALUE_TOKEN(Integer, c)); tokens.emplace_back(VALUE_TOKEN(Integer, Token::Literal(c)));
offset += charSize; offset += charSize;
} else if (c == '\"') { } else if (c == '\"') {
auto string = getStringLiteral(code.substr(offset)); auto string = getStringLiteral(code.substr(offset));
@ -389,7 +373,7 @@ namespace hex::pl {
auto [s, stringSize] = string.value(); auto [s, stringSize] = string.value();
tokens.emplace_back(VALUE_TOKEN(String, s)); tokens.emplace_back(VALUE_TOKEN(String, Token::Literal(s)));
offset += stringSize; offset += stringSize;
} else if (std::isalpha(c)) { } else if (std::isalpha(c)) {
std::string identifier = matchTillInvalid(&code[offset], [](char c) -> bool { return std::isalnum(c) || c == '_'; }); std::string identifier = matchTillInvalid(&code[offset], [](char c) -> bool { return std::isalnum(c) || c == '_'; });
@ -415,11 +399,13 @@ namespace hex::pl {
else if (identifier == "else") else if (identifier == "else")
tokens.emplace_back(TOKEN(Keyword, Else)); tokens.emplace_back(TOKEN(Keyword, Else));
else if (identifier == "false") else if (identifier == "false")
tokens.emplace_back(VALUE_TOKEN(Integer, bool(0))); tokens.emplace_back(VALUE_TOKEN(Integer, Token::Literal(false)));
else if (identifier == "true") else if (identifier == "true")
tokens.emplace_back(VALUE_TOKEN(Integer, bool(1))); tokens.emplace_back(VALUE_TOKEN(Integer, Token::Literal(true)));
else if (identifier == "parent") else if (identifier == "parent")
tokens.emplace_back(TOKEN(Keyword, Parent)); tokens.emplace_back(TOKEN(Keyword, Parent));
else if (identifier == "this")
tokens.emplace_back(TOKEN(Keyword, This));
else if (identifier == "while") else if (identifier == "while")
tokens.emplace_back(TOKEN(Keyword, While)); tokens.emplace_back(TOKEN(Keyword, While));
else if (identifier == "fn") else if (identifier == "fn")
@ -460,13 +446,15 @@ namespace hex::pl {
tokens.emplace_back(TOKEN(ValueType, Character16)); tokens.emplace_back(TOKEN(ValueType, Character16));
else if (identifier == "bool") else if (identifier == "bool")
tokens.emplace_back(TOKEN(ValueType, Boolean)); tokens.emplace_back(TOKEN(ValueType, Boolean));
else if (identifier == "str")
tokens.emplace_back(TOKEN(ValueType, String));
else if (identifier == "padding") else if (identifier == "padding")
tokens.emplace_back(TOKEN(ValueType, Padding)); tokens.emplace_back(TOKEN(ValueType, Padding));
// If it's not a keyword and a builtin type, it has to be an identifier // If it's not a keyword and a builtin type, it has to be an identifier
else else
tokens.emplace_back(VALUE_TOKEN(Identifier, identifier)); tokens.emplace_back(VALUE_TOKEN(Identifier, Token::Identifier(identifier)));
offset += identifier.length(); offset += identifier.length();
} else if (std::isdigit(c)) { } else if (std::isdigit(c)) {
@ -476,7 +464,7 @@ namespace hex::pl {
throwLexerError("invalid integer literal", lineNumber); throwLexerError("invalid integer literal", lineNumber);
tokens.emplace_back(VALUE_TOKEN(Integer, integer.value())); tokens.emplace_back(VALUE_TOKEN(Integer, Token::Literal(integer.value())));
offset += getIntegerLiteralLength(&code[offset]); offset += getIntegerLiteralLength(&code[offset]);
} else } else
throwLexerError("unknown token", lineNumber); throwLexerError("unknown token", lineNumber);

View File

@ -1,13 +1,9 @@
#include <hex/pattern_language/parser.hpp> #include <hex/pattern_language/parser.hpp>
#include <hex/helpers/fmt.hpp>
#include <optional> #include <optional>
#define MATCHES(x) (begin() && x) #define MATCHES(x) (begin() && x)
#define TO_NUMERIC_EXPRESSION(node) new ASTNodeNumericExpression((node), new ASTNodeIntegerLiteral(s32(0)), Token::Operator::Plus)
// Definition syntax: // Definition syntax:
// [A] : Either A or no token // [A] : Either A or no token
// [A|B] : Either A, B or no token // [A|B] : Either A, B or no token
@ -33,10 +29,7 @@ namespace hex::pl {
}; };
while (!MATCHES(sequence(SEPARATOR_ROUNDBRACKETCLOSE))) { while (!MATCHES(sequence(SEPARATOR_ROUNDBRACKETCLOSE))) {
if (MATCHES(sequence(STRING))) params.push_back(parseMathematicalExpression());
params.push_back(parseStringLiteral());
else
params.push_back(parseMathematicalExpression());
if (MATCHES(sequence(SEPARATOR_COMMA, SEPARATOR_ROUNDBRACKETCLOSE))) if (MATCHES(sequence(SEPARATOR_COMMA, SEPARATOR_ROUNDBRACKETCLOSE)))
throwParseError("unexpected ',' at end of function parameter list", -1); throwParseError("unexpected ',' at end of function parameter list", -1);
@ -49,18 +42,18 @@ namespace hex::pl {
paramCleanup.release(); paramCleanup.release();
return new ASTNodeFunctionCall(functionName, params); return create(new ASTNodeFunctionCall(functionName, params));
} }
ASTNode* Parser::parseStringLiteral() { ASTNode* Parser::parseStringLiteral() {
return new ASTNodeStringLiteral(getValue<std::string>(-1)); return create(new ASTNodeLiteral(getValue<Token::Literal>(-1)));
} }
std::string Parser::parseNamespaceResolution() { std::string Parser::parseNamespaceResolution() {
std::string name; std::string name;
while (true) { while (true) {
name += getValue<std::string>(-1); name += getValue<Token::Identifier>(-1).get();
if (MATCHES(sequence(OPERATOR_SCOPERESOLUTION, IDENTIFIER))) { if (MATCHES(sequence(OPERATOR_SCOPERESOLUTION, IDENTIFIER))) {
name += "::"; name += "::";
@ -77,14 +70,14 @@ namespace hex::pl {
std::string typeName; std::string typeName;
while (true) { while (true) {
typeName += getValue<std::string>(-1); typeName += getValue<Token::Identifier>(-1).get();
if (MATCHES(sequence(OPERATOR_SCOPERESOLUTION, IDENTIFIER))) { if (MATCHES(sequence(OPERATOR_SCOPERESOLUTION, IDENTIFIER))) {
if (peek(OPERATOR_SCOPERESOLUTION, 0) && peek(IDENTIFIER, 1)) { if (peek(OPERATOR_SCOPERESOLUTION, 0) && peek(IDENTIFIER, 1)) {
typeName += "::"; typeName += "::";
continue; continue;
} else { } else {
return new ASTNodeScopeResolution({ typeName, getValue<std::string>(-1) }); return create(new ASTNodeScopeResolution({ typeName, getValue<Token::Identifier>(-1).get() }));
} }
} }
else else
@ -97,9 +90,11 @@ namespace hex::pl {
// <Identifier[.]...> // <Identifier[.]...>
ASTNode* Parser::parseRValue(ASTNodeRValue::Path &path) { ASTNode* Parser::parseRValue(ASTNodeRValue::Path &path) {
if (peek(IDENTIFIER, -1)) if (peek(IDENTIFIER, -1))
path.push_back(getValue<std::string>(-1)); path.push_back(getValue<Token::Identifier>(-1).get());
else if (peek(KEYWORD_PARENT, -1)) else if (peek(KEYWORD_PARENT, -1))
path.emplace_back("parent"); path.emplace_back("parent");
else if (peek(KEYWORD_THIS, -1))
path.emplace_back("this");
if (MATCHES(sequence(SEPARATOR_SQUAREBRACKETOPEN))) { if (MATCHES(sequence(SEPARATOR_SQUAREBRACKETOPEN))) {
path.push_back(parseMathematicalExpression()); path.push_back(parseMathematicalExpression());
@ -113,13 +108,13 @@ namespace hex::pl {
else else
throwParseError("expected member name or 'parent' keyword", -1); throwParseError("expected member name or 'parent' keyword", -1);
} else } else
return new ASTNodeRValue(path); return create(new ASTNodeRValue(path));
} }
// <Integer|((parseMathematicalExpression))> // <Integer|((parseMathematicalExpression))>
ASTNode* Parser::parseFactor() { ASTNode* Parser::parseFactor() {
if (MATCHES(sequence(INTEGER))) if (MATCHES(sequence(INTEGER)))
return TO_NUMERIC_EXPRESSION(new ASTNodeIntegerLiteral(getValue<Token::IntegerLiteral>(-1))); return new ASTNodeLiteral(getValue<Token::Literal>(-1));
else if (MATCHES(sequence(SEPARATOR_ROUNDBRACKETOPEN))) { else if (MATCHES(sequence(SEPARATOR_ROUNDBRACKETOPEN))) {
auto node = this->parseMathematicalExpression(); auto node = this->parseMathematicalExpression();
if (!MATCHES(sequence(SEPARATOR_ROUNDBRACKETCLOSE))) { if (!MATCHES(sequence(SEPARATOR_ROUNDBRACKETCLOSE))) {
@ -135,34 +130,54 @@ namespace hex::pl {
if (isFunction) { if (isFunction) {
return TO_NUMERIC_EXPRESSION(this->parseFunctionCall()); return this->parseFunctionCall();
} else if (peek(OPERATOR_SCOPERESOLUTION, 0)) { } else if (peek(OPERATOR_SCOPERESOLUTION, 0)) {
return TO_NUMERIC_EXPRESSION(this->parseScopeResolution()); return this->parseScopeResolution();
} else { } else {
ASTNodeRValue::Path path; ASTNodeRValue::Path path;
return TO_NUMERIC_EXPRESSION(this->parseRValue(path)); return this->parseRValue(path);
} }
} else if (MATCHES(oneOf(KEYWORD_PARENT))) { } else if (MATCHES(oneOf(KEYWORD_PARENT, KEYWORD_THIS))) {
ASTNodeRValue::Path path; ASTNodeRValue::Path path;
return TO_NUMERIC_EXPRESSION(this->parseRValue(path)); return this->parseRValue(path);
} else if (MATCHES(sequence(OPERATOR_DOLLAR))) { } else if (MATCHES(sequence(OPERATOR_DOLLAR))) {
return TO_NUMERIC_EXPRESSION(new ASTNodeRValue({ "$" })); return new ASTNodeRValue({ "$" });
} else if (MATCHES(oneOf(OPERATOR_ADDRESSOF, OPERATOR_SIZEOF) && sequence(SEPARATOR_ROUNDBRACKETOPEN))) { } else if (MATCHES(oneOf(OPERATOR_ADDRESSOF, OPERATOR_SIZEOF) && sequence(SEPARATOR_ROUNDBRACKETOPEN))) {
auto op = getValue<Token::Operator>(-2); auto op = getValue<Token::Operator>(-2);
if (!MATCHES(oneOf(IDENTIFIER, KEYWORD_PARENT))) { if (!MATCHES(oneOf(IDENTIFIER, KEYWORD_PARENT, KEYWORD_THIS))) {
throwParseError("expected rvalue identifier"); throwParseError("expected rvalue identifier");
} }
ASTNodeRValue::Path path; ASTNodeRValue::Path path;
auto node = new ASTNodeTypeOperator(op, this->parseRValue(path)); auto node = create(new ASTNodeTypeOperator(op, this->parseRValue(path)));
if (!MATCHES(sequence(SEPARATOR_ROUNDBRACKETCLOSE))) { if (!MATCHES(sequence(SEPARATOR_ROUNDBRACKETCLOSE))) {
delete node; delete node;
throwParseError("expected closing parenthesis"); throwParseError("expected closing parenthesis");
} }
return TO_NUMERIC_EXPRESSION(node); return node;
} else } else
throwParseError("expected integer or parenthesis"); throwParseError("expected value or parenthesis");
}
ASTNode* Parser::parseCastExpression() {
if (peek(KEYWORD_BE) || peek(KEYWORD_LE) || peek(VALUETYPE_ANY)) {
auto type = parseType();
auto builtinType = dynamic_cast<ASTNodeBuiltinType*>(type->getType());
if (builtinType == nullptr)
throwParseError("invalid type used for pointer size", -1);
if (!MATCHES(sequence(SEPARATOR_ROUNDBRACKETOPEN)))
throwParseError("expected '(' before cast expression", -1);
auto node = parseFactor();
if (!MATCHES(sequence(SEPARATOR_ROUNDBRACKETCLOSE)))
throwParseError("expected ')' after cast expression", -1);
return new ASTNodeCast(node, type);
} else return parseFactor();
} }
// <+|-|!|~> (parseFactor) // <+|-|!|~> (parseFactor)
@ -170,10 +185,12 @@ namespace hex::pl {
if (MATCHES(oneOf(OPERATOR_PLUS, OPERATOR_MINUS, OPERATOR_BOOLNOT, OPERATOR_BITNOT))) { if (MATCHES(oneOf(OPERATOR_PLUS, OPERATOR_MINUS, OPERATOR_BOOLNOT, OPERATOR_BITNOT))) {
auto op = getValue<Token::Operator>(-1); auto op = getValue<Token::Operator>(-1);
return new ASTNodeNumericExpression(new ASTNodeIntegerLiteral(0), this->parseFactor(), op); return create(new ASTNodeMathematicalExpression(new ASTNodeLiteral(0), this->parseCastExpression(), op));
} else if (MATCHES(sequence(STRING))) {
return this->parseStringLiteral();
} }
return this->parseFactor(); return this->parseCastExpression();
} }
// (parseUnaryExpression) <*|/|%> (parseUnaryExpression) // (parseUnaryExpression) <*|/|%> (parseUnaryExpression)
@ -184,7 +201,7 @@ namespace hex::pl {
while (MATCHES(oneOf(OPERATOR_STAR, OPERATOR_SLASH, OPERATOR_PERCENT))) { while (MATCHES(oneOf(OPERATOR_STAR, OPERATOR_SLASH, OPERATOR_PERCENT))) {
auto op = getValue<Token::Operator>(-1); auto op = getValue<Token::Operator>(-1);
node = new ASTNodeNumericExpression(node, this->parseUnaryExpression(), op); node = create(new ASTNodeMathematicalExpression(node, this->parseUnaryExpression(), op));
} }
nodeCleanup.release(); nodeCleanup.release();
@ -200,7 +217,7 @@ namespace hex::pl {
while (MATCHES(variant(OPERATOR_PLUS, OPERATOR_MINUS))) { while (MATCHES(variant(OPERATOR_PLUS, OPERATOR_MINUS))) {
auto op = getValue<Token::Operator>(-1); auto op = getValue<Token::Operator>(-1);
node = new ASTNodeNumericExpression(node, this->parseMultiplicativeExpression(), op); node = create(new ASTNodeMathematicalExpression(node, this->parseMultiplicativeExpression(), op));
} }
nodeCleanup.release(); nodeCleanup.release();
@ -216,7 +233,7 @@ namespace hex::pl {
while (MATCHES(variant(OPERATOR_SHIFTLEFT, OPERATOR_SHIFTRIGHT))) { while (MATCHES(variant(OPERATOR_SHIFTLEFT, OPERATOR_SHIFTRIGHT))) {
auto op = getValue<Token::Operator>(-1); auto op = getValue<Token::Operator>(-1);
node = new ASTNodeNumericExpression(node, this->parseAdditiveExpression(), op); node = create(new ASTNodeMathematicalExpression(node, this->parseAdditiveExpression(), op));
} }
nodeCleanup.release(); nodeCleanup.release();
@ -232,7 +249,7 @@ namespace hex::pl {
while (MATCHES(sequence(OPERATOR_BOOLGREATERTHAN) || sequence(OPERATOR_BOOLLESSTHAN) || sequence(OPERATOR_BOOLGREATERTHANOREQUALS) || sequence(OPERATOR_BOOLLESSTHANOREQUALS))) { while (MATCHES(sequence(OPERATOR_BOOLGREATERTHAN) || sequence(OPERATOR_BOOLLESSTHAN) || sequence(OPERATOR_BOOLGREATERTHANOREQUALS) || sequence(OPERATOR_BOOLLESSTHANOREQUALS))) {
auto op = getValue<Token::Operator>(-1); auto op = getValue<Token::Operator>(-1);
node = new ASTNodeNumericExpression(node, this->parseShiftExpression(), op); node = create(new ASTNodeMathematicalExpression(node, this->parseShiftExpression(), op));
} }
nodeCleanup.release(); nodeCleanup.release();
@ -248,7 +265,7 @@ namespace hex::pl {
while (MATCHES(sequence(OPERATOR_BOOLEQUALS) || sequence(OPERATOR_BOOLNOTEQUALS))) { while (MATCHES(sequence(OPERATOR_BOOLEQUALS) || sequence(OPERATOR_BOOLNOTEQUALS))) {
auto op = getValue<Token::Operator>(-1); auto op = getValue<Token::Operator>(-1);
node = new ASTNodeNumericExpression(node, this->parseRelationExpression(), op); node = create(new ASTNodeMathematicalExpression(node, this->parseRelationExpression(), op));
} }
nodeCleanup.release(); nodeCleanup.release();
@ -263,7 +280,7 @@ namespace hex::pl {
auto nodeCleanup = SCOPE_GUARD { delete node; }; auto nodeCleanup = SCOPE_GUARD { delete node; };
while (MATCHES(sequence(OPERATOR_BITAND))) { while (MATCHES(sequence(OPERATOR_BITAND))) {
node = new ASTNodeNumericExpression(node, this->parseEqualityExpression(), Token::Operator::BitAnd); node = create(new ASTNodeMathematicalExpression(node, this->parseEqualityExpression(), Token::Operator::BitAnd));
} }
nodeCleanup.release(); nodeCleanup.release();
@ -278,7 +295,7 @@ namespace hex::pl {
auto nodeCleanup = SCOPE_GUARD { delete node; }; auto nodeCleanup = SCOPE_GUARD { delete node; };
while (MATCHES(sequence(OPERATOR_BITXOR))) { while (MATCHES(sequence(OPERATOR_BITXOR))) {
node = new ASTNodeNumericExpression(node, this->parseBinaryAndExpression(), Token::Operator::BitXor); node = create(new ASTNodeMathematicalExpression(node, this->parseBinaryAndExpression(), Token::Operator::BitXor));
} }
nodeCleanup.release(); nodeCleanup.release();
@ -293,7 +310,7 @@ namespace hex::pl {
auto nodeCleanup = SCOPE_GUARD { delete node; }; auto nodeCleanup = SCOPE_GUARD { delete node; };
while (MATCHES(sequence(OPERATOR_BITOR))) { while (MATCHES(sequence(OPERATOR_BITOR))) {
node = new ASTNodeNumericExpression(node, this->parseBinaryXorExpression(), Token::Operator::BitOr); node = create(new ASTNodeMathematicalExpression(node, this->parseBinaryXorExpression(), Token::Operator::BitOr));
} }
nodeCleanup.release(); nodeCleanup.release();
@ -308,7 +325,7 @@ namespace hex::pl {
auto nodeCleanup = SCOPE_GUARD { delete node; }; auto nodeCleanup = SCOPE_GUARD { delete node; };
while (MATCHES(sequence(OPERATOR_BOOLAND))) { while (MATCHES(sequence(OPERATOR_BOOLAND))) {
node = new ASTNodeNumericExpression(node, this->parseBinaryOrExpression(), Token::Operator::BitOr); node = create(new ASTNodeMathematicalExpression(node, this->parseBinaryOrExpression(), Token::Operator::BitOr));
} }
nodeCleanup.release(); nodeCleanup.release();
@ -323,7 +340,7 @@ namespace hex::pl {
auto nodeCleanup = SCOPE_GUARD { delete node; }; auto nodeCleanup = SCOPE_GUARD { delete node; };
while (MATCHES(sequence(OPERATOR_BOOLXOR))) { while (MATCHES(sequence(OPERATOR_BOOLXOR))) {
node = new ASTNodeNumericExpression(node, this->parseBooleanAnd(), Token::Operator::BitOr); node = create(new ASTNodeMathematicalExpression(node, this->parseBooleanAnd(), Token::Operator::BitOr));
} }
nodeCleanup.release(); nodeCleanup.release();
@ -338,7 +355,7 @@ namespace hex::pl {
auto nodeCleanup = SCOPE_GUARD { delete node; }; auto nodeCleanup = SCOPE_GUARD { delete node; };
while (MATCHES(sequence(OPERATOR_BOOLOR))) { while (MATCHES(sequence(OPERATOR_BOOLOR))) {
node = new ASTNodeNumericExpression(node, this->parseBooleanXor(), Token::Operator::BitOr); node = create(new ASTNodeMathematicalExpression(node, this->parseBooleanXor(), Token::Operator::BitOr));
} }
nodeCleanup.release(); nodeCleanup.release();
@ -359,7 +376,7 @@ namespace hex::pl {
throwParseError("expected ':' in ternary expression"); throwParseError("expected ':' in ternary expression");
auto third = this->parseBooleanOr(); auto third = this->parseBooleanOr();
node = TO_NUMERIC_EXPRESSION(new ASTNodeTernaryExpression(node, second, third, Token::Operator::TernaryConditional)); node = create(new ASTNodeTernaryExpression(node, second, third, Token::Operator::TernaryConditional));
} }
nodeCleanup.release(); nodeCleanup.release();
@ -381,14 +398,19 @@ namespace hex::pl {
if (!MATCHES(sequence(IDENTIFIER))) if (!MATCHES(sequence(IDENTIFIER)))
throwParseError("expected attribute expression"); throwParseError("expected attribute expression");
auto attribute = this->getValue<std::string>(-1); auto attribute = getValue<Token::Identifier>(-1).get();
if (MATCHES(sequence(SEPARATOR_ROUNDBRACKETOPEN, STRING, SEPARATOR_ROUNDBRACKETCLOSE))) { if (MATCHES(sequence(SEPARATOR_ROUNDBRACKETOPEN, STRING, SEPARATOR_ROUNDBRACKETCLOSE))) {
auto value = this->getValue<std::string>(-2); auto value = getValue<Token::Literal>(-2);
currNode->addAttribute(new ASTNodeAttribute(attribute, value)); auto string = std::get_if<std::string>(&value);
if (string == nullptr)
throwParseError("expected string attribute argument");
currNode->addAttribute(create(new ASTNodeAttribute(attribute, *string)));
} }
else else
currNode->addAttribute(new ASTNodeAttribute(attribute)); currNode->addAttribute(create(new ASTNodeAttribute(attribute)));
} while (MATCHES(sequence(SEPARATOR_COMMA))); } while (MATCHES(sequence(SEPARATOR_COMMA)));
@ -398,16 +420,24 @@ namespace hex::pl {
/* Functions */ /* Functions */
ASTNode* Parser::parseFunctionDefintion() { ASTNode* Parser::parseFunctionDefinition() {
const auto &functionName = getValue<std::string>(-2); const auto &functionName = getValue<Token::Identifier>(-2).get();
std::vector<std::string> params; std::map<std::string, ASTNode*> params;
// Parse parameter list // Parse parameter list
bool hasParams = MATCHES(sequence(IDENTIFIER)); bool hasParams = !peek(SEPARATOR_ROUNDBRACKETCLOSE);
u32 unnamedParamCount = 0;
while (hasParams) { while (hasParams) {
params.push_back(getValue<std::string>(-1)); auto type = parseType(true);
if (!MATCHES(sequence(SEPARATOR_COMMA, IDENTIFIER))) { if (MATCHES(sequence(IDENTIFIER)))
params.emplace(getValue<Token::Identifier>(-1).get(), type);
else {
params.emplace(std::to_string(unnamedParamCount), type);
unnamedParamCount++;
}
if (!MATCHES(sequence(SEPARATOR_COMMA))) {
if (MATCHES(sequence(SEPARATOR_ROUNDBRACKETCLOSE))) if (MATCHES(sequence(SEPARATOR_ROUNDBRACKETCLOSE)))
break; break;
else else
@ -435,7 +465,7 @@ namespace hex::pl {
} }
bodyCleanup.release(); bodyCleanup.release();
return new ASTNodeFunctionDefinition(getNamespacePrefixedName(functionName), params, body); return create(new ASTNodeFunctionDefinition(getNamespacePrefixedName(functionName), params, body));
} }
ASTNode* Parser::parseFunctionStatement() { ASTNode* Parser::parseFunctionStatement() {
@ -487,18 +517,18 @@ namespace hex::pl {
} }
ASTNode* Parser::parseFunctionVariableAssignment() { ASTNode* Parser::parseFunctionVariableAssignment() {
const auto &lvalue = getValue<std::string>(-2); const auto &lvalue = getValue<Token::Identifier>(-2).get();
auto rvalue = this->parseMathematicalExpression(); auto rvalue = this->parseMathematicalExpression();
return new ASTNodeAssignment(lvalue, rvalue); return create(new ASTNodeAssignment(lvalue, rvalue));
} }
ASTNode* Parser::parseFunctionReturnStatement() { ASTNode* Parser::parseFunctionReturnStatement() {
if (peek(SEPARATOR_ENDOFEXPRESSION)) if (peek(SEPARATOR_ENDOFEXPRESSION))
return new ASTNodeReturnStatement(nullptr); return create(new ASTNodeReturnStatement(nullptr));
else else
return new ASTNodeReturnStatement(this->parseMathematicalExpression()); return create(new ASTNodeReturnStatement(this->parseMathematicalExpression()));
} }
ASTNode* Parser::parseFunctionConditional() { ASTNode* Parser::parseFunctionConditional() {
@ -532,7 +562,7 @@ namespace hex::pl {
cleanup.release(); cleanup.release();
return new ASTNodeConditionalStatement(condition, trueBody, falseBody); return create(new ASTNodeConditionalStatement(condition, trueBody, falseBody));
} }
ASTNode* Parser::parseFunctionWhileLoop() { ASTNode* Parser::parseFunctionWhileLoop() {
@ -556,7 +586,7 @@ namespace hex::pl {
cleanup.release(); cleanup.release();
return new ASTNodeWhileStatement(condition, body); return create(new ASTNodeWhileStatement(condition, body));
} }
/* Control flow */ /* Control flow */
@ -593,7 +623,7 @@ namespace hex::pl {
cleanup.release(); cleanup.release();
return new ASTNodeConditionalStatement(condition, trueBody, falseBody); return create(new ASTNodeConditionalStatement(condition, trueBody, falseBody));
} }
// while ((parseMathematicalExpression)) // while ((parseMathematicalExpression))
@ -609,13 +639,13 @@ namespace hex::pl {
cleanup.release(); cleanup.release();
return new ASTNodeWhileStatement(condition, { }); return create(new ASTNodeWhileStatement(condition, { }));
} }
/* Type declarations */ /* Type declarations */
// [be|le] <Identifier|u8|u16|u32|u64|u128|s8|s16|s32|s64|s128|float|double> // [be|le] <Identifier|u8|u16|u32|u64|u128|s8|s16|s32|s64|s128|float|double|str>
ASTNodeTypeDecl* Parser::parseType() { ASTNodeTypeDecl* Parser::parseType(bool allowString) {
std::optional<std::endian> endian; std::optional<std::endian> endian;
if (MATCHES(sequence(KEYWORD_LE))) if (MATCHES(sequence(KEYWORD_LE)))
@ -627,24 +657,28 @@ namespace hex::pl {
std::string typeName = parseNamespaceResolution(); std::string typeName = parseNamespaceResolution();
if (this->m_types.contains(typeName)) if (this->m_types.contains(typeName))
return new ASTNodeTypeDecl({ }, this->m_types[typeName]->clone(), endian); return create(new ASTNodeTypeDecl({ }, this->m_types[typeName]->clone(), endian));
else if (this->m_types.contains(getNamespacePrefixedName(typeName))) else if (this->m_types.contains(getNamespacePrefixedName(typeName)))
return new ASTNodeTypeDecl({ }, this->m_types[getNamespacePrefixedName(typeName)]->clone(), endian); return create(new ASTNodeTypeDecl({ }, this->m_types[getNamespacePrefixedName(typeName)]->clone(), endian));
else else
throwParseError(hex::format("unknown type '{}'", typeName)); throwParseError(hex::format("unknown type '{}'", typeName));
} }
else if (MATCHES(sequence(VALUETYPE_ANY))) { // Builtin type else if (MATCHES(sequence(VALUETYPE_ANY))) { // Builtin type
return new ASTNodeTypeDecl({ }, new ASTNodeBuiltinType(getValue<Token::ValueType>(-1)), endian); auto type = getValue<Token::ValueType>(-1);
if (!allowString && type == Token::ValueType::String)
throwParseError("cannot use 'str' in this context. Use a character array instead");
return create(new ASTNodeTypeDecl({ }, new ASTNodeBuiltinType(type), endian));
} else throwParseError("failed to parse type. Expected identifier or builtin type"); } else throwParseError("failed to parse type. Expected identifier or builtin type");
} }
// using Identifier = (parseType) // using Identifier = (parseType)
ASTNode* Parser::parseUsingDeclaration() { ASTNode* Parser::parseUsingDeclaration() {
auto name = getValue<std::string>(-2); auto name = getValue<Token::Identifier>(-2).get();
auto *type = dynamic_cast<ASTNodeTypeDecl *>(parseType()); auto *type = dynamic_cast<ASTNodeTypeDecl *>(parseType());
if (type == nullptr) throwParseError("invalid type used in variable declaration", -1); if (type == nullptr) throwParseError("invalid type used in variable declaration", -1);
return new ASTNodeTypeDecl(name, type, type->getEndian()); return create(new ASTNodeTypeDecl(name, type, type->getEndian()));
} }
// padding[(parseMathematicalExpression)] // padding[(parseMathematicalExpression)]
@ -656,7 +690,7 @@ namespace hex::pl {
throwParseError("expected closing ']' at end of array declaration", -1); throwParseError("expected closing ']' at end of array declaration", -1);
} }
return new ASTNodeArrayVariableDecl({ }, new ASTNodeTypeDecl({ }, new ASTNodeBuiltinType(Token::ValueType::Padding)), size);; return create(new ASTNodeArrayVariableDecl({ }, new ASTNodeTypeDecl({ }, new ASTNodeBuiltinType(Token::ValueType::Padding)), size));
} }
// (parseType) Identifier // (parseType) Identifier
@ -667,19 +701,19 @@ namespace hex::pl {
auto variableCleanup = SCOPE_GUARD { for (auto var : variables) delete var; }; auto variableCleanup = SCOPE_GUARD { for (auto var : variables) delete var; };
do { do {
variables.push_back(new ASTNodeVariableDecl(getValue<std::string>(-1), type->clone())); variables.push_back(create(new ASTNodeVariableDecl(getValue<Token::Identifier>(-1).get(), type->clone())));
} while (MATCHES(sequence(SEPARATOR_COMMA, IDENTIFIER))); } while (MATCHES(sequence(SEPARATOR_COMMA, IDENTIFIER)));
variableCleanup.release(); variableCleanup.release();
return new ASTNodeMultiVariableDecl(variables); return create(new ASTNodeMultiVariableDecl(variables));
} else } else
return new ASTNodeVariableDecl(getValue<std::string>(-1), type->clone()); return create(new ASTNodeVariableDecl(getValue<Token::Identifier>(-1).get(), type->clone()));
} }
// (parseType) Identifier[(parseMathematicalExpression)] // (parseType) Identifier[(parseMathematicalExpression)]
ASTNode* Parser::parseMemberArrayVariable(ASTNodeTypeDecl *type) { ASTNode* Parser::parseMemberArrayVariable(ASTNodeTypeDecl *type) {
auto name = getValue<std::string>(-2); auto name = getValue<Token::Identifier>(-2).get();
ASTNode *size = nullptr; ASTNode *size = nullptr;
auto sizeCleanup = SCOPE_GUARD { delete size; }; auto sizeCleanup = SCOPE_GUARD { delete size; };
@ -696,12 +730,12 @@ namespace hex::pl {
sizeCleanup.release(); sizeCleanup.release();
return new ASTNodeArrayVariableDecl(name, type->clone(), size); return create(new ASTNodeArrayVariableDecl(name, type->clone(), size));
} }
// (parseType) *Identifier : (parseType) // (parseType) *Identifier : (parseType)
ASTNode* Parser::parseMemberPointerVariable(ASTNodeTypeDecl *type) { ASTNode* Parser::parseMemberPointerVariable(ASTNodeTypeDecl *type) {
auto name = getValue<std::string>(-2); auto name = getValue<Token::Identifier>(-2).get();
auto sizeType = parseType(); auto sizeType = parseType();
@ -712,7 +746,7 @@ namespace hex::pl {
throwParseError("invalid type used for pointer size", -1); throwParseError("invalid type used for pointer size", -1);
} }
return new ASTNodePointerVariableDecl(name, type->clone(), sizeType); return create(new ASTNodePointerVariableDecl(name, type->clone(), sizeType));
} }
// [(parsePadding)|(parseMemberVariable)|(parseMemberArrayVariable)|(parseMemberPointerVariable)] // [(parsePadding)|(parseMemberVariable)|(parseMemberArrayVariable)|(parseMemberPointerVariable)]
@ -758,8 +792,8 @@ namespace hex::pl {
// struct Identifier { <(parseMember)...> } // struct Identifier { <(parseMember)...> }
ASTNode* Parser::parseStruct() { ASTNode* Parser::parseStruct() {
const auto structNode = new ASTNodeStruct(); const auto structNode = create(new ASTNodeStruct());
const auto &typeName = getValue<std::string>(-2); const auto &typeName = getValue<Token::Identifier>(-2).get();
auto structGuard = SCOPE_GUARD { delete structNode; }; auto structGuard = SCOPE_GUARD { delete structNode; };
while (!MATCHES(sequence(SEPARATOR_CURLYBRACKETCLOSE))) { while (!MATCHES(sequence(SEPARATOR_CURLYBRACKETCLOSE))) {
@ -768,13 +802,13 @@ namespace hex::pl {
structGuard.release(); structGuard.release();
return new ASTNodeTypeDecl(typeName, structNode); return create(new ASTNodeTypeDecl(typeName, structNode));
} }
// union Identifier { <(parseMember)...> } // union Identifier { <(parseMember)...> }
ASTNode* Parser::parseUnion() { ASTNode* Parser::parseUnion() {
const auto unionNode = new ASTNodeUnion(); const auto unionNode = create(new ASTNodeUnion());
const auto &typeName = getValue<std::string>(-2); const auto &typeName = getValue<Token::Identifier>(-2).get();
auto unionGuard = SCOPE_GUARD { delete unionNode; }; auto unionGuard = SCOPE_GUARD { delete unionNode; };
while (!MATCHES(sequence(SEPARATOR_CURLYBRACKETCLOSE))) { while (!MATCHES(sequence(SEPARATOR_CURLYBRACKETCLOSE))) {
@ -783,17 +817,17 @@ namespace hex::pl {
unionGuard.release(); unionGuard.release();
return new ASTNodeTypeDecl(typeName, unionNode); return create(new ASTNodeTypeDecl(typeName, unionNode));
} }
// enum Identifier : (parseType) { <<Identifier|Identifier = (parseMathematicalExpression)[,]>...> } // enum Identifier : (parseType) { <<Identifier|Identifier = (parseMathematicalExpression)[,]>...> }
ASTNode* Parser::parseEnum() { ASTNode* Parser::parseEnum() {
auto typeName = getValue<std::string>(-2); auto typeName = getValue<Token::Identifier>(-2).get();
auto underlyingType = parseType(); auto underlyingType = parseType();
if (underlyingType->getEndian().has_value()) throwParseError("underlying type may not have an endian specification", -2); if (underlyingType->getEndian().has_value()) throwParseError("underlying type may not have an endian specification", -2);
const auto enumNode = new ASTNodeEnum(underlyingType); const auto enumNode = create(new ASTNodeEnum(underlyingType));
auto enumGuard = SCOPE_GUARD { delete enumNode; }; auto enumGuard = SCOPE_GUARD { delete enumNode; };
if (!MATCHES(sequence(SEPARATOR_CURLYBRACKETOPEN))) if (!MATCHES(sequence(SEPARATOR_CURLYBRACKETOPEN)))
@ -802,7 +836,7 @@ namespace hex::pl {
ASTNode *lastEntry = nullptr; ASTNode *lastEntry = nullptr;
while (!MATCHES(sequence(SEPARATOR_CURLYBRACKETCLOSE))) { while (!MATCHES(sequence(SEPARATOR_CURLYBRACKETCLOSE))) {
if (MATCHES(sequence(IDENTIFIER, OPERATOR_ASSIGNMENT))) { if (MATCHES(sequence(IDENTIFIER, OPERATOR_ASSIGNMENT))) {
auto name = getValue<std::string>(-2); auto name = getValue<Token::Identifier>(-2).get();
auto value = parseMathematicalExpression(); auto value = parseMathematicalExpression();
enumNode->addEntry(name, value); enumNode->addEntry(name, value);
@ -810,11 +844,11 @@ namespace hex::pl {
} }
else if (MATCHES(sequence(IDENTIFIER))) { else if (MATCHES(sequence(IDENTIFIER))) {
ASTNode *valueExpr; ASTNode *valueExpr;
auto name = getValue<std::string>(-1); auto name = getValue<Token::Identifier>(-1).get();
if (enumNode->getEntries().empty()) if (enumNode->getEntries().empty())
valueExpr = lastEntry = TO_NUMERIC_EXPRESSION(new ASTNodeIntegerLiteral(u8(0))); valueExpr = lastEntry = create(new ASTNodeLiteral(u128(0)));
else else
valueExpr = lastEntry = new ASTNodeNumericExpression(lastEntry->clone(), new ASTNodeIntegerLiteral(s32(1)), Token::Operator::Plus); valueExpr = lastEntry = create(new ASTNodeMathematicalExpression(lastEntry->clone(), new ASTNodeLiteral(u128(1)), Token::Operator::Plus));
enumNode->addEntry(name, valueExpr); enumNode->addEntry(name, valueExpr);
} }
@ -833,19 +867,19 @@ namespace hex::pl {
enumGuard.release(); enumGuard.release();
return new ASTNodeTypeDecl(typeName, enumNode); return create(new ASTNodeTypeDecl(typeName, enumNode));
} }
// bitfield Identifier { <Identifier : (parseMathematicalExpression)[;]...> } // bitfield Identifier { <Identifier : (parseMathematicalExpression)[;]...> }
ASTNode* Parser::parseBitfield() { ASTNode* Parser::parseBitfield() {
std::string typeName = getValue<std::string>(-2); std::string typeName = getValue<Token::Identifier>(-2).get();
const auto bitfieldNode = new ASTNodeBitfield(); const auto bitfieldNode = create(new ASTNodeBitfield());
auto enumGuard = SCOPE_GUARD { delete bitfieldNode; }; auto enumGuard = SCOPE_GUARD { delete bitfieldNode; };
while (!MATCHES(sequence(SEPARATOR_CURLYBRACKETCLOSE))) { while (!MATCHES(sequence(SEPARATOR_CURLYBRACKETCLOSE))) {
if (MATCHES(sequence(IDENTIFIER, OPERATOR_INHERIT))) { if (MATCHES(sequence(IDENTIFIER, OPERATOR_INHERIT))) {
auto name = getValue<std::string>(-2); auto name = getValue<Token::Identifier>(-2).get();
bitfieldNode->addEntry(name, parseMathematicalExpression()); bitfieldNode->addEntry(name, parseMathematicalExpression());
} }
else if (MATCHES(sequence(SEPARATOR_ENDOFPROGRAM))) else if (MATCHES(sequence(SEPARATOR_ENDOFPROGRAM)))
@ -862,24 +896,24 @@ namespace hex::pl {
enumGuard.release(); enumGuard.release();
return new ASTNodeTypeDecl(typeName, bitfieldNode); return create(new ASTNodeTypeDecl(typeName, bitfieldNode));
} }
// (parseType) Identifier @ Integer // (parseType) Identifier @ Integer
ASTNode* Parser::parseVariablePlacement(ASTNodeTypeDecl *type) { ASTNode* Parser::parseVariablePlacement(ASTNodeTypeDecl *type) {
auto name = getValue<std::string>(-1); auto name = getValue<Token::Identifier>(-1).get();
if (!MATCHES(sequence(OPERATOR_AT))) if (!MATCHES(sequence(OPERATOR_AT)))
throwParseError("expected placement instruction", -1); throwParseError("expected placement instruction", -1);
auto placementOffset = parseMathematicalExpression(); auto placementOffset = parseMathematicalExpression();
return new ASTNodeVariableDecl(name, type, placementOffset); return create(new ASTNodeVariableDecl(name, type, placementOffset));
} }
// (parseType) Identifier[[(parseMathematicalExpression)]] @ Integer // (parseType) Identifier[[(parseMathematicalExpression)]] @ Integer
ASTNode* Parser::parseArrayVariablePlacement(ASTNodeTypeDecl *type) { ASTNode* Parser::parseArrayVariablePlacement(ASTNodeTypeDecl *type) {
auto name = getValue<std::string>(-2); auto name = getValue<Token::Identifier>(-2).get();
ASTNode *size = nullptr; ASTNode *size = nullptr;
auto sizeCleanup = SCOPE_GUARD { delete size; }; auto sizeCleanup = SCOPE_GUARD { delete size; };
@ -901,12 +935,12 @@ namespace hex::pl {
sizeCleanup.release(); sizeCleanup.release();
return new ASTNodeArrayVariableDecl(name, type, size, placementOffset); return create(new ASTNodeArrayVariableDecl(name, type, size, placementOffset));
} }
// (parseType) *Identifier : (parseType) @ Integer // (parseType) *Identifier : (parseType) @ Integer
ASTNode* Parser::parsePointerVariablePlacement(ASTNodeTypeDecl *type) { ASTNode* Parser::parsePointerVariablePlacement(ASTNodeTypeDecl *type) {
auto name = getValue<std::string>(-2); auto name = getValue<Token::Identifier>(-2).get();
auto sizeType = parseType(); auto sizeType = parseType();
auto sizeCleanup = SCOPE_GUARD { delete sizeType; }; auto sizeCleanup = SCOPE_GUARD { delete sizeType; };
@ -925,7 +959,7 @@ namespace hex::pl {
sizeCleanup.release(); sizeCleanup.release();
return new ASTNodePointerVariableDecl(name, type, sizeType, placementOffset); return create(new ASTNodePointerVariableDecl(name, type, sizeType, placementOffset));
} }
std::vector<ASTNode*> Parser::parseNamespace() { std::vector<ASTNode*> Parser::parseNamespace() {
@ -937,7 +971,7 @@ namespace hex::pl {
this->m_currNamespace.push_back(this->m_currNamespace.back()); this->m_currNamespace.push_back(this->m_currNamespace.back());
while (true) { while (true) {
this->m_currNamespace.back().push_back(getValue<std::string>(-1)); this->m_currNamespace.back().push_back(getValue<Token::Identifier>(-1).get());
if (MATCHES(sequence(OPERATOR_SCOPERESOLUTION, IDENTIFIER))) if (MATCHES(sequence(OPERATOR_SCOPERESOLUTION, IDENTIFIER)))
continue; continue;
@ -974,7 +1008,7 @@ namespace hex::pl {
// <(parseUsingDeclaration)|(parseVariablePlacement)|(parseStruct)> // <(parseUsingDeclaration)|(parseVariablePlacement)|(parseStruct)>
std::vector<ASTNode*> Parser::parseStatements() { std::vector<ASTNode*> Parser::parseStatements() {
ASTNode *statement = nullptr; ASTNode *statement;
if (MATCHES(sequence(KEYWORD_USING, IDENTIFIER, OPERATOR_ASSIGNMENT))) if (MATCHES(sequence(KEYWORD_USING, IDENTIFIER, OPERATOR_ASSIGNMENT)))
statement = parseUsingDeclaration(); statement = parseUsingDeclaration();
@ -1003,7 +1037,7 @@ namespace hex::pl {
else if (MATCHES(sequence(KEYWORD_BITFIELD, IDENTIFIER, SEPARATOR_CURLYBRACKETOPEN))) else if (MATCHES(sequence(KEYWORD_BITFIELD, IDENTIFIER, SEPARATOR_CURLYBRACKETOPEN)))
statement = parseBitfield(); statement = parseBitfield();
else if (MATCHES(sequence(KEYWORD_FUNCTION, IDENTIFIER, SEPARATOR_ROUNDBRACKETOPEN))) else if (MATCHES(sequence(KEYWORD_FUNCTION, IDENTIFIER, SEPARATOR_ROUNDBRACKETOPEN)))
statement = parseFunctionDefintion(); statement = parseFunctionDefinition();
else if (MATCHES(sequence(KEYWORD_NAMESPACE))) else if (MATCHES(sequence(KEYWORD_NAMESPACE)))
return parseNamespace(); return parseNamespace();
else throwParseError("invalid sequence", 0); else throwParseError("invalid sequence", 0);
@ -1018,7 +1052,7 @@ namespace hex::pl {
while (MATCHES(sequence(SEPARATOR_ENDOFEXPRESSION))); while (MATCHES(sequence(SEPARATOR_ENDOFEXPRESSION)));
if (auto typeDecl = dynamic_cast<ASTNodeTypeDecl*>(statement); typeDecl != nullptr) { if (auto typeDecl = dynamic_cast<ASTNodeTypeDecl*>(statement); typeDecl != nullptr) {
auto typeName = getNamespacePrefixedName(typeDecl->getName().data()); auto typeName = getNamespacePrefixedName(typeDecl->getName());
if (this->m_types.contains(typeName)) if (this->m_types.contains(typeName))
throwParseError(hex::format("redefinition of type '{}'", typeName)); throwParseError(hex::format("redefinition of type '{}'", typeName));

View File

@ -2,6 +2,7 @@
#include <hex/helpers/file.hpp> #include <hex/helpers/file.hpp>
#include <hex/providers/provider.hpp> #include <hex/providers/provider.hpp>
#include <hex/helpers/logger.hpp>
#include <hex/pattern_language/preprocessor.hpp> #include <hex/pattern_language/preprocessor.hpp>
#include <hex/pattern_language/lexer.hpp> #include <hex/pattern_language/lexer.hpp>
@ -61,7 +62,6 @@ namespace hex::pl {
delete this->m_lexer; delete this->m_lexer;
delete this->m_parser; delete this->m_parser;
delete this->m_validator; delete this->m_validator;
delete this->m_evaluator;
} }
@ -70,6 +70,10 @@ namespace hex::pl {
this->m_evaluator->getConsole().clear(); this->m_evaluator->getConsole().clear();
this->m_evaluator->setProvider(provider); this->m_evaluator->setProvider(provider);
for (auto &node : this->m_currAST)
delete node;
this->m_currAST.clear();
auto preprocessedCode = this->m_preprocessor->preprocess(string); auto preprocessedCode = this->m_preprocessor->preprocess(string);
if (!preprocessedCode.has_value()) { if (!preprocessedCode.has_value()) {
this->m_currError = this->m_preprocessor->getError(); this->m_currError = this->m_preprocessor->getError();
@ -77,7 +81,7 @@ namespace hex::pl {
} }
this->m_evaluator->setDefaultEndian(this->m_defaultEndian); this->m_evaluator->setDefaultEndian(this->m_defaultEndian);
this->m_evaluator->setRecursionLimit(this->m_recursionLimit); // this->m_evaluator->setRecursionLimit(this->m_recursionLimit);
auto tokens = this->m_lexer->lex(preprocessedCode.value()); auto tokens = this->m_lexer->lex(preprocessedCode.value());
if (!tokens.has_value()) { if (!tokens.has_value()) {
@ -91,22 +95,15 @@ namespace hex::pl {
return { }; return { };
} }
ON_SCOPE_EXIT { this->m_currAST = ast.value();
for(auto &node : ast.value())
delete node;
};
auto validatorResult = this->m_validator->validate(ast.value()); auto patterns = this->m_evaluator->evaluate(ast.value());
if (!validatorResult) { if (!patterns.has_value()) {
this->m_currError = this->m_validator->getError(); this->m_currError = this->m_evaluator->getConsole().getLastHardError();
return { }; return { };
} }
auto patternData = this->m_evaluator->evaluate(ast.value()); return patterns;
if (!patternData.has_value())
return { };
return patternData.value();
} }
std::optional<std::vector<PatternData*>> PatternLanguage::executeFile(prv::Provider *provider, const std::string &path) { std::optional<std::vector<PatternData*>> PatternLanguage::executeFile(prv::Provider *provider, const std::string &path) {

View File

@ -25,8 +25,9 @@ namespace hex::pl {
output.reserve(code.length()); output.reserve(code.length());
try { try {
bool startOfLine = true;
while (offset < code.length()) { while (offset < code.length()) {
if (code[offset] == '#') { if (code[offset] == '#' && startOfLine) {
offset += 1; offset += 1;
if (code.substr(offset, 7) == "include") { if (code.substr(offset, 7) == "include") {
@ -164,8 +165,11 @@ namespace hex::pl {
throwPreprocessorError("unterminated comment", lineNumber - 1); throwPreprocessorError("unterminated comment", lineNumber - 1);
} }
if (code[offset] == '\n') if (code[offset] == '\n') {
lineNumber++; lineNumber++;
startOfLine = true;
} else if (!std::isspace(code[offset]))
startOfLine = false;
output += code[offset]; output += code[offset];
offset += 1; offset += 1;

View File

@ -23,21 +23,21 @@ namespace hex {
static TextEditor::LanguageDefinition langDef; static TextEditor::LanguageDefinition langDef;
if (!initialized) { if (!initialized) {
static const char* const keywords[] = { static const char* const keywords[] = {
"using", "struct", "union", "enum", "bitfield", "be", "le", "if", "else", "false", "true", "parent", "addressof", "sizeof", "$", "while", "fn", "return", "namespace" "using", "struct", "union", "enum", "bitfield", "be", "le", "if", "else", "false", "true", "this", "parent", "addressof", "sizeof", "$", "while", "fn", "return", "namespace"
}; };
for (auto& k : keywords) for (auto& k : keywords)
langDef.mKeywords.insert(k); langDef.mKeywords.insert(k);
static std::pair<const char* const, size_t> builtInTypes[] = { static const char* const builtInTypes[] = {
{ "u8", 1 }, { "u16", 2 }, { "u32", 4 }, { "u64", 8 }, { "u128", 16 }, "u8", "u16", "u32", "u64", "u128",
{ "s8", 1 }, { "s16", 2 }, { "s32", 4 }, { "s64", 8 }, { "s128", 16 }, "s8", "s16", "s32", "s64", "s128",
{ "float", 4 }, { "double", 8 }, { "char", 1 }, { "char16", 2 }, { "bool", 1 }, { "padding", 1 } "float", "double", "char", "char16",
"bool", "padding", "str"
}; };
for (const auto &[name, size] : builtInTypes) { for (const auto name : builtInTypes) {
TextEditor::Identifier id; TextEditor::Identifier id;
id.mDeclaration = std::to_string(size); id.mDeclaration = "Built-in type";
id.mDeclaration += size == 1 ? " byte" : " bytes";
langDef.mIdentifiers.insert(std::make_pair(std::string(name), id)); langDef.mIdentifiers.insert(std::make_pair(std::string(name), id));
} }

View File

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

View File

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

View File

@ -33,7 +33,7 @@ namespace hex::test {
std::assert(0xFF00FF | 0x00AA00 == 0xFFAAFF, "| operator error"); std::assert(0xFF00FF | 0x00AA00 == 0xFFAAFF, "| operator error");
std::assert(0xFFFFFF & 0x00FF00 == 0x00FF00, "& operator error"); std::assert(0xFFFFFF & 0x00FF00 == 0x00FF00, "& operator error");
std::assert(0xFFFFFF ^ 0x00AA00 == 0xFF55FF, "^ 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 == 0x0A, ">> operator error");
std::assert(0xAA << 4 == 0xAA0, "<< operator error"); std::assert(0xAA << 4 == 0xAA0, "<< operator error");
@ -46,7 +46,7 @@ namespace hex::test {
// Special operators // Special operators
std::assert($ == 0, "$ operator error"); 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 // Type operators
struct TypeTest { u32 x, y, z; }; 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 testStruct = create<PatternDataStruct>("TestStruct", "testStruct", 0x100, sizeof(s32) + 20 + sizeof(u8[0x10]));
auto variable = create<PatternDataSigned>("s32", "variable", 0x100, sizeof(s32)); 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])); 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); array->setEntries(create<PatternDataUnsigned>("u8", "", 0x100 + sizeof(s32) + 20, sizeof(u8)), 0x10);

View File

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