mirror of
https://github.com/WerWolv/ImHex.git
synced 2026-04-02 21:47:40 -05: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:
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <hex.hpp>
|
||||
#include <hex/helpers/concepts.hpp>
|
||||
#include <hex/pattern_language/token.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
@@ -15,7 +16,7 @@ namespace hex {
|
||||
|
||||
class View;
|
||||
class LanguageDefinition;
|
||||
namespace pl { class ASTNode; class Evaluator; }
|
||||
namespace pl { class Evaluator; }
|
||||
namespace dp { class Node; }
|
||||
|
||||
/*
|
||||
@@ -90,7 +91,7 @@ namespace hex {
|
||||
constexpr static u32 NoParameters = 0x0000'0000;
|
||||
|
||||
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 {
|
||||
u32 parameterCount;
|
||||
|
||||
@@ -57,6 +57,11 @@ namespace hex {
|
||||
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> overloaded(Ts...) -> overloaded<Ts...>;
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,93 +1,96 @@
|
||||
#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 <string>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#define LITERAL_COMPARE(literal, cond) std::visit([&](auto &&literal) { return (cond) != 0; }, literal)
|
||||
#define AS_TYPE(type, value) ctx.template asType<type>(value)
|
||||
#include <hex/pattern_language/log_console.hpp>
|
||||
#include <hex/api/content_registry.hpp>
|
||||
|
||||
namespace hex::prv { class Provider; }
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class PatternData;
|
||||
class ASTNode;
|
||||
|
||||
class Evaluator {
|
||||
public:
|
||||
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; }
|
||||
|
||||
void setDefaultEndian(std::endian endian) { this->m_defaultDataEndian = endian; }
|
||||
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");
|
||||
[[nodiscard]]
|
||||
LogConsole& getConsole() {
|
||||
return this->m_console;
|
||||
}
|
||||
|
||||
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:
|
||||
std::map<std::string, ASTNode*> m_types;
|
||||
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;
|
||||
u64 m_currOffset;
|
||||
prv::Provider *m_provider = nullptr;
|
||||
LogConsole m_console;
|
||||
|
||||
u32 m_recursionLimit;
|
||||
u32 m_currRecursionDepth;
|
||||
std::endian m_defaultEndian = std::endian::native;
|
||||
|
||||
void createLocalVariable(const std::string &varName, PatternData *pattern);
|
||||
void setLocalVariableValue(const std::string &varName, const void *value, size_t size);
|
||||
|
||||
ASTNodeIntegerLiteral* evaluateScopeResolution(ASTNodeScopeResolution *node);
|
||||
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);
|
||||
std::vector<Scope> m_scopes;
|
||||
std::map<std::string, ContentRegistry::PatternLanguageFunctions::Function> m_customFunctions;
|
||||
std::vector<ASTNode*> m_customFunctionDefinitions;
|
||||
std::vector<Token::Literal> m_stack;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -7,8 +7,12 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <hex/pattern_language/ast_node_base.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNode;
|
||||
|
||||
class LogConsole {
|
||||
public:
|
||||
enum Level {
|
||||
@@ -18,9 +22,10 @@ namespace hex::pl {
|
||||
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) {
|
||||
switch (level) {
|
||||
@@ -32,16 +37,29 @@ namespace hex::pl {
|
||||
}
|
||||
}
|
||||
|
||||
[[noreturn]] void abortEvaluation(const std::string &message) {
|
||||
throw EvaluateError(message);
|
||||
[[noreturn]]
|
||||
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() {
|
||||
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:
|
||||
std::vector<std::pair<Level, std::string>> m_consoleLog;
|
||||
EvaluateError m_lastHardError;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -37,6 +37,11 @@ namespace hex::pl {
|
||||
return this->m_curr[index].lineNumber;
|
||||
}
|
||||
|
||||
auto* create(auto *node) {
|
||||
node->setLineNumber(this->getLineNumber(-1));
|
||||
return node;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
const T& getValue(s32 index) const {
|
||||
auto value = std::get_if<T>(&this->m_curr[index].value);
|
||||
@@ -68,6 +73,7 @@ namespace hex::pl {
|
||||
ASTNode* parseScopeResolution();
|
||||
ASTNode* parseRValue(ASTNodeRValue::Path &path);
|
||||
ASTNode* parseFactor();
|
||||
ASTNode* parseCastExpression();
|
||||
ASTNode* parseUnaryExpression();
|
||||
ASTNode* parseMultiplicativeExpression();
|
||||
ASTNode* parseAdditiveExpression();
|
||||
@@ -83,7 +89,7 @@ namespace hex::pl {
|
||||
ASTNode* parseTernaryConditional();
|
||||
ASTNode* parseMathematicalExpression();
|
||||
|
||||
ASTNode* parseFunctionDefintion();
|
||||
ASTNode* parseFunctionDefinition();
|
||||
ASTNode* parseFunctionStatement();
|
||||
ASTNode* parseFunctionVariableAssignment();
|
||||
ASTNode* parseFunctionReturnStatement();
|
||||
@@ -93,7 +99,7 @@ namespace hex::pl {
|
||||
void parseAttribute(Attributable *currNode);
|
||||
ASTNode* parseConditional();
|
||||
ASTNode* parseWhileStatement();
|
||||
ASTNodeTypeDecl* parseType();
|
||||
ASTNodeTypeDecl* parseType(bool allowString = false);
|
||||
ASTNode* parseUsingDeclaration();
|
||||
ASTNode* parsePadding();
|
||||
ASTNode* parseMemberVariable(ASTNodeTypeDecl *type);
|
||||
@@ -238,12 +244,6 @@ namespace hex::pl {
|
||||
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]));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <hex/helpers/utils.hpp>
|
||||
#include <hex/helpers/fmt.hpp>
|
||||
#include <hex/helpers/concepts.hpp>
|
||||
#include <hex/helpers/logger.hpp>
|
||||
|
||||
#include <cstring>
|
||||
#include <codecvt>
|
||||
@@ -87,6 +88,13 @@ namespace hex::pl {
|
||||
[[nodiscard]] PatternData* getParent() const { return this->m_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;
|
||||
[[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) {
|
||||
if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("name")) {
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return left->getVariableName() > right->getVariableName();
|
||||
return left->getDisplayName() > right->getDisplayName();
|
||||
else
|
||||
return left->getVariableName() < right->getVariableName();
|
||||
return left->getDisplayName() < right->getDisplayName();
|
||||
}
|
||||
else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("offset")) {
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
@@ -208,16 +216,16 @@ namespace hex::pl {
|
||||
}
|
||||
|
||||
protected:
|
||||
void createDefaultEntry(const std::string &value) const {
|
||||
void createDefaultEntry(const std::string &value, const Token::Literal &literal) const {
|
||||
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();
|
||||
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() });
|
||||
}
|
||||
this->drawCommentTooltip();
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("%s", this->getVariableName().c_str());
|
||||
ImGui::Text("%s", this->getDisplayName().c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight()));
|
||||
ImGui::TableNextColumn();
|
||||
@@ -227,7 +235,20 @@ namespace hex::pl {
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextColored(ImColor(0xFF9BC64D), "%s", this->getFormattedName().c_str());
|
||||
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 {
|
||||
@@ -248,10 +269,13 @@ namespace hex::pl {
|
||||
size_t m_size;
|
||||
|
||||
u32 m_color;
|
||||
std::optional<std::string> m_displayName;
|
||||
std::string m_variableName;
|
||||
std::optional<std::string> m_comment;
|
||||
std::string m_typeName;
|
||||
|
||||
std::optional<std::pair<ContentRegistry::PatternLanguageFunctions::Function, Evaluator*>> m_formatterFunction;
|
||||
|
||||
PatternData *m_parent;
|
||||
bool m_local = false;
|
||||
};
|
||||
@@ -299,7 +323,7 @@ namespace hex::pl {
|
||||
|
||||
ImGui::TableNextRow();
|
||||
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();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight()));
|
||||
@@ -354,7 +378,7 @@ namespace hex::pl {
|
||||
|
||||
void setPointedAtPattern(PatternData *pattern) {
|
||||
this->m_pointedAt = pattern;
|
||||
this->m_pointedAt->setVariableName("*" + this->getVariableName());
|
||||
this->m_pointedAt->setVariableName("*" + this->getDisplayName());
|
||||
}
|
||||
|
||||
[[nodiscard]] PatternData* getPointedAtPattern() {
|
||||
@@ -380,11 +404,11 @@ namespace hex::pl {
|
||||
}
|
||||
|
||||
void createEntry(prv::Provider* &provider) override {
|
||||
u64 data = 0;
|
||||
u128 data = 0;
|
||||
provider->read(this->getOffset(), &data, this->getSize());
|
||||
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 {
|
||||
@@ -411,42 +435,12 @@ namespace hex::pl {
|
||||
}
|
||||
|
||||
void createEntry(prv::Provider* &provider) override {
|
||||
u128 data = 0;
|
||||
s128 data = 0;
|
||||
provider->read(this->getOffset(), &data, this->getSize());
|
||||
data = hex::changeEndianess(data, this->getSize(), this->getEndian());
|
||||
|
||||
switch (this->getSize()) {
|
||||
case 1: {
|
||||
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;
|
||||
}
|
||||
data = hex::signExtend(this->getSize() * 8, data);
|
||||
this->createDefaultEntry(hex::format("{:d} (0x{:0{}X})", data, data, 1 * 2), data);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string getFormattedName() const override {
|
||||
@@ -478,13 +472,13 @@ namespace hex::pl {
|
||||
provider->read(this->getOffset(), &data, 4);
|
||||
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) {
|
||||
u64 data = 0;
|
||||
provider->read(this->getOffset(), &data, 8);
|
||||
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);
|
||||
|
||||
if (boolean == 0)
|
||||
this->createDefaultEntry("false");
|
||||
this->createDefaultEntry("false", false);
|
||||
else if (boolean == 1)
|
||||
this->createDefaultEntry("true");
|
||||
this->createDefaultEntry("true", true);
|
||||
else
|
||||
this->createDefaultEntry("true*");
|
||||
this->createDefaultEntry("true*", true);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string getFormattedName() const override {
|
||||
@@ -540,7 +534,7 @@ namespace hex::pl {
|
||||
char character;
|
||||
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 {
|
||||
@@ -564,7 +558,8 @@ namespace hex::pl {
|
||||
provider->read(this->getOffset(), &character, 2);
|
||||
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 {
|
||||
@@ -587,7 +582,7 @@ namespace hex::pl {
|
||||
std::string buffer(this->getSize(), 0x00);
|
||||
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 {
|
||||
@@ -615,7 +610,7 @@ namespace hex::pl {
|
||||
|
||||
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 {
|
||||
@@ -662,7 +657,7 @@ namespace hex::pl {
|
||||
|
||||
ImGui::TableNextRow();
|
||||
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();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight()));
|
||||
@@ -777,7 +772,7 @@ namespace hex::pl {
|
||||
|
||||
ImGui::TableNextRow();
|
||||
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();
|
||||
ImGui::TableNextColumn();
|
||||
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_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);
|
||||
}
|
||||
|
||||
@@ -914,7 +910,7 @@ namespace hex::pl {
|
||||
void createEntry(prv::Provider* &provider) override {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
bool open = ImGui::TreeNodeEx(this->getVariableName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
|
||||
bool open = ImGui::TreeNodeEx(this->getDisplayName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
|
||||
this->drawCommentTooltip();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TableNextColumn();
|
||||
@@ -1045,7 +1041,7 @@ namespace hex::pl {
|
||||
void createEntry(prv::Provider* &provider) override {
|
||||
ImGui::TableNextRow();
|
||||
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();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TableNextColumn();
|
||||
@@ -1163,14 +1159,18 @@ namespace hex::pl {
|
||||
|
||||
bool foundValue = false;
|
||||
for (auto &[entryValueLiteral, entryName] : this->m_enumValues) {
|
||||
bool matches = std::visit([&, name = entryName](auto &&entryValue) {
|
||||
if (value == entryValue) {
|
||||
valueString += name;
|
||||
foundValue = true;
|
||||
return true;
|
||||
}
|
||||
bool matches = std::visit(overloaded {
|
||||
[&, name = entryName](auto &&entryValue) {
|
||||
if (value == entryValue) {
|
||||
valueString += name;
|
||||
foundValue = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return false;
|
||||
},
|
||||
[](std::string) { return false; },
|
||||
[](PatternData*) { return false; }
|
||||
}, entryValueLiteral);
|
||||
if (matches)
|
||||
break;
|
||||
@@ -1180,14 +1180,14 @@ namespace hex::pl {
|
||||
valueString += "???";
|
||||
|
||||
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();
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Selectable(("##PatternDataLine"s + std::to_string(this->getOffset())).c_str(), false, ImGuiSelectableFlags_SpanAllColumns)) {
|
||||
EventManager::post<RequestSelectionChange>(Region { this->getOffset(), this->getSize() });
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("%s", this->getVariableName().c_str());
|
||||
ImGui::Text("%s", this->getDisplayName().c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight()));
|
||||
ImGui::TableNextColumn();
|
||||
@@ -1208,7 +1208,7 @@ namespace hex::pl {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -1229,7 +1229,7 @@ namespace hex::pl {
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
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::Text("%s", this->getVariableName().c_str());
|
||||
ImGui::Text("%s", this->getDisplayName().c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight()));
|
||||
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 {
|
||||
for (auto field : this->m_fields)
|
||||
delete field;
|
||||
@@ -1327,7 +1332,7 @@ namespace hex::pl {
|
||||
|
||||
ImGui::TableNextRow();
|
||||
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();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
@@ -21,6 +21,8 @@ namespace hex::pl {
|
||||
class Evaluator;
|
||||
class PatternData;
|
||||
|
||||
class ASTNode;
|
||||
|
||||
class PatternLanguage {
|
||||
public:
|
||||
PatternLanguage();
|
||||
@@ -39,6 +41,8 @@ namespace hex::pl {
|
||||
Validator *m_validator;
|
||||
Evaluator *m_evaluator;
|
||||
|
||||
std::vector<ASTNode*> m_currAST;
|
||||
|
||||
prv::Provider *m_provider = nullptr;
|
||||
std::endian m_defaultEndian = std::endian::native;
|
||||
u32 m_recursionLimit = 32;
|
||||
|
||||
@@ -6,8 +6,12 @@
|
||||
#include <string>
|
||||
#include <variant>
|
||||
|
||||
#include <hex/helpers/utils.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class PatternData;
|
||||
|
||||
class Token {
|
||||
public:
|
||||
enum class Type : u64 {
|
||||
@@ -31,6 +35,7 @@ namespace hex::pl {
|
||||
If,
|
||||
Else,
|
||||
Parent,
|
||||
This,
|
||||
While,
|
||||
Function,
|
||||
Return,
|
||||
@@ -85,6 +90,7 @@ namespace hex::pl {
|
||||
Boolean = 0x14,
|
||||
Float = 0x42,
|
||||
Double = 0x82,
|
||||
String = 0x15,
|
||||
CustomType = 0x00,
|
||||
Padding = 0x1F,
|
||||
|
||||
@@ -108,8 +114,21 @@ namespace hex::pl {
|
||||
EndOfProgram
|
||||
};
|
||||
|
||||
using IntegerLiteral = std::variant<char, bool, u8, s8, u16, s16, u32, s32, u64, s64, u128, s128, float, double>;
|
||||
using ValueTypes = std::variant<Keyword, std::string, Operator, IntegerLiteral, ValueType, Separator>;
|
||||
struct Identifier {
|
||||
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) {
|
||||
|
||||
@@ -131,6 +150,58 @@ namespace hex::pl {
|
||||
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) {
|
||||
switch (type) {
|
||||
case ValueType::Signed8Bit: return "s8";
|
||||
@@ -147,6 +218,7 @@ namespace hex::pl {
|
||||
case ValueType::Double: return "double";
|
||||
case ValueType::Character: return "char";
|
||||
case ValueType::Character16: return "char16";
|
||||
case ValueType::Padding: return "padding";
|
||||
default: return "< ??? >";
|
||||
}
|
||||
}
|
||||
@@ -204,14 +276,15 @@ namespace hex::pl {
|
||||
#define KEYWORD_IF COMPONENT(Keyword, If)
|
||||
#define KEYWORD_ELSE COMPONENT(Keyword, Else)
|
||||
#define KEYWORD_PARENT COMPONENT(Keyword, Parent)
|
||||
#define KEYWORD_THIS COMPONENT(Keyword, This)
|
||||
#define KEYWORD_WHILE COMPONENT(Keyword, While)
|
||||
#define KEYWORD_FUNCTION COMPONENT(Keyword, Function)
|
||||
#define KEYWORD_RETURN COMPONENT(Keyword, Return)
|
||||
#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 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_ASSIGNMENT COMPONENT(Operator, Assignment)
|
||||
|
||||
@@ -167,7 +167,7 @@ namespace hex {
|
||||
/* 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;
|
||||
for (auto &scope : ns)
|
||||
functionName += scope + "::";
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -28,9 +28,9 @@ namespace hex::pl {
|
||||
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::IntegerLiteral result;
|
||||
Token::Literal result;
|
||||
|
||||
u8 base;
|
||||
|
||||
@@ -38,20 +38,8 @@ namespace hex::pl {
|
||||
auto numberData = std::string_view(string).substr(0, endPos);
|
||||
|
||||
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;
|
||||
numberData.remove_suffix(3);
|
||||
} else if (numberData.ends_with("L")) {
|
||||
type = Token::ValueType::Signed64Bit;
|
||||
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")) {
|
||||
if (numberData.ends_with('F')) {
|
||||
type = Token::ValueType::Float;
|
||||
@@ -98,7 +86,7 @@ namespace hex::pl {
|
||||
} else return { };
|
||||
|
||||
if (type == Token::ValueType::Any)
|
||||
type = Token::ValueType::Signed32Bit;
|
||||
type = Token::ValueType::Signed128Bit;
|
||||
|
||||
|
||||
if (numberData.length() == 0)
|
||||
@@ -119,10 +107,6 @@ namespace hex::pl {
|
||||
}
|
||||
|
||||
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::Signed128Bit: return { s128(integer) };
|
||||
default: return { };
|
||||
@@ -379,7 +363,7 @@ namespace hex::pl {
|
||||
|
||||
auto [c, charSize] = character.value();
|
||||
|
||||
tokens.emplace_back(VALUE_TOKEN(Integer, c));
|
||||
tokens.emplace_back(VALUE_TOKEN(Integer, Token::Literal(c)));
|
||||
offset += charSize;
|
||||
} else if (c == '\"') {
|
||||
auto string = getStringLiteral(code.substr(offset));
|
||||
@@ -389,7 +373,7 @@ namespace hex::pl {
|
||||
|
||||
auto [s, stringSize] = string.value();
|
||||
|
||||
tokens.emplace_back(VALUE_TOKEN(String, s));
|
||||
tokens.emplace_back(VALUE_TOKEN(String, Token::Literal(s)));
|
||||
offset += stringSize;
|
||||
} else if (std::isalpha(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")
|
||||
tokens.emplace_back(TOKEN(Keyword, Else));
|
||||
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")
|
||||
tokens.emplace_back(VALUE_TOKEN(Integer, bool(1)));
|
||||
tokens.emplace_back(VALUE_TOKEN(Integer, Token::Literal(true)));
|
||||
else if (identifier == "parent")
|
||||
tokens.emplace_back(TOKEN(Keyword, Parent));
|
||||
else if (identifier == "this")
|
||||
tokens.emplace_back(TOKEN(Keyword, This));
|
||||
else if (identifier == "while")
|
||||
tokens.emplace_back(TOKEN(Keyword, While));
|
||||
else if (identifier == "fn")
|
||||
@@ -460,13 +446,15 @@ namespace hex::pl {
|
||||
tokens.emplace_back(TOKEN(ValueType, Character16));
|
||||
else if (identifier == "bool")
|
||||
tokens.emplace_back(TOKEN(ValueType, Boolean));
|
||||
else if (identifier == "str")
|
||||
tokens.emplace_back(TOKEN(ValueType, String));
|
||||
else if (identifier == "padding")
|
||||
tokens.emplace_back(TOKEN(ValueType, Padding));
|
||||
|
||||
// If it's not a keyword and a builtin type, it has to be an identifier
|
||||
|
||||
else
|
||||
tokens.emplace_back(VALUE_TOKEN(Identifier, identifier));
|
||||
tokens.emplace_back(VALUE_TOKEN(Identifier, Token::Identifier(identifier)));
|
||||
|
||||
offset += identifier.length();
|
||||
} else if (std::isdigit(c)) {
|
||||
@@ -476,7 +464,7 @@ namespace hex::pl {
|
||||
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]);
|
||||
} else
|
||||
throwLexerError("unknown token", lineNumber);
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
#include <hex/pattern_language/parser.hpp>
|
||||
|
||||
#include <hex/helpers/fmt.hpp>
|
||||
|
||||
#include <optional>
|
||||
|
||||
#define MATCHES(x) (begin() && x)
|
||||
|
||||
#define TO_NUMERIC_EXPRESSION(node) new ASTNodeNumericExpression((node), new ASTNodeIntegerLiteral(s32(0)), Token::Operator::Plus)
|
||||
|
||||
// Definition syntax:
|
||||
// [A] : Either A or no token
|
||||
// [A|B] : Either A, B or no token
|
||||
@@ -33,10 +29,7 @@ namespace hex::pl {
|
||||
};
|
||||
|
||||
while (!MATCHES(sequence(SEPARATOR_ROUNDBRACKETCLOSE))) {
|
||||
if (MATCHES(sequence(STRING)))
|
||||
params.push_back(parseStringLiteral());
|
||||
else
|
||||
params.push_back(parseMathematicalExpression());
|
||||
params.push_back(parseMathematicalExpression());
|
||||
|
||||
if (MATCHES(sequence(SEPARATOR_COMMA, SEPARATOR_ROUNDBRACKETCLOSE)))
|
||||
throwParseError("unexpected ',' at end of function parameter list", -1);
|
||||
@@ -49,18 +42,18 @@ namespace hex::pl {
|
||||
|
||||
paramCleanup.release();
|
||||
|
||||
return new ASTNodeFunctionCall(functionName, params);
|
||||
return create(new ASTNodeFunctionCall(functionName, params));
|
||||
}
|
||||
|
||||
ASTNode* Parser::parseStringLiteral() {
|
||||
return new ASTNodeStringLiteral(getValue<std::string>(-1));
|
||||
return create(new ASTNodeLiteral(getValue<Token::Literal>(-1)));
|
||||
}
|
||||
|
||||
std::string Parser::parseNamespaceResolution() {
|
||||
std::string name;
|
||||
|
||||
while (true) {
|
||||
name += getValue<std::string>(-1);
|
||||
name += getValue<Token::Identifier>(-1).get();
|
||||
|
||||
if (MATCHES(sequence(OPERATOR_SCOPERESOLUTION, IDENTIFIER))) {
|
||||
name += "::";
|
||||
@@ -77,14 +70,14 @@ namespace hex::pl {
|
||||
std::string typeName;
|
||||
|
||||
while (true) {
|
||||
typeName += getValue<std::string>(-1);
|
||||
typeName += getValue<Token::Identifier>(-1).get();
|
||||
|
||||
if (MATCHES(sequence(OPERATOR_SCOPERESOLUTION, IDENTIFIER))) {
|
||||
if (peek(OPERATOR_SCOPERESOLUTION, 0) && peek(IDENTIFIER, 1)) {
|
||||
typeName += "::";
|
||||
continue;
|
||||
} else {
|
||||
return new ASTNodeScopeResolution({ typeName, getValue<std::string>(-1) });
|
||||
return create(new ASTNodeScopeResolution({ typeName, getValue<Token::Identifier>(-1).get() }));
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -97,9 +90,11 @@ namespace hex::pl {
|
||||
// <Identifier[.]...>
|
||||
ASTNode* Parser::parseRValue(ASTNodeRValue::Path &path) {
|
||||
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))
|
||||
path.emplace_back("parent");
|
||||
else if (peek(KEYWORD_THIS, -1))
|
||||
path.emplace_back("this");
|
||||
|
||||
if (MATCHES(sequence(SEPARATOR_SQUAREBRACKETOPEN))) {
|
||||
path.push_back(parseMathematicalExpression());
|
||||
@@ -113,13 +108,13 @@ namespace hex::pl {
|
||||
else
|
||||
throwParseError("expected member name or 'parent' keyword", -1);
|
||||
} else
|
||||
return new ASTNodeRValue(path);
|
||||
return create(new ASTNodeRValue(path));
|
||||
}
|
||||
|
||||
// <Integer|((parseMathematicalExpression))>
|
||||
ASTNode* Parser::parseFactor() {
|
||||
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))) {
|
||||
auto node = this->parseMathematicalExpression();
|
||||
if (!MATCHES(sequence(SEPARATOR_ROUNDBRACKETCLOSE))) {
|
||||
@@ -135,34 +130,54 @@ namespace hex::pl {
|
||||
|
||||
|
||||
if (isFunction) {
|
||||
return TO_NUMERIC_EXPRESSION(this->parseFunctionCall());
|
||||
return this->parseFunctionCall();
|
||||
} else if (peek(OPERATOR_SCOPERESOLUTION, 0)) {
|
||||
return TO_NUMERIC_EXPRESSION(this->parseScopeResolution());
|
||||
return this->parseScopeResolution();
|
||||
} else {
|
||||
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;
|
||||
return TO_NUMERIC_EXPRESSION(this->parseRValue(path));
|
||||
return this->parseRValue(path);
|
||||
} 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))) {
|
||||
auto op = getValue<Token::Operator>(-2);
|
||||
|
||||
if (!MATCHES(oneOf(IDENTIFIER, KEYWORD_PARENT))) {
|
||||
if (!MATCHES(oneOf(IDENTIFIER, KEYWORD_PARENT, KEYWORD_THIS))) {
|
||||
throwParseError("expected rvalue identifier");
|
||||
}
|
||||
|
||||
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))) {
|
||||
delete node;
|
||||
throwParseError("expected closing parenthesis");
|
||||
}
|
||||
return TO_NUMERIC_EXPRESSION(node);
|
||||
return node;
|
||||
} 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)
|
||||
@@ -170,10 +185,12 @@ namespace hex::pl {
|
||||
if (MATCHES(oneOf(OPERATOR_PLUS, OPERATOR_MINUS, OPERATOR_BOOLNOT, OPERATOR_BITNOT))) {
|
||||
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)
|
||||
@@ -184,7 +201,7 @@ namespace hex::pl {
|
||||
|
||||
while (MATCHES(oneOf(OPERATOR_STAR, OPERATOR_SLASH, OPERATOR_PERCENT))) {
|
||||
auto op = getValue<Token::Operator>(-1);
|
||||
node = new ASTNodeNumericExpression(node, this->parseUnaryExpression(), op);
|
||||
node = create(new ASTNodeMathematicalExpression(node, this->parseUnaryExpression(), op));
|
||||
}
|
||||
|
||||
nodeCleanup.release();
|
||||
@@ -200,7 +217,7 @@ namespace hex::pl {
|
||||
|
||||
while (MATCHES(variant(OPERATOR_PLUS, OPERATOR_MINUS))) {
|
||||
auto op = getValue<Token::Operator>(-1);
|
||||
node = new ASTNodeNumericExpression(node, this->parseMultiplicativeExpression(), op);
|
||||
node = create(new ASTNodeMathematicalExpression(node, this->parseMultiplicativeExpression(), op));
|
||||
}
|
||||
|
||||
nodeCleanup.release();
|
||||
@@ -216,7 +233,7 @@ namespace hex::pl {
|
||||
|
||||
while (MATCHES(variant(OPERATOR_SHIFTLEFT, OPERATOR_SHIFTRIGHT))) {
|
||||
auto op = getValue<Token::Operator>(-1);
|
||||
node = new ASTNodeNumericExpression(node, this->parseAdditiveExpression(), op);
|
||||
node = create(new ASTNodeMathematicalExpression(node, this->parseAdditiveExpression(), op));
|
||||
}
|
||||
|
||||
nodeCleanup.release();
|
||||
@@ -232,7 +249,7 @@ namespace hex::pl {
|
||||
|
||||
while (MATCHES(sequence(OPERATOR_BOOLGREATERTHAN) || sequence(OPERATOR_BOOLLESSTHAN) || sequence(OPERATOR_BOOLGREATERTHANOREQUALS) || sequence(OPERATOR_BOOLLESSTHANOREQUALS))) {
|
||||
auto op = getValue<Token::Operator>(-1);
|
||||
node = new ASTNodeNumericExpression(node, this->parseShiftExpression(), op);
|
||||
node = create(new ASTNodeMathematicalExpression(node, this->parseShiftExpression(), op));
|
||||
}
|
||||
|
||||
nodeCleanup.release();
|
||||
@@ -248,7 +265,7 @@ namespace hex::pl {
|
||||
|
||||
while (MATCHES(sequence(OPERATOR_BOOLEQUALS) || sequence(OPERATOR_BOOLNOTEQUALS))) {
|
||||
auto op = getValue<Token::Operator>(-1);
|
||||
node = new ASTNodeNumericExpression(node, this->parseRelationExpression(), op);
|
||||
node = create(new ASTNodeMathematicalExpression(node, this->parseRelationExpression(), op));
|
||||
}
|
||||
|
||||
nodeCleanup.release();
|
||||
@@ -263,7 +280,7 @@ namespace hex::pl {
|
||||
auto nodeCleanup = SCOPE_GUARD { delete node; };
|
||||
|
||||
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();
|
||||
@@ -278,7 +295,7 @@ namespace hex::pl {
|
||||
auto nodeCleanup = SCOPE_GUARD { delete node; };
|
||||
|
||||
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();
|
||||
@@ -293,7 +310,7 @@ namespace hex::pl {
|
||||
auto nodeCleanup = SCOPE_GUARD { delete node; };
|
||||
|
||||
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();
|
||||
@@ -308,7 +325,7 @@ namespace hex::pl {
|
||||
auto nodeCleanup = SCOPE_GUARD { delete node; };
|
||||
|
||||
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();
|
||||
@@ -323,7 +340,7 @@ namespace hex::pl {
|
||||
auto nodeCleanup = SCOPE_GUARD { delete node; };
|
||||
|
||||
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();
|
||||
@@ -338,7 +355,7 @@ namespace hex::pl {
|
||||
auto nodeCleanup = SCOPE_GUARD { delete node; };
|
||||
|
||||
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();
|
||||
@@ -359,7 +376,7 @@ namespace hex::pl {
|
||||
throwParseError("expected ':' in ternary expression");
|
||||
|
||||
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();
|
||||
@@ -381,14 +398,19 @@ namespace hex::pl {
|
||||
if (!MATCHES(sequence(IDENTIFIER)))
|
||||
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))) {
|
||||
auto value = this->getValue<std::string>(-2);
|
||||
currNode->addAttribute(new ASTNodeAttribute(attribute, value));
|
||||
auto value = getValue<Token::Literal>(-2);
|
||||
auto string = std::get_if<std::string>(&value);
|
||||
|
||||
if (string == nullptr)
|
||||
throwParseError("expected string attribute argument");
|
||||
|
||||
currNode->addAttribute(create(new ASTNodeAttribute(attribute, *string)));
|
||||
}
|
||||
else
|
||||
currNode->addAttribute(new ASTNodeAttribute(attribute));
|
||||
currNode->addAttribute(create(new ASTNodeAttribute(attribute)));
|
||||
|
||||
} while (MATCHES(sequence(SEPARATOR_COMMA)));
|
||||
|
||||
@@ -398,16 +420,24 @@ namespace hex::pl {
|
||||
|
||||
/* Functions */
|
||||
|
||||
ASTNode* Parser::parseFunctionDefintion() {
|
||||
const auto &functionName = getValue<std::string>(-2);
|
||||
std::vector<std::string> params;
|
||||
ASTNode* Parser::parseFunctionDefinition() {
|
||||
const auto &functionName = getValue<Token::Identifier>(-2).get();
|
||||
std::map<std::string, ASTNode*> params;
|
||||
|
||||
// Parse parameter list
|
||||
bool hasParams = MATCHES(sequence(IDENTIFIER));
|
||||
bool hasParams = !peek(SEPARATOR_ROUNDBRACKETCLOSE);
|
||||
u32 unnamedParamCount = 0;
|
||||
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)))
|
||||
break;
|
||||
else
|
||||
@@ -435,7 +465,7 @@ namespace hex::pl {
|
||||
}
|
||||
|
||||
bodyCleanup.release();
|
||||
return new ASTNodeFunctionDefinition(getNamespacePrefixedName(functionName), params, body);
|
||||
return create(new ASTNodeFunctionDefinition(getNamespacePrefixedName(functionName), params, body));
|
||||
}
|
||||
|
||||
ASTNode* Parser::parseFunctionStatement() {
|
||||
@@ -487,18 +517,18 @@ namespace hex::pl {
|
||||
}
|
||||
|
||||
ASTNode* Parser::parseFunctionVariableAssignment() {
|
||||
const auto &lvalue = getValue<std::string>(-2);
|
||||
const auto &lvalue = getValue<Token::Identifier>(-2).get();
|
||||
|
||||
auto rvalue = this->parseMathematicalExpression();
|
||||
|
||||
return new ASTNodeAssignment(lvalue, rvalue);
|
||||
return create(new ASTNodeAssignment(lvalue, rvalue));
|
||||
}
|
||||
|
||||
ASTNode* Parser::parseFunctionReturnStatement() {
|
||||
if (peek(SEPARATOR_ENDOFEXPRESSION))
|
||||
return new ASTNodeReturnStatement(nullptr);
|
||||
return create(new ASTNodeReturnStatement(nullptr));
|
||||
else
|
||||
return new ASTNodeReturnStatement(this->parseMathematicalExpression());
|
||||
return create(new ASTNodeReturnStatement(this->parseMathematicalExpression()));
|
||||
}
|
||||
|
||||
ASTNode* Parser::parseFunctionConditional() {
|
||||
@@ -532,7 +562,7 @@ namespace hex::pl {
|
||||
|
||||
cleanup.release();
|
||||
|
||||
return new ASTNodeConditionalStatement(condition, trueBody, falseBody);
|
||||
return create(new ASTNodeConditionalStatement(condition, trueBody, falseBody));
|
||||
}
|
||||
|
||||
ASTNode* Parser::parseFunctionWhileLoop() {
|
||||
@@ -556,7 +586,7 @@ namespace hex::pl {
|
||||
|
||||
cleanup.release();
|
||||
|
||||
return new ASTNodeWhileStatement(condition, body);
|
||||
return create(new ASTNodeWhileStatement(condition, body));
|
||||
}
|
||||
|
||||
/* Control flow */
|
||||
@@ -593,7 +623,7 @@ namespace hex::pl {
|
||||
|
||||
cleanup.release();
|
||||
|
||||
return new ASTNodeConditionalStatement(condition, trueBody, falseBody);
|
||||
return create(new ASTNodeConditionalStatement(condition, trueBody, falseBody));
|
||||
}
|
||||
|
||||
// while ((parseMathematicalExpression))
|
||||
@@ -609,13 +639,13 @@ namespace hex::pl {
|
||||
|
||||
cleanup.release();
|
||||
|
||||
return new ASTNodeWhileStatement(condition, { });
|
||||
return create(new ASTNodeWhileStatement(condition, { }));
|
||||
}
|
||||
|
||||
/* Type declarations */
|
||||
|
||||
// [be|le] <Identifier|u8|u16|u32|u64|u128|s8|s16|s32|s64|s128|float|double>
|
||||
ASTNodeTypeDecl* Parser::parseType() {
|
||||
// [be|le] <Identifier|u8|u16|u32|u64|u128|s8|s16|s32|s64|s128|float|double|str>
|
||||
ASTNodeTypeDecl* Parser::parseType(bool allowString) {
|
||||
std::optional<std::endian> endian;
|
||||
|
||||
if (MATCHES(sequence(KEYWORD_LE)))
|
||||
@@ -627,24 +657,28 @@ namespace hex::pl {
|
||||
std::string typeName = parseNamespaceResolution();
|
||||
|
||||
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)))
|
||||
return new ASTNodeTypeDecl({ }, this->m_types[getNamespacePrefixedName(typeName)]->clone(), endian);
|
||||
return create(new ASTNodeTypeDecl({ }, this->m_types[getNamespacePrefixedName(typeName)]->clone(), endian));
|
||||
else
|
||||
throwParseError(hex::format("unknown type '{}'", typeName));
|
||||
}
|
||||
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");
|
||||
}
|
||||
|
||||
// using Identifier = (parseType)
|
||||
ASTNode* Parser::parseUsingDeclaration() {
|
||||
auto name = getValue<std::string>(-2);
|
||||
auto name = getValue<Token::Identifier>(-2).get();
|
||||
auto *type = dynamic_cast<ASTNodeTypeDecl *>(parseType());
|
||||
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)]
|
||||
@@ -656,7 +690,7 @@ namespace hex::pl {
|
||||
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
|
||||
@@ -667,19 +701,19 @@ namespace hex::pl {
|
||||
auto variableCleanup = SCOPE_GUARD { for (auto var : variables) delete var; };
|
||||
|
||||
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)));
|
||||
|
||||
variableCleanup.release();
|
||||
|
||||
return new ASTNodeMultiVariableDecl(variables);
|
||||
return create(new ASTNodeMultiVariableDecl(variables));
|
||||
} else
|
||||
return new ASTNodeVariableDecl(getValue<std::string>(-1), type->clone());
|
||||
return create(new ASTNodeVariableDecl(getValue<Token::Identifier>(-1).get(), type->clone()));
|
||||
}
|
||||
|
||||
// (parseType) Identifier[(parseMathematicalExpression)]
|
||||
ASTNode* Parser::parseMemberArrayVariable(ASTNodeTypeDecl *type) {
|
||||
auto name = getValue<std::string>(-2);
|
||||
auto name = getValue<Token::Identifier>(-2).get();
|
||||
|
||||
ASTNode *size = nullptr;
|
||||
auto sizeCleanup = SCOPE_GUARD { delete size; };
|
||||
@@ -696,12 +730,12 @@ namespace hex::pl {
|
||||
|
||||
sizeCleanup.release();
|
||||
|
||||
return new ASTNodeArrayVariableDecl(name, type->clone(), size);
|
||||
return create(new ASTNodeArrayVariableDecl(name, type->clone(), size));
|
||||
}
|
||||
|
||||
// (parseType) *Identifier : (parseType)
|
||||
ASTNode* Parser::parseMemberPointerVariable(ASTNodeTypeDecl *type) {
|
||||
auto name = getValue<std::string>(-2);
|
||||
auto name = getValue<Token::Identifier>(-2).get();
|
||||
|
||||
auto sizeType = parseType();
|
||||
|
||||
@@ -712,7 +746,7 @@ namespace hex::pl {
|
||||
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)]
|
||||
@@ -758,8 +792,8 @@ namespace hex::pl {
|
||||
|
||||
// struct Identifier { <(parseMember)...> }
|
||||
ASTNode* Parser::parseStruct() {
|
||||
const auto structNode = new ASTNodeStruct();
|
||||
const auto &typeName = getValue<std::string>(-2);
|
||||
const auto structNode = create(new ASTNodeStruct());
|
||||
const auto &typeName = getValue<Token::Identifier>(-2).get();
|
||||
auto structGuard = SCOPE_GUARD { delete structNode; };
|
||||
|
||||
while (!MATCHES(sequence(SEPARATOR_CURLYBRACKETCLOSE))) {
|
||||
@@ -768,13 +802,13 @@ namespace hex::pl {
|
||||
|
||||
structGuard.release();
|
||||
|
||||
return new ASTNodeTypeDecl(typeName, structNode);
|
||||
return create(new ASTNodeTypeDecl(typeName, structNode));
|
||||
}
|
||||
|
||||
// union Identifier { <(parseMember)...> }
|
||||
ASTNode* Parser::parseUnion() {
|
||||
const auto unionNode = new ASTNodeUnion();
|
||||
const auto &typeName = getValue<std::string>(-2);
|
||||
const auto unionNode = create(new ASTNodeUnion());
|
||||
const auto &typeName = getValue<Token::Identifier>(-2).get();
|
||||
auto unionGuard = SCOPE_GUARD { delete unionNode; };
|
||||
|
||||
while (!MATCHES(sequence(SEPARATOR_CURLYBRACKETCLOSE))) {
|
||||
@@ -783,17 +817,17 @@ namespace hex::pl {
|
||||
|
||||
unionGuard.release();
|
||||
|
||||
return new ASTNodeTypeDecl(typeName, unionNode);
|
||||
return create(new ASTNodeTypeDecl(typeName, unionNode));
|
||||
}
|
||||
|
||||
// enum Identifier : (parseType) { <<Identifier|Identifier = (parseMathematicalExpression)[,]>...> }
|
||||
ASTNode* Parser::parseEnum() {
|
||||
auto typeName = getValue<std::string>(-2);
|
||||
auto typeName = getValue<Token::Identifier>(-2).get();
|
||||
|
||||
auto underlyingType = parseType();
|
||||
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; };
|
||||
|
||||
if (!MATCHES(sequence(SEPARATOR_CURLYBRACKETOPEN)))
|
||||
@@ -802,7 +836,7 @@ namespace hex::pl {
|
||||
ASTNode *lastEntry = nullptr;
|
||||
while (!MATCHES(sequence(SEPARATOR_CURLYBRACKETCLOSE))) {
|
||||
if (MATCHES(sequence(IDENTIFIER, OPERATOR_ASSIGNMENT))) {
|
||||
auto name = getValue<std::string>(-2);
|
||||
auto name = getValue<Token::Identifier>(-2).get();
|
||||
auto value = parseMathematicalExpression();
|
||||
|
||||
enumNode->addEntry(name, value);
|
||||
@@ -810,11 +844,11 @@ namespace hex::pl {
|
||||
}
|
||||
else if (MATCHES(sequence(IDENTIFIER))) {
|
||||
ASTNode *valueExpr;
|
||||
auto name = getValue<std::string>(-1);
|
||||
auto name = getValue<Token::Identifier>(-1).get();
|
||||
if (enumNode->getEntries().empty())
|
||||
valueExpr = lastEntry = TO_NUMERIC_EXPRESSION(new ASTNodeIntegerLiteral(u8(0)));
|
||||
valueExpr = lastEntry = create(new ASTNodeLiteral(u128(0)));
|
||||
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);
|
||||
}
|
||||
@@ -833,19 +867,19 @@ namespace hex::pl {
|
||||
|
||||
enumGuard.release();
|
||||
|
||||
return new ASTNodeTypeDecl(typeName, enumNode);
|
||||
return create(new ASTNodeTypeDecl(typeName, enumNode));
|
||||
}
|
||||
|
||||
// bitfield Identifier { <Identifier : (parseMathematicalExpression)[;]...> }
|
||||
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; };
|
||||
|
||||
while (!MATCHES(sequence(SEPARATOR_CURLYBRACKETCLOSE))) {
|
||||
if (MATCHES(sequence(IDENTIFIER, OPERATOR_INHERIT))) {
|
||||
auto name = getValue<std::string>(-2);
|
||||
auto name = getValue<Token::Identifier>(-2).get();
|
||||
bitfieldNode->addEntry(name, parseMathematicalExpression());
|
||||
}
|
||||
else if (MATCHES(sequence(SEPARATOR_ENDOFPROGRAM)))
|
||||
@@ -862,24 +896,24 @@ namespace hex::pl {
|
||||
|
||||
enumGuard.release();
|
||||
|
||||
return new ASTNodeTypeDecl(typeName, bitfieldNode);
|
||||
return create(new ASTNodeTypeDecl(typeName, bitfieldNode));
|
||||
}
|
||||
|
||||
// (parseType) Identifier @ Integer
|
||||
ASTNode* Parser::parseVariablePlacement(ASTNodeTypeDecl *type) {
|
||||
auto name = getValue<std::string>(-1);
|
||||
auto name = getValue<Token::Identifier>(-1).get();
|
||||
|
||||
if (!MATCHES(sequence(OPERATOR_AT)))
|
||||
throwParseError("expected placement instruction", -1);
|
||||
|
||||
auto placementOffset = parseMathematicalExpression();
|
||||
|
||||
return new ASTNodeVariableDecl(name, type, placementOffset);
|
||||
return create(new ASTNodeVariableDecl(name, type, placementOffset));
|
||||
}
|
||||
|
||||
// (parseType) Identifier[[(parseMathematicalExpression)]] @ Integer
|
||||
ASTNode* Parser::parseArrayVariablePlacement(ASTNodeTypeDecl *type) {
|
||||
auto name = getValue<std::string>(-2);
|
||||
auto name = getValue<Token::Identifier>(-2).get();
|
||||
|
||||
ASTNode *size = nullptr;
|
||||
auto sizeCleanup = SCOPE_GUARD { delete size; };
|
||||
@@ -901,12 +935,12 @@ namespace hex::pl {
|
||||
|
||||
sizeCleanup.release();
|
||||
|
||||
return new ASTNodeArrayVariableDecl(name, type, size, placementOffset);
|
||||
return create(new ASTNodeArrayVariableDecl(name, type, size, placementOffset));
|
||||
}
|
||||
|
||||
// (parseType) *Identifier : (parseType) @ Integer
|
||||
ASTNode* Parser::parsePointerVariablePlacement(ASTNodeTypeDecl *type) {
|
||||
auto name = getValue<std::string>(-2);
|
||||
auto name = getValue<Token::Identifier>(-2).get();
|
||||
|
||||
auto sizeType = parseType();
|
||||
auto sizeCleanup = SCOPE_GUARD { delete sizeType; };
|
||||
@@ -925,7 +959,7 @@ namespace hex::pl {
|
||||
|
||||
sizeCleanup.release();
|
||||
|
||||
return new ASTNodePointerVariableDecl(name, type, sizeType, placementOffset);
|
||||
return create(new ASTNodePointerVariableDecl(name, type, sizeType, placementOffset));
|
||||
}
|
||||
|
||||
std::vector<ASTNode*> Parser::parseNamespace() {
|
||||
@@ -937,7 +971,7 @@ namespace hex::pl {
|
||||
this->m_currNamespace.push_back(this->m_currNamespace.back());
|
||||
|
||||
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)))
|
||||
continue;
|
||||
@@ -974,7 +1008,7 @@ namespace hex::pl {
|
||||
|
||||
// <(parseUsingDeclaration)|(parseVariablePlacement)|(parseStruct)>
|
||||
std::vector<ASTNode*> Parser::parseStatements() {
|
||||
ASTNode *statement = nullptr;
|
||||
ASTNode *statement;
|
||||
|
||||
if (MATCHES(sequence(KEYWORD_USING, IDENTIFIER, OPERATOR_ASSIGNMENT)))
|
||||
statement = parseUsingDeclaration();
|
||||
@@ -1003,7 +1037,7 @@ namespace hex::pl {
|
||||
else if (MATCHES(sequence(KEYWORD_BITFIELD, IDENTIFIER, SEPARATOR_CURLYBRACKETOPEN)))
|
||||
statement = parseBitfield();
|
||||
else if (MATCHES(sequence(KEYWORD_FUNCTION, IDENTIFIER, SEPARATOR_ROUNDBRACKETOPEN)))
|
||||
statement = parseFunctionDefintion();
|
||||
statement = parseFunctionDefinition();
|
||||
else if (MATCHES(sequence(KEYWORD_NAMESPACE)))
|
||||
return parseNamespace();
|
||||
else throwParseError("invalid sequence", 0);
|
||||
@@ -1018,7 +1052,7 @@ namespace hex::pl {
|
||||
while (MATCHES(sequence(SEPARATOR_ENDOFEXPRESSION)));
|
||||
|
||||
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))
|
||||
throwParseError(hex::format("redefinition of type '{}'", typeName));
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <hex/helpers/file.hpp>
|
||||
#include <hex/providers/provider.hpp>
|
||||
#include <hex/helpers/logger.hpp>
|
||||
|
||||
#include <hex/pattern_language/preprocessor.hpp>
|
||||
#include <hex/pattern_language/lexer.hpp>
|
||||
@@ -61,7 +62,6 @@ namespace hex::pl {
|
||||
delete this->m_lexer;
|
||||
delete this->m_parser;
|
||||
delete this->m_validator;
|
||||
delete this->m_evaluator;
|
||||
}
|
||||
|
||||
|
||||
@@ -70,6 +70,10 @@ namespace hex::pl {
|
||||
this->m_evaluator->getConsole().clear();
|
||||
this->m_evaluator->setProvider(provider);
|
||||
|
||||
for (auto &node : this->m_currAST)
|
||||
delete node;
|
||||
this->m_currAST.clear();
|
||||
|
||||
auto preprocessedCode = this->m_preprocessor->preprocess(string);
|
||||
if (!preprocessedCode.has_value()) {
|
||||
this->m_currError = this->m_preprocessor->getError();
|
||||
@@ -77,7 +81,7 @@ namespace hex::pl {
|
||||
}
|
||||
|
||||
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());
|
||||
if (!tokens.has_value()) {
|
||||
@@ -91,22 +95,15 @@ namespace hex::pl {
|
||||
return { };
|
||||
}
|
||||
|
||||
ON_SCOPE_EXIT {
|
||||
for(auto &node : ast.value())
|
||||
delete node;
|
||||
};
|
||||
this->m_currAST = ast.value();
|
||||
|
||||
auto validatorResult = this->m_validator->validate(ast.value());
|
||||
if (!validatorResult) {
|
||||
this->m_currError = this->m_validator->getError();
|
||||
auto patterns = this->m_evaluator->evaluate(ast.value());
|
||||
if (!patterns.has_value()) {
|
||||
this->m_currError = this->m_evaluator->getConsole().getLastHardError();
|
||||
return { };
|
||||
}
|
||||
|
||||
auto patternData = this->m_evaluator->evaluate(ast.value());
|
||||
if (!patternData.has_value())
|
||||
return { };
|
||||
|
||||
return patternData.value();
|
||||
return patterns;
|
||||
}
|
||||
|
||||
std::optional<std::vector<PatternData*>> PatternLanguage::executeFile(prv::Provider *provider, const std::string &path) {
|
||||
|
||||
@@ -25,8 +25,9 @@ namespace hex::pl {
|
||||
output.reserve(code.length());
|
||||
|
||||
try {
|
||||
bool startOfLine = true;
|
||||
while (offset < code.length()) {
|
||||
if (code[offset] == '#') {
|
||||
if (code[offset] == '#' && startOfLine) {
|
||||
offset += 1;
|
||||
|
||||
if (code.substr(offset, 7) == "include") {
|
||||
@@ -164,8 +165,11 @@ namespace hex::pl {
|
||||
throwPreprocessorError("unterminated comment", lineNumber - 1);
|
||||
}
|
||||
|
||||
if (code[offset] == '\n')
|
||||
if (code[offset] == '\n') {
|
||||
lineNumber++;
|
||||
startOfLine = true;
|
||||
} else if (!std::isspace(code[offset]))
|
||||
startOfLine = false;
|
||||
|
||||
output += code[offset];
|
||||
offset += 1;
|
||||
|
||||
Reference in New Issue
Block a user