mirror of https://github.com/WerWolv/ImHex.git
Language refactoring, added builtin function registry
This commit is contained in:
parent
90e0aa83d8
commit
c09a8bca7f
|
@ -153,13 +153,6 @@ add_executable(imhex ${application_type}
|
|||
source/helpers/loader_script_handler.cpp
|
||||
source/helpers/plugin_handler.cpp
|
||||
|
||||
source/lang/preprocessor.cpp
|
||||
source/lang/lexer.cpp
|
||||
source/lang/parser.cpp
|
||||
source/lang/validator.cpp
|
||||
source/lang/evaluator.cpp
|
||||
source/lang/builtin_functions.cpp
|
||||
|
||||
source/providers/file_provider.cpp
|
||||
|
||||
source/views/view_hexeditor.cpp
|
||||
|
|
|
@ -16,6 +16,13 @@ add_library(libimhex STATIC
|
|||
source/helpers/utils.cpp
|
||||
source/helpers/content_registry.cpp
|
||||
|
||||
source/lang/preprocessor.cpp
|
||||
source/lang/lexer.cpp
|
||||
source/lang/parser.cpp
|
||||
source/lang/validator.cpp
|
||||
source/lang/evaluator.cpp
|
||||
source/lang/builtin_functions.cpp
|
||||
|
||||
source/providers/provider.cpp
|
||||
|
||||
source/views/view.cpp
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
|
||||
namespace hex {
|
||||
|
||||
namespace lang { class ASTNode; }
|
||||
|
||||
class ContentRegistry {
|
||||
public:
|
||||
ContentRegistry() = delete;
|
||||
|
@ -58,6 +60,23 @@ namespace hex {
|
|||
static void add(Type type, std::string_view command, std::string_view description, const std::function<std::string(std::string)> &callback);
|
||||
static std::vector<Entry> getEntries();
|
||||
};
|
||||
|
||||
struct PatternLanguageFunctions {
|
||||
PatternLanguageFunctions() = delete;
|
||||
|
||||
constexpr static u32 UnlimitedParameters = 0xFFFF'FFFF;
|
||||
constexpr static u32 MoreParametersThan = 0x8000'0000;
|
||||
constexpr static u32 LessParametersThan = 0x4000'0000;
|
||||
constexpr static u32 NoParameters = 0x0000'0000;
|
||||
|
||||
struct Function {
|
||||
u32 parameterCount;
|
||||
std::function<hex::lang::ASTNode*(std::vector<hex::lang::ASTNode*>)> func;
|
||||
};
|
||||
|
||||
static void add(std::string_view name, u32 parameterCount, const std::function<hex::lang::ASTNode*(std::vector<hex::lang::ASTNode*>)> &func);
|
||||
static std::map<std::string, ContentRegistry::PatternLanguageFunctions::Function> getEntries();
|
||||
};
|
||||
};
|
||||
|
||||
}
|
|
@ -59,6 +59,7 @@ namespace hex {
|
|||
static std::map<std::string, Events> customEventsStorage;
|
||||
static u32 customEventsLastIdStorage = u32(Events::Events_BuiltinEnd) + 1;
|
||||
static std::vector<ContentRegistry::CommandPaletteCommands::Entry> commandPaletteCommandsStorage;
|
||||
static std::map<std::string, ContentRegistry::PatternLanguageFunctions::Function> patternLanguageFunctionsStorage;
|
||||
|
||||
this->imguiContext = ImGui::GetCurrentContext();
|
||||
this->eventHandlers = &eventHandlersStorage;
|
||||
|
@ -72,6 +73,7 @@ namespace hex {
|
|||
this->customEvents = &customEventsStorage;
|
||||
this->customEventsLastId = &customEventsLastIdStorage;
|
||||
this->commandPaletteCommands = &commandPaletteCommandsStorage;
|
||||
this->patternLanguageFunctions = &patternLanguageFunctionsStorage;
|
||||
}
|
||||
|
||||
void initializeData(const SharedData &other) {
|
||||
|
@ -87,6 +89,7 @@ namespace hex {
|
|||
this->customEvents = other.customEvents;
|
||||
this->customEventsLastId = other.customEventsLastId;
|
||||
this->commandPaletteCommands = other.commandPaletteCommands;
|
||||
this->patternLanguageFunctions = other.patternLanguageFunctions;
|
||||
}
|
||||
|
||||
public:
|
||||
|
@ -99,6 +102,7 @@ namespace hex {
|
|||
std::map<std::string, Events> *customEvents;
|
||||
u32 *customEventsLastId;
|
||||
std::vector<ContentRegistry::CommandPaletteCommands::Entry> *commandPaletteCommands;
|
||||
std::map<std::string, ContentRegistry::PatternLanguageFunctions::Function> *patternLanguageFunctions;
|
||||
|
||||
ImVec2 *windowPos;
|
||||
ImVec2 *windowSize;
|
||||
|
|
|
@ -23,16 +23,6 @@ namespace hex::lang {
|
|||
Error
|
||||
};
|
||||
|
||||
struct Function {
|
||||
constexpr static u32 UnlimitedParameters = 0xFFFF'FFFF;
|
||||
constexpr static u32 MoreParametersThan = 0x8000'0000;
|
||||
constexpr static u32 LessParametersThan = 0x4000'0000;
|
||||
constexpr static u32 NoParameters = 0x0000'0000;
|
||||
|
||||
u32 parameterCount;
|
||||
std::function<ASTNodeIntegerLiteral*(std::vector<ASTNode*>)> func;
|
||||
};
|
||||
|
||||
Evaluator(prv::Provider* &provider, std::endian defaultDataEndian);
|
||||
|
||||
std::optional<std::vector<PatternData*>> evaluate(const std::vector<ASTNode*>& ast);
|
||||
|
@ -46,7 +36,6 @@ namespace hex::lang {
|
|||
std::vector<std::endian> m_endianStack;
|
||||
std::vector<PatternData*> m_globalMembers;
|
||||
std::vector<std::vector<PatternData*>*> m_currMembers;
|
||||
std::map<std::string, Function> m_functions;
|
||||
|
||||
std::vector<std::pair<ConsoleLogLevel, std::string>> m_consoleLog;
|
||||
|
||||
|
@ -72,16 +61,9 @@ namespace hex::lang {
|
|||
return this->m_endianStack.back();
|
||||
}
|
||||
|
||||
void addFunction(std::string_view name, u32 parameterCount, std::function<ASTNodeIntegerLiteral*(std::vector<ASTNode*>)> func) {
|
||||
if (this->m_functions.contains(name.data()))
|
||||
throwEvaluateError(hex::format("redefinition of function '%s'", name.data()));
|
||||
|
||||
this->m_functions[name.data()] = { parameterCount, func };
|
||||
}
|
||||
|
||||
ASTNodeIntegerLiteral* evaluateScopeResolution(ASTNodeScopeResolution *node);
|
||||
ASTNodeIntegerLiteral* evaluateRValue(ASTNodeRValue *node);
|
||||
ASTNodeIntegerLiteral* evaluateFunctionCall(ASTNodeFunctionCall *node);
|
||||
ASTNode* evaluateFunctionCall(ASTNodeFunctionCall *node);
|
||||
ASTNodeIntegerLiteral* evaluateOperator(ASTNodeIntegerLiteral *left, ASTNodeIntegerLiteral *right, Token::Operator op);
|
||||
ASTNodeIntegerLiteral* evaluateOperand(ASTNode *node);
|
||||
ASTNodeIntegerLiteral* evaluateTernaryExpression(ASTNodeTernaryExpression *node);
|
||||
|
@ -98,7 +80,6 @@ namespace hex::lang {
|
|||
PatternData* evaluateArray(ASTNodeArrayVariableDecl *node);
|
||||
PatternData* evaluatePointer(ASTNodePointerVariableDecl *node);
|
||||
|
||||
|
||||
template<typename T>
|
||||
T* asType(ASTNode *param) {
|
||||
if (auto evaluatedParam = dynamic_cast<T*>(param); evaluatedParam != nullptr)
|
||||
|
@ -107,7 +88,7 @@ namespace hex::lang {
|
|||
throwEvaluateError("function got wrong type of parameter");
|
||||
}
|
||||
|
||||
|
||||
void registerBuiltinFunctions();
|
||||
|
||||
#define BUILTIN_FUNCTION(name) ASTNodeIntegerLiteral* TOKEN_CONCAT(builtin_, name)(std::vector<ASTNode*> params)
|
||||
|
|
@ -2,12 +2,12 @@
|
|||
|
||||
#include <hex.hpp>
|
||||
|
||||
#include "imgui.h"
|
||||
#include "imgui_memory_editor.h"
|
||||
#include <imgui.h>
|
||||
|
||||
#include "providers/provider.hpp"
|
||||
#include "helpers/utils.hpp"
|
||||
#include "lang/token.hpp"
|
||||
#include "views/view.hpp"
|
||||
|
||||
#include <cstring>
|
||||
#include <random>
|
|
@ -73,4 +73,15 @@ namespace hex {
|
|||
return *SharedData::get().commandPaletteCommands;
|
||||
}
|
||||
|
||||
|
||||
/* Pattern Language Functions */
|
||||
|
||||
void ContentRegistry::PatternLanguageFunctions::add(std::string_view name, u32 parameterCount, const std::function<hex::lang::ASTNode*(std::vector<hex::lang::ASTNode*>)> &func) {
|
||||
(*SharedData::get().patternLanguageFunctions)[name.data()] = Function{ parameterCount, func };
|
||||
}
|
||||
|
||||
std::map<std::string, ContentRegistry::PatternLanguageFunctions::Function> ContentRegistry::PatternLanguageFunctions::getEntries() {
|
||||
return *SharedData::get().patternLanguageFunctions;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,143 @@
|
|||
#include "lang/evaluator.hpp"
|
||||
#include "helpers/content_registry.hpp"
|
||||
|
||||
namespace hex::lang {
|
||||
|
||||
#define LITERAL_COMPARE(literal, cond) std::visit([&, this](auto &&literal) { return (cond) != 0; }, literal)
|
||||
|
||||
void Evaluator::registerBuiltinFunctions() {
|
||||
/* findSequence */
|
||||
ContentRegistry::PatternLanguageFunctions::add("findSequence", ContentRegistry::PatternLanguageFunctions::MoreParametersThan | 1, [this](auto params) {
|
||||
auto& occurrenceIndex = asType<ASTNodeIntegerLiteral>(params[0])->getValue();
|
||||
std::vector<u8> sequence;
|
||||
for (u32 i = 1; i < params.size(); i++) {
|
||||
sequence.push_back(std::visit([](auto &&value) -> u8 {
|
||||
if (value <= 0xFF)
|
||||
return value;
|
||||
else
|
||||
throwEvaluateError("sequence bytes need to fit into 1 byte");
|
||||
}, asType<ASTNodeIntegerLiteral>(params[i])->getValue()));
|
||||
}
|
||||
|
||||
std::vector<u8> bytes(sequence.size(), 0x00);
|
||||
u32 occurrences = 0;
|
||||
for (u64 offset = 0; offset < this->m_provider->getSize() - sequence.size(); offset++) {
|
||||
this->m_provider->read(offset, bytes.data(), bytes.size());
|
||||
|
||||
if (bytes == sequence) {
|
||||
if (LITERAL_COMPARE(occurrenceIndex, occurrences < occurrenceIndex)) {
|
||||
occurrences++;
|
||||
continue;
|
||||
}
|
||||
|
||||
return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned64Bit, offset });
|
||||
}
|
||||
}
|
||||
|
||||
throwEvaluateError("failed to find sequence");
|
||||
});
|
||||
|
||||
/* assert */
|
||||
ContentRegistry::PatternLanguageFunctions::add("readUnsigned", 2, [this](auto params) {
|
||||
auto address = asType<ASTNodeIntegerLiteral>(params[0])->getValue();
|
||||
auto size = asType<ASTNodeIntegerLiteral>(params[1])->getValue();
|
||||
|
||||
if (LITERAL_COMPARE(address, address >= this->m_provider->getActualSize()))
|
||||
throwEvaluateError("address out of range");
|
||||
|
||||
return std::visit([this](auto &&address, auto &&size) {
|
||||
if (size <= 0 || size > 16)
|
||||
throwEvaluateError("invalid read size");
|
||||
|
||||
u8 value[(u8)size];
|
||||
this->m_provider->read(address, value, size);
|
||||
|
||||
switch ((u8)size) {
|
||||
case 1: return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned8Bit, hex::changeEndianess(*reinterpret_cast<u8*>(value), 1, this->getCurrentEndian()) });
|
||||
case 2: return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned16Bit, hex::changeEndianess(*reinterpret_cast<u16*>(value), 2, this->getCurrentEndian()) });
|
||||
case 4: return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned32Bit, hex::changeEndianess(*reinterpret_cast<u32*>(value), 4, this->getCurrentEndian()) });
|
||||
case 8: return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned64Bit, hex::changeEndianess(*reinterpret_cast<u64*>(value), 8, this->getCurrentEndian()) });
|
||||
case 16: return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned128Bit, hex::changeEndianess(*reinterpret_cast<u128*>(value), 16, this->getCurrentEndian()) });
|
||||
default: throwEvaluateError("invalid read size");
|
||||
}
|
||||
}, address, size);
|
||||
});
|
||||
|
||||
ContentRegistry::PatternLanguageFunctions::add("readSigned", 2, [this](auto params) {
|
||||
auto address = asType<ASTNodeIntegerLiteral>(params[0])->getValue();
|
||||
auto size = asType<ASTNodeIntegerLiteral>(params[1])->getValue();
|
||||
|
||||
if (LITERAL_COMPARE(address, address >= this->m_provider->getActualSize()))
|
||||
throwEvaluateError("address out of range");
|
||||
|
||||
return std::visit([this](auto &&address, auto &&size) {
|
||||
if (size <= 0 || size > 16)
|
||||
throwEvaluateError("invalid read size");
|
||||
|
||||
u8 value[(u8)size];
|
||||
this->m_provider->read(address, value, size);
|
||||
|
||||
switch ((u8)size) {
|
||||
case 1: return new ASTNodeIntegerLiteral({ Token::ValueType::Signed8Bit, hex::changeEndianess(*reinterpret_cast<s8*>(value), 1, this->getCurrentEndian()) });
|
||||
case 2: return new ASTNodeIntegerLiteral({ Token::ValueType::Signed16Bit, hex::changeEndianess(*reinterpret_cast<s16*>(value), 2, this->getCurrentEndian()) });
|
||||
case 4: return new ASTNodeIntegerLiteral({ Token::ValueType::Signed32Bit, hex::changeEndianess(*reinterpret_cast<s32*>(value), 4, this->getCurrentEndian()) });
|
||||
case 8: return new ASTNodeIntegerLiteral({ Token::ValueType::Signed64Bit, hex::changeEndianess(*reinterpret_cast<s64*>(value), 8, this->getCurrentEndian()) });
|
||||
case 16: return new ASTNodeIntegerLiteral({ Token::ValueType::Signed128Bit, hex::changeEndianess(*reinterpret_cast<s128*>(value), 16, this->getCurrentEndian()) });
|
||||
default: throwEvaluateError("invalid read size");
|
||||
}
|
||||
}, address, size);
|
||||
});
|
||||
|
||||
ContentRegistry::PatternLanguageFunctions::add("assert", 2, [this](auto params) {
|
||||
auto condition = asType<ASTNodeIntegerLiteral>(params[0])->getValue();
|
||||
auto message = asType<ASTNodeStringLiteral>(params[1])->getString();
|
||||
|
||||
if (LITERAL_COMPARE(condition, condition == 0))
|
||||
throwEvaluateError(hex::format("assert failed \"%s\"", message.data()));
|
||||
|
||||
return nullptr;
|
||||
});
|
||||
|
||||
ContentRegistry::PatternLanguageFunctions::add("warnAssert", 2, [this](auto params) {
|
||||
auto condition = asType<ASTNodeIntegerLiteral>(params[0])->getValue();
|
||||
auto message = asType<ASTNodeStringLiteral>(params[1])->getString();
|
||||
|
||||
if (LITERAL_COMPARE(condition, condition == 0))
|
||||
this->emmitWaring(hex::format("assert failed \"%s\"", message.data()));
|
||||
|
||||
return nullptr;
|
||||
});
|
||||
|
||||
ContentRegistry::PatternLanguageFunctions::add("print", ContentRegistry::PatternLanguageFunctions::MoreParametersThan | 0, [this](auto params) {
|
||||
std::string message;
|
||||
for (auto& param : params) {
|
||||
if (auto integerLiteral = dynamic_cast<ASTNodeIntegerLiteral*>(param); integerLiteral != nullptr) {
|
||||
switch (integerLiteral->getType()) {
|
||||
case Token::ValueType::Character: message += std::get<s8>(integerLiteral->getValue()); break;
|
||||
case Token::ValueType::Unsigned8Bit: message += std::to_string(std::get<u8>(integerLiteral->getValue())); break;
|
||||
case Token::ValueType::Signed8Bit: message += std::to_string(std::get<s8>(integerLiteral->getValue())); break;
|
||||
case Token::ValueType::Unsigned16Bit: message += std::to_string(std::get<u16>(integerLiteral->getValue())); break;
|
||||
case Token::ValueType::Signed16Bit: message += std::to_string(std::get<s16>(integerLiteral->getValue())); break;
|
||||
case Token::ValueType::Unsigned32Bit: message += std::to_string(std::get<u32>(integerLiteral->getValue())); break;
|
||||
case Token::ValueType::Signed32Bit: message += std::to_string(std::get<s32>(integerLiteral->getValue())); break;
|
||||
case Token::ValueType::Unsigned64Bit: message += std::to_string(std::get<u64>(integerLiteral->getValue())); break;
|
||||
case Token::ValueType::Signed64Bit: message += std::to_string(std::get<s64>(integerLiteral->getValue())); break;
|
||||
case Token::ValueType::Unsigned128Bit: message += "A lot"; break; // TODO: Implement u128 to_string
|
||||
case Token::ValueType::Signed128Bit: message += "A lot"; break; // TODO: Implement s128 to_string
|
||||
case Token::ValueType::Float: message += std::to_string(std::get<float>(integerLiteral->getValue())); break;
|
||||
case Token::ValueType::Double: message += std::to_string(std::get<double>(integerLiteral->getValue())); break;
|
||||
case Token::ValueType::Boolean: message += std::get<s32>(integerLiteral->getValue()) ? "true" : "false"; break;
|
||||
case Token::ValueType::CustomType: message += "< Custom Type >"; break;
|
||||
}
|
||||
}
|
||||
else if (auto stringLiteral = dynamic_cast<ASTNodeStringLiteral*>(param); stringLiteral != nullptr)
|
||||
message += stringLiteral->getString();
|
||||
}
|
||||
|
||||
this->emmitInfo(message);
|
||||
|
||||
return nullptr;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include "lang/token.hpp"
|
||||
#include "helpers/utils.hpp"
|
||||
#include "helpers/content_registry.hpp"
|
||||
|
||||
#include <bit>
|
||||
#include <algorithm>
|
||||
|
@ -13,29 +14,7 @@ namespace hex::lang {
|
|||
Evaluator::Evaluator(prv::Provider* &provider, std::endian defaultDataEndian)
|
||||
: m_provider(provider), m_defaultDataEndian(defaultDataEndian) {
|
||||
|
||||
this->addFunction("findSequence", Function::MoreParametersThan | 1, [this](auto params) {
|
||||
return this->builtin_findSequence(params);
|
||||
});
|
||||
|
||||
this->addFunction("readUnsigned", 2, [this](auto params) {
|
||||
return this->builtin_readUnsigned(params);
|
||||
});
|
||||
|
||||
this->addFunction("readSigned", 2, [this](auto params) {
|
||||
return this->builtin_readSigned(params);
|
||||
});
|
||||
|
||||
this->addFunction("assert", 2, [this](auto params) {
|
||||
return this->builtin_assert(params);
|
||||
});
|
||||
|
||||
this->addFunction("warnAssert", 2, [this](auto params) {
|
||||
return this->builtin_warnAssert(params);
|
||||
});
|
||||
|
||||
this->addFunction("print", Function::MoreParametersThan | 0, [this](auto params) {
|
||||
return this->builtin_print(params);
|
||||
});
|
||||
this->registerBuiltinFunctions();
|
||||
}
|
||||
|
||||
ASTNodeIntegerLiteral* Evaluator::evaluateScopeResolution(ASTNodeScopeResolution *node) {
|
||||
|
@ -137,7 +116,7 @@ namespace hex::lang {
|
|||
throwEvaluateError("tried to use non-integer value in numeric expression");
|
||||
}
|
||||
|
||||
ASTNodeIntegerLiteral* Evaluator::evaluateFunctionCall(ASTNodeFunctionCall *node) {
|
||||
ASTNode* Evaluator::evaluateFunctionCall(ASTNodeFunctionCall *node) {
|
||||
std::vector<ASTNode*> evaluatedParams;
|
||||
ScopeExit paramCleanup([&] {
|
||||
for (auto ¶m : evaluatedParams)
|
||||
|
@ -151,20 +130,20 @@ namespace hex::lang {
|
|||
evaluatedParams.push_back(stringLiteral->clone());
|
||||
}
|
||||
|
||||
if (!this->m_functions.contains(node->getFunctionName().data()))
|
||||
if (!ContentRegistry::PatternLanguageFunctions::getEntries().contains(node->getFunctionName().data()))
|
||||
throwEvaluateError(hex::format("no function named '%s' found", node->getFunctionName().data()));
|
||||
|
||||
auto &function = this->m_functions[node->getFunctionName().data()];
|
||||
auto &function = ContentRegistry::PatternLanguageFunctions::getEntries()[node->getFunctionName().data()];
|
||||
|
||||
if (function.parameterCount == Function::UnlimitedParameters) {
|
||||
if (function.parameterCount == ContentRegistry::PatternLanguageFunctions::UnlimitedParameters) {
|
||||
; // Don't check parameter count
|
||||
}
|
||||
else if (function.parameterCount & Function::LessParametersThan) {
|
||||
if (evaluatedParams.size() >= (function.parameterCount & ~Function::LessParametersThan))
|
||||
throwEvaluateError(hex::format("too many parameters for function '%s'. Expected %d", node->getFunctionName().data(), function.parameterCount & ~Function::LessParametersThan));
|
||||
} else if (function.parameterCount & Function::MoreParametersThan) {
|
||||
if (evaluatedParams.size() <= (function.parameterCount & ~Function::MoreParametersThan))
|
||||
throwEvaluateError(hex::format("too few parameters for function '%s'. Expected %d", node->getFunctionName().data(), function.parameterCount & ~Function::MoreParametersThan));
|
||||
else if (function.parameterCount & ContentRegistry::PatternLanguageFunctions::LessParametersThan) {
|
||||
if (evaluatedParams.size() >= (function.parameterCount & ~ContentRegistry::PatternLanguageFunctions::LessParametersThan))
|
||||
throwEvaluateError(hex::format("too many parameters for function '%s'. Expected %d", node->getFunctionName().data(), function.parameterCount & ~ContentRegistry::PatternLanguageFunctions::LessParametersThan));
|
||||
} else if (function.parameterCount & ContentRegistry::PatternLanguageFunctions::MoreParametersThan) {
|
||||
if (evaluatedParams.size() <= (function.parameterCount & ~ContentRegistry::PatternLanguageFunctions::MoreParametersThan))
|
||||
throwEvaluateError(hex::format("too few parameters for function '%s'. Expected %d", node->getFunctionName().data(), function.parameterCount & ~ContentRegistry::PatternLanguageFunctions::MoreParametersThan));
|
||||
} else if (function.parameterCount != evaluatedParams.size()) {
|
||||
throwEvaluateError(hex::format("invalid number of parameters for function '%s'. Expected %d", node->getFunctionName().data(), function.parameterCount));
|
||||
}
|
||||
|
@ -305,8 +284,10 @@ namespace hex::lang {
|
|||
|
||||
if (returnValue == nullptr)
|
||||
throwEvaluateError("function returning void used in expression");
|
||||
else if (auto integerNode = dynamic_cast<ASTNodeIntegerLiteral*>(returnValue); integerNode != nullptr)
|
||||
return integerNode;
|
||||
else
|
||||
return returnValue;
|
||||
throwEvaluateError("function not returning a numeric value used in expression");
|
||||
}
|
||||
else
|
||||
throwEvaluateError("invalid operand");
|
|
@ -1,144 +0,0 @@
|
|||
#include "lang/evaluator.hpp"
|
||||
|
||||
namespace hex::lang {
|
||||
|
||||
#define BUILTIN_FUNCTION(name) ASTNodeIntegerLiteral* Evaluator::TOKEN_CONCAT(builtin_, name)(std::vector<ASTNode*> params)
|
||||
|
||||
#define LITERAL_COMPARE(literal, cond) std::visit([&, this](auto &&literal) { return (cond) != 0; }, literal)
|
||||
|
||||
BUILTIN_FUNCTION(findSequence) {
|
||||
auto& occurrenceIndex = asType<ASTNodeIntegerLiteral>(params[0])->getValue();
|
||||
std::vector<u8> sequence;
|
||||
for (u32 i = 1; i < params.size(); i++) {
|
||||
sequence.push_back(std::visit([](auto &&value) -> u8 {
|
||||
if (value <= 0xFF)
|
||||
return value;
|
||||
else
|
||||
throwEvaluateError("sequence bytes need to fit into 1 byte");
|
||||
}, asType<ASTNodeIntegerLiteral>(params[i])->getValue()));
|
||||
}
|
||||
|
||||
std::vector<u8> bytes(sequence.size(), 0x00);
|
||||
u32 occurrences = 0;
|
||||
for (u64 offset = 0; offset < this->m_provider->getSize() - sequence.size(); offset++) {
|
||||
this->m_provider->read(offset, bytes.data(), bytes.size());
|
||||
|
||||
if (bytes == sequence) {
|
||||
if (LITERAL_COMPARE(occurrenceIndex, occurrences < occurrenceIndex)) {
|
||||
occurrences++;
|
||||
continue;
|
||||
}
|
||||
|
||||
return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned64Bit, offset });
|
||||
}
|
||||
}
|
||||
|
||||
throwEvaluateError("failed to find sequence");
|
||||
}
|
||||
|
||||
BUILTIN_FUNCTION(readUnsigned) {
|
||||
auto address = asType<ASTNodeIntegerLiteral>(params[0])->getValue();
|
||||
auto size = asType<ASTNodeIntegerLiteral>(params[1])->getValue();
|
||||
|
||||
if (LITERAL_COMPARE(address, address >= this->m_provider->getActualSize()))
|
||||
throwEvaluateError("address out of range");
|
||||
|
||||
return std::visit([this](auto &&address, auto &&size) {
|
||||
if (size <= 0 || size > 16)
|
||||
throwEvaluateError("invalid read size");
|
||||
|
||||
u8 value[(u8)size];
|
||||
this->m_provider->read(address, value, size);
|
||||
|
||||
switch ((u8)size) {
|
||||
case 1: return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned8Bit, hex::changeEndianess(*reinterpret_cast<u8*>(value), 1, this->getCurrentEndian()) });
|
||||
case 2: return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned16Bit, hex::changeEndianess(*reinterpret_cast<u16*>(value), 2, this->getCurrentEndian()) });
|
||||
case 4: return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned32Bit, hex::changeEndianess(*reinterpret_cast<u32*>(value), 4, this->getCurrentEndian()) });
|
||||
case 8: return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned64Bit, hex::changeEndianess(*reinterpret_cast<u64*>(value), 8, this->getCurrentEndian()) });
|
||||
case 16: return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned128Bit, hex::changeEndianess(*reinterpret_cast<u128*>(value), 16, this->getCurrentEndian()) });
|
||||
default: throwEvaluateError("invalid read size");
|
||||
}
|
||||
}, address, size);
|
||||
}
|
||||
|
||||
BUILTIN_FUNCTION(readSigned) {
|
||||
auto address = asType<ASTNodeIntegerLiteral>(params[0])->getValue();
|
||||
auto size = asType<ASTNodeIntegerLiteral>(params[1])->getValue();
|
||||
|
||||
if (LITERAL_COMPARE(address, address >= this->m_provider->getActualSize()))
|
||||
throwEvaluateError("address out of range");
|
||||
|
||||
return std::visit([this](auto &&address, auto &&size) {
|
||||
if (size <= 0 || size > 16)
|
||||
throwEvaluateError("invalid read size");
|
||||
|
||||
u8 value[(u8)size];
|
||||
this->m_provider->read(address, value, size);
|
||||
|
||||
switch ((u8)size) {
|
||||
case 1: return new ASTNodeIntegerLiteral({ Token::ValueType::Signed8Bit, hex::changeEndianess(*reinterpret_cast<s8*>(value), 1, this->getCurrentEndian()) });
|
||||
case 2: return new ASTNodeIntegerLiteral({ Token::ValueType::Signed16Bit, hex::changeEndianess(*reinterpret_cast<s16*>(value), 2, this->getCurrentEndian()) });
|
||||
case 4: return new ASTNodeIntegerLiteral({ Token::ValueType::Signed32Bit, hex::changeEndianess(*reinterpret_cast<s32*>(value), 4, this->getCurrentEndian()) });
|
||||
case 8: return new ASTNodeIntegerLiteral({ Token::ValueType::Signed64Bit, hex::changeEndianess(*reinterpret_cast<s64*>(value), 8, this->getCurrentEndian()) });
|
||||
case 16: return new ASTNodeIntegerLiteral({ Token::ValueType::Signed128Bit, hex::changeEndianess(*reinterpret_cast<s128*>(value), 16, this->getCurrentEndian()) });
|
||||
default: throwEvaluateError("invalid read size");
|
||||
}
|
||||
}, address, size);
|
||||
}
|
||||
|
||||
BUILTIN_FUNCTION(assert) {
|
||||
auto condition = asType<ASTNodeIntegerLiteral>(params[0])->getValue();
|
||||
auto message = asType<ASTNodeStringLiteral>(params[1])->getString();
|
||||
|
||||
if (LITERAL_COMPARE(condition, condition == 0))
|
||||
throwEvaluateError(hex::format("assert failed \"%s\"", message.data()));
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BUILTIN_FUNCTION(warnAssert) {
|
||||
auto condition = asType<ASTNodeIntegerLiteral>(params[0])->getValue();
|
||||
auto message = asType<ASTNodeStringLiteral>(params[1])->getString();
|
||||
|
||||
if (LITERAL_COMPARE(condition, condition == 0))
|
||||
this->emmitWaring(hex::format("assert failed \"%s\"", message.data()));
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BUILTIN_FUNCTION(print) {
|
||||
|
||||
std::string message;
|
||||
for (auto& param : params) {
|
||||
if (auto integerLiteral = dynamic_cast<ASTNodeIntegerLiteral*>(param); integerLiteral != nullptr) {
|
||||
switch (integerLiteral->getType()) {
|
||||
case Token::ValueType::Character: message += std::get<s8>(integerLiteral->getValue()); break;
|
||||
case Token::ValueType::Unsigned8Bit: message += std::to_string(std::get<u8>(integerLiteral->getValue())); break;
|
||||
case Token::ValueType::Signed8Bit: message += std::to_string(std::get<s8>(integerLiteral->getValue())); break;
|
||||
case Token::ValueType::Unsigned16Bit: message += std::to_string(std::get<u16>(integerLiteral->getValue())); break;
|
||||
case Token::ValueType::Signed16Bit: message += std::to_string(std::get<s16>(integerLiteral->getValue())); break;
|
||||
case Token::ValueType::Unsigned32Bit: message += std::to_string(std::get<u32>(integerLiteral->getValue())); break;
|
||||
case Token::ValueType::Signed32Bit: message += std::to_string(std::get<s32>(integerLiteral->getValue())); break;
|
||||
case Token::ValueType::Unsigned64Bit: message += std::to_string(std::get<u64>(integerLiteral->getValue())); break;
|
||||
case Token::ValueType::Signed64Bit: message += std::to_string(std::get<s64>(integerLiteral->getValue())); break;
|
||||
case Token::ValueType::Unsigned128Bit: message += "A lot"; break; // TODO: Implement u128 to_string
|
||||
case Token::ValueType::Signed128Bit: message += "A lot"; break; // TODO: Implement s128 to_string
|
||||
case Token::ValueType::Float: message += std::to_string(std::get<float>(integerLiteral->getValue())); break;
|
||||
case Token::ValueType::Double: message += std::to_string(std::get<double>(integerLiteral->getValue())); break;
|
||||
case Token::ValueType::Boolean: message += std::get<s32>(integerLiteral->getValue()) ? "true" : "false"; break;
|
||||
case Token::ValueType::CustomType: message += "< Custom Type >"; break;
|
||||
}
|
||||
}
|
||||
else if (auto stringLiteral = dynamic_cast<ASTNodeStringLiteral*>(param); stringLiteral != nullptr)
|
||||
message += stringLiteral->getString();
|
||||
}
|
||||
|
||||
this->emmitInfo(message);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
#undef BUILTIN_FUNCTION
|
||||
|
||||
}
|
Loading…
Reference in New Issue