mirror of
https://github.com/WerWolv/ImHex.git
synced 2026-04-01 21:17:44 -05:00
Huge refactoring of builtin features into an external plugin
This commit is contained in:
31
plugins/builtin/source/content/command_palette_commands.cpp
Normal file
31
plugins/builtin/source/content/command_palette_commands.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
#include <hex/plugin.hpp>
|
||||
|
||||
#include "math_evaluator.hpp"
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
void registerCommandPaletteCommands() {
|
||||
|
||||
hex::ContentRegistry::CommandPaletteCommands::add(
|
||||
hex::ContentRegistry::CommandPaletteCommands::Type::SymbolCommand,
|
||||
"#", "Calculator",
|
||||
[](auto input) {
|
||||
hex::MathEvaluator evaluator;
|
||||
evaluator.registerStandardVariables();
|
||||
evaluator.registerStandardFunctions();
|
||||
|
||||
std::optional<long double> result;
|
||||
|
||||
try {
|
||||
result = evaluator.evaluate(input);
|
||||
} catch (std::runtime_error &e) {}
|
||||
|
||||
if (result.has_value())
|
||||
return hex::format("#%s = %Lf", input.data(), result.value());
|
||||
else
|
||||
return hex::format("#%s = ???", input.data());
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
188
plugins/builtin/source/content/data_inspector.cpp
Normal file
188
plugins/builtin/source/content/data_inspector.cpp
Normal file
@@ -0,0 +1,188 @@
|
||||
#include <hex/plugin.hpp>
|
||||
|
||||
#include <hex/helpers/utils.hpp>
|
||||
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
|
||||
#include <imgui_internal.h>
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
struct GUID {
|
||||
u32 data1;
|
||||
u16 data2;
|
||||
u16 data3;
|
||||
u8 data4[8];
|
||||
};
|
||||
|
||||
void registerDataInspectorEntries() {
|
||||
|
||||
using Style = hex::ContentRegistry::DataInspector::NumberDisplayStyle;
|
||||
|
||||
hex::ContentRegistry::DataInspector::add("Binary (8 bit)", sizeof(u8), [](auto buffer, auto endian, auto style) {
|
||||
std::string binary;
|
||||
for (u8 i = 0; i < 8; i++)
|
||||
binary += ((buffer[0] << i) & 0x80) == 0 ? '0' : '1';
|
||||
|
||||
return [binary] { ImGui::TextUnformatted(binary.c_str()); };
|
||||
});
|
||||
|
||||
hex::ContentRegistry::DataInspector::add("uint8_t", sizeof(u8), [](auto buffer, auto endian, auto style) {
|
||||
auto format = (style == Style::Decimal) ? "%u" : ((style == Style::Hexadecimal) ? "0x%X" : "0o%o");
|
||||
auto value = hex::format(format, *reinterpret_cast<u8*>(buffer.data()));
|
||||
return [value] { ImGui::TextUnformatted(value.c_str()); };
|
||||
});
|
||||
|
||||
hex::ContentRegistry::DataInspector::add("int8_t", sizeof(s8), [](auto buffer, auto endian, auto style) {
|
||||
auto format = (style == Style::Decimal) ? "%d" : ((style == Style::Hexadecimal) ? "0x%X" : "0o%o");
|
||||
auto value = hex::format(format, *reinterpret_cast<s8*>(buffer.data()));
|
||||
return [value] { ImGui::TextUnformatted(value.c_str()); };
|
||||
});
|
||||
|
||||
hex::ContentRegistry::DataInspector::add("uint16_t", sizeof(u16), [](auto buffer, auto endian, auto style) {
|
||||
auto format = (style == Style::Decimal) ? "%u" : ((style == Style::Hexadecimal) ? "0x%X" : "0o%o");
|
||||
auto value = hex::format(format, hex::changeEndianess(*reinterpret_cast<u16*>(buffer.data()), endian));
|
||||
return [value] { ImGui::TextUnformatted(value.c_str()); };
|
||||
});
|
||||
|
||||
hex::ContentRegistry::DataInspector::add("int16_t", sizeof(s16), [](auto buffer, auto endian, auto style) {
|
||||
auto format = (style == Style::Decimal) ? "%d" : ((style == Style::Hexadecimal) ? "0x%X" : "0o%o");
|
||||
auto value = hex::format(format, hex::changeEndianess(*reinterpret_cast<s16*>(buffer.data()), endian));
|
||||
return [value] { ImGui::TextUnformatted(value.c_str()); };
|
||||
});
|
||||
|
||||
hex::ContentRegistry::DataInspector::add("uint32_t", sizeof(u32), [](auto buffer, auto endian, auto style) {
|
||||
auto format = (style == Style::Decimal) ? "%u" : ((style == Style::Hexadecimal) ? "0x%X" : "0o%o");
|
||||
auto value = hex::format(format, hex::changeEndianess(*reinterpret_cast<u32*>(buffer.data()), endian));
|
||||
return [value] { ImGui::TextUnformatted(value.c_str()); };
|
||||
});
|
||||
|
||||
hex::ContentRegistry::DataInspector::add("int32_t", sizeof(s32), [](auto buffer, auto endian, auto style) {
|
||||
auto format = (style == Style::Decimal) ? "%d" : ((style == Style::Hexadecimal) ? "0x%X" : "0o%o");
|
||||
auto value = hex::format(format, hex::changeEndianess(*reinterpret_cast<s32*>(buffer.data()), endian));
|
||||
return [value] { ImGui::TextUnformatted(value.c_str()); };
|
||||
});
|
||||
|
||||
hex::ContentRegistry::DataInspector::add("uint64_t", sizeof(u64), [](auto buffer, auto endian, auto style) {
|
||||
auto format = (style == Style::Decimal) ? "%lu" : ((style == Style::Hexadecimal) ? "0x%lX" : "0o%lo");
|
||||
auto value = hex::format(format, hex::changeEndianess(*reinterpret_cast<u64*>(buffer.data()), endian));
|
||||
return [value] { ImGui::TextUnformatted(value.c_str()); };
|
||||
});
|
||||
|
||||
hex::ContentRegistry::DataInspector::add("int64_t", sizeof(s64), [](auto buffer, auto endian, auto style) {
|
||||
auto format = (style == Style::Decimal) ? "%ld" : ((style == Style::Hexadecimal) ? "0x%lX" : "0o%lo");
|
||||
auto value = hex::format(format, hex::changeEndianess(*reinterpret_cast<s64*>(buffer.data()), endian));
|
||||
return [value] { ImGui::TextUnformatted(value.c_str()); };
|
||||
});
|
||||
|
||||
hex::ContentRegistry::DataInspector::add("float (32 bit)", sizeof(float), [](auto buffer, auto endian, auto style) {
|
||||
auto value = hex::format("%e", hex::changeEndianess(*reinterpret_cast<float*>(buffer.data()), endian));
|
||||
return [value] { ImGui::TextUnformatted(value.c_str()); };
|
||||
});
|
||||
|
||||
hex::ContentRegistry::DataInspector::add("double (64 bit)", sizeof(double), [](auto buffer, auto endian, auto style) {
|
||||
auto value = hex::format("%e", hex::changeEndianess(*reinterpret_cast<double*>(buffer.data()), endian));
|
||||
return [value] { ImGui::TextUnformatted(value.c_str()); };
|
||||
});
|
||||
|
||||
hex::ContentRegistry::DataInspector::add("ASCII Character", sizeof(char8_t), [](auto buffer, auto endian, auto style) {
|
||||
auto value = hex::format("'%s'", makePrintable(*reinterpret_cast<char8_t*>(buffer.data())).c_str());
|
||||
return [value] { ImGui::TextUnformatted(value.c_str()); };
|
||||
});
|
||||
|
||||
hex::ContentRegistry::DataInspector::add("Wide Character", sizeof(char16_t), [](auto buffer, auto endian, auto style) {
|
||||
auto c = *reinterpret_cast<char16_t*>(buffer.data());
|
||||
auto value = hex::format("'%lc'", c == 0 ? '\x01' : hex::changeEndianess(c, endian));
|
||||
return [value] { ImGui::TextUnformatted(value.c_str()); };
|
||||
});
|
||||
|
||||
hex::ContentRegistry::DataInspector::add("UTF-8 code point", sizeof(char8_t) * 4, [](auto buffer, auto endian, auto style) {
|
||||
char utf8Buffer[5] = { 0 };
|
||||
char codepointString[5] = { 0 };
|
||||
u32 codepoint = 0;
|
||||
|
||||
std::memcpy(utf8Buffer, reinterpret_cast<char8_t*>(buffer.data()), 4);
|
||||
u8 codepointSize = ImTextCharFromUtf8(&codepoint, utf8Buffer, utf8Buffer + 4);
|
||||
|
||||
std::memcpy(codepointString, &codepoint, std::min(codepointSize, u8(4)));
|
||||
auto value = hex::format("'%s' (U+%04lx)", codepoint == 0xFFFD ? "Invalid" :
|
||||
codepoint < 0xFF ? makePrintable(codepoint).c_str() :
|
||||
codepointString,
|
||||
codepoint);
|
||||
|
||||
return [value] { ImGui::TextUnformatted(value.c_str()); };
|
||||
});
|
||||
|
||||
#if defined(OS_WINDOWS) && defined(ARCH_64_BIT)
|
||||
|
||||
hex::ContentRegistry::DataInspector::add("__time32_t", sizeof(__time32_t), [](auto buffer, auto endian, auto style) {
|
||||
auto endianAdjustedTime = hex::changeEndianess(*reinterpret_cast<__time32_t*>(buffer.data()), endian);
|
||||
std::tm * ptm = _localtime32(&endianAdjustedTime);
|
||||
char timeBuffer[32];
|
||||
std::string value;
|
||||
if (ptm != nullptr && std::strftime(timeBuffer, 32, "%a, %d.%m.%Y %H:%M:%S", ptm))
|
||||
value = timeBuffer;
|
||||
else
|
||||
value = "Invalid";
|
||||
|
||||
return [value] { ImGui::TextUnformatted(value.c_str()); };
|
||||
});
|
||||
|
||||
hex::ContentRegistry::DataInspector::add("__time64_t", sizeof(__time64_t), [](auto buffer, auto endian, auto style) {
|
||||
auto endianAdjustedTime = hex::changeEndianess(*reinterpret_cast<__time64_t*>(buffer.data()), endian);
|
||||
std::tm * ptm = _localtime64(&endianAdjustedTime);
|
||||
char timeBuffer[64];
|
||||
std::string value;
|
||||
if (ptm != nullptr && std::strftime(timeBuffer, 64, "%a, %d.%m.%Y %H:%M:%S", ptm))
|
||||
value = timeBuffer;
|
||||
else
|
||||
value = "Invalid";
|
||||
|
||||
return [value] { ImGui::TextUnformatted(value.c_str()); };
|
||||
});
|
||||
|
||||
#else
|
||||
|
||||
hex::ContentRegistry::DataInspector::add("time_t", sizeof(time_t), [](auto buffer, auto endian, auto style) {
|
||||
auto endianAdjustedTime = hex::changeEndianess(*reinterpret_cast<time_t*>(buffer.data()), endian);
|
||||
std::tm * ptm = localtime(&endianAdjustedTime);
|
||||
char timeBuffer[64];
|
||||
std::string value;
|
||||
if (ptm != nullptr && std::strftime(timeBuffer, 64, "%a, %d.%m.%Y %H:%M:%S", ptm))
|
||||
value = timeBuffer;
|
||||
else
|
||||
value = "Invalid";
|
||||
|
||||
return [value] { ImGui::TextUnformatted(value.c_str()); };
|
||||
});
|
||||
|
||||
#endif
|
||||
|
||||
hex::ContentRegistry::DataInspector::add("GUID", sizeof(GUID), [](auto buffer, auto endian, auto style) {
|
||||
GUID guid;
|
||||
std::memcpy(&guid, buffer.data(), sizeof(GUID));
|
||||
auto value = hex::format("%s{%08lX-%04hX-%04hX-%02hhX%02hhX-%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX}",
|
||||
(hex::changeEndianess(guid.data3, endian) >> 12) <= 5 && ((guid.data4[0] >> 4) >= 8 || (guid.data4[0] >> 4) == 0) ? "" : "Invalid ",
|
||||
hex::changeEndianess(guid.data1, endian),
|
||||
hex::changeEndianess(guid.data2, endian),
|
||||
hex::changeEndianess(guid.data3, endian),
|
||||
guid.data4[0], guid.data4[1], guid.data4[2], guid.data4[3],
|
||||
guid.data4[4], guid.data4[5], guid.data4[6], guid.data4[7]);
|
||||
|
||||
return [value] { ImGui::TextUnformatted(value.c_str()); };
|
||||
});
|
||||
|
||||
hex::ContentRegistry::DataInspector::add("RGBA Color", sizeof(u32), [](auto buffer, auto endian, auto style) {
|
||||
ImColor value(hex::changeEndianess(*reinterpret_cast<u32*>(buffer.data()), endian));
|
||||
|
||||
return [value] {
|
||||
ImGui::ColorButton("##inspectorColor", value,
|
||||
ImGuiColorEditFlags_None,
|
||||
ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight()));
|
||||
};
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
200
plugins/builtin/source/content/lang_builtin_functions.cpp
Normal file
200
plugins/builtin/source/content/lang_builtin_functions.cpp
Normal file
@@ -0,0 +1,200 @@
|
||||
#include <hex/plugin.hpp>
|
||||
|
||||
#include <hex/lang/ast_node.hpp>
|
||||
#include <hex/lang/log_console.hpp>
|
||||
#include <hex/lang/evaluator.hpp>
|
||||
|
||||
#include <hex/helpers/utils.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
#define LITERAL_COMPARE(literal, cond) std::visit([&](auto &&literal) { return (cond) != 0; }, literal)
|
||||
#define AS_TYPE(type, value) ctx.template asType<type>(value)
|
||||
|
||||
void registerPatternLanguageFunctions() {
|
||||
using namespace hex::lang;
|
||||
|
||||
/* findSequence(occurrenceIndex, byte...) */
|
||||
ContentRegistry::PatternLanguageFunctions::add("findSequence", ContentRegistry::PatternLanguageFunctions::MoreParametersThan | 1, [](auto &ctx, auto params) {
|
||||
auto& occurrenceIndex = AS_TYPE(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
|
||||
ctx.getConsole().abortEvaluation("sequence bytes need to fit into 1 byte");
|
||||
}, AS_TYPE(ASTNodeIntegerLiteral, params[i])->getValue()));
|
||||
}
|
||||
|
||||
std::vector<u8> bytes(sequence.size(), 0x00);
|
||||
u32 occurrences = 0;
|
||||
for (u64 offset = 0; offset < SharedData::currentProvider->getSize() - sequence.size(); offset++) {
|
||||
SharedData::currentProvider->read(offset, bytes.data(), bytes.size());
|
||||
|
||||
if (bytes == sequence) {
|
||||
if (LITERAL_COMPARE(occurrenceIndex, occurrences < occurrenceIndex)) {
|
||||
occurrences++;
|
||||
continue;
|
||||
}
|
||||
|
||||
return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned64Bit, offset });
|
||||
}
|
||||
}
|
||||
|
||||
ctx.getConsole().abortEvaluation("failed to find sequence");
|
||||
});
|
||||
|
||||
/* readUnsigned(address, size) */
|
||||
ContentRegistry::PatternLanguageFunctions::add("readUnsigned", 2, [](auto &ctx, auto params) {
|
||||
auto address = AS_TYPE(ASTNodeIntegerLiteral, params[0])->getValue();
|
||||
auto size = AS_TYPE(ASTNodeIntegerLiteral, params[1])->getValue();
|
||||
|
||||
if (LITERAL_COMPARE(address, address >= SharedData::currentProvider->getActualSize()))
|
||||
ctx.getConsole().abortEvaluation("address out of range");
|
||||
|
||||
return std::visit([&](auto &&address, auto &&size) {
|
||||
if (size <= 0 || size > 16)
|
||||
ctx.getConsole().abortEvaluation("invalid read size");
|
||||
|
||||
u8 value[(u8)size];
|
||||
SharedData::currentProvider->read(address, value, size);
|
||||
|
||||
switch ((u8)size) {
|
||||
case 1: return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned8Bit, *reinterpret_cast<u8*>(value) });
|
||||
case 2: return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned16Bit, *reinterpret_cast<u16*>(value) });
|
||||
case 4: return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned32Bit, *reinterpret_cast<u32*>(value) });
|
||||
case 8: return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned64Bit, *reinterpret_cast<u64*>(value) });
|
||||
case 16: return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned128Bit, *reinterpret_cast<u128*>(value) });
|
||||
default: ctx.getConsole().abortEvaluation("invalid read size");
|
||||
}
|
||||
}, address, size);
|
||||
});
|
||||
|
||||
/* readSigned(address, size) */
|
||||
ContentRegistry::PatternLanguageFunctions::add("readSigned", 2, [](auto &ctx, auto params) {
|
||||
auto address = AS_TYPE(ASTNodeIntegerLiteral, params[0])->getValue();
|
||||
auto size = AS_TYPE(ASTNodeIntegerLiteral, params[1])->getValue();
|
||||
|
||||
if (LITERAL_COMPARE(address, address >= SharedData::currentProvider->getActualSize()))
|
||||
ctx.getConsole().abortEvaluation("address out of range");
|
||||
|
||||
return std::visit([&](auto &&address, auto &&size) {
|
||||
if (size <= 0 || size > 16)
|
||||
ctx.getConsole().abortEvaluation("invalid read size");
|
||||
|
||||
u8 value[(u8)size];
|
||||
SharedData::currentProvider->read(address, value, size);
|
||||
|
||||
switch ((u8)size) {
|
||||
case 1: return new ASTNodeIntegerLiteral({ Token::ValueType::Signed8Bit, *reinterpret_cast<s8*>(value) });
|
||||
case 2: return new ASTNodeIntegerLiteral({ Token::ValueType::Signed16Bit, *reinterpret_cast<s16*>(value) });
|
||||
case 4: return new ASTNodeIntegerLiteral({ Token::ValueType::Signed32Bit, *reinterpret_cast<s32*>(value) });
|
||||
case 8: return new ASTNodeIntegerLiteral({ Token::ValueType::Signed64Bit, *reinterpret_cast<s64*>(value) });
|
||||
case 16: return new ASTNodeIntegerLiteral({ Token::ValueType::Signed128Bit, *reinterpret_cast<s128*>(value) });
|
||||
default: ctx.getConsole().abortEvaluation("invalid read size");
|
||||
}
|
||||
}, address, size);
|
||||
});
|
||||
|
||||
/* assert(condition, message) */
|
||||
ContentRegistry::PatternLanguageFunctions::add("assert", 2, [](auto &ctx, auto params) {
|
||||
auto condition = AS_TYPE(ASTNodeIntegerLiteral, params[0])->getValue();
|
||||
auto message = AS_TYPE(ASTNodeStringLiteral, params[1])->getString();
|
||||
|
||||
if (LITERAL_COMPARE(condition, condition == 0))
|
||||
ctx.getConsole().abortEvaluation(hex::format("assert failed \"%s\"", message.data()));
|
||||
|
||||
return nullptr;
|
||||
});
|
||||
|
||||
/* warnAssert(condition, message) */
|
||||
ContentRegistry::PatternLanguageFunctions::add("warnAssert", 2, [](auto ctx, auto params) {
|
||||
auto condition = AS_TYPE(ASTNodeIntegerLiteral, params[0])->getValue();
|
||||
auto message = AS_TYPE(ASTNodeStringLiteral, params[1])->getString();
|
||||
|
||||
if (LITERAL_COMPARE(condition, condition == 0))
|
||||
ctx.getConsole().log(LogConsole::Level::Warning, hex::format("assert failed \"%s\"", message.data()));
|
||||
|
||||
return nullptr;
|
||||
});
|
||||
|
||||
/* print(values...) */
|
||||
ContentRegistry::PatternLanguageFunctions::add("print", ContentRegistry::PatternLanguageFunctions::MoreParametersThan | 0, [](auto &ctx, 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 += hex::to_string(std::get<u128>(integerLiteral->getValue())); break;
|
||||
case Token::ValueType::Signed128Bit: message += hex::to_string(std::get<s128>(integerLiteral->getValue())); break;
|
||||
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();
|
||||
}
|
||||
|
||||
ctx.getConsole().log(LogConsole::Level::Info, message);
|
||||
|
||||
return nullptr;
|
||||
});
|
||||
|
||||
/* addressof(rValueString) */
|
||||
ContentRegistry::PatternLanguageFunctions::add("addressof", 1, [](auto &ctx, auto params) -> ASTNode* {
|
||||
auto name = AS_TYPE(ASTNodeStringLiteral, params[0])->getString();
|
||||
|
||||
std::vector<std::string> path = splitString(name, ".");
|
||||
auto pattern = ctx.patternFromName(path);
|
||||
|
||||
return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned64Bit, u64(pattern->getOffset()) });
|
||||
});
|
||||
|
||||
/* sizeof(rValueString) */
|
||||
ContentRegistry::PatternLanguageFunctions::add("sizeof", 1, [](auto &ctx, auto params) -> ASTNode* {
|
||||
auto name = AS_TYPE(ASTNodeStringLiteral, params[0])->getString();
|
||||
|
||||
std::vector<std::string> path = splitString(name, ".");
|
||||
auto pattern = ctx.patternFromName(path);
|
||||
|
||||
return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned64Bit, u64(pattern->getSize()) });
|
||||
});
|
||||
|
||||
/* nextAfter(rValueString) */
|
||||
ContentRegistry::PatternLanguageFunctions::add("nextAfter", 1, [](auto &ctx, auto params) -> ASTNode* {
|
||||
auto name = AS_TYPE(ASTNodeStringLiteral, params[0])->getString();
|
||||
|
||||
std::vector<std::string> path = splitString(name, ".");
|
||||
auto pattern = ctx.patternFromName(path);
|
||||
|
||||
return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned64Bit, u64(pattern->getOffset() + pattern->getSize()) });
|
||||
});
|
||||
|
||||
/* alignTo(alignment, value) */
|
||||
ContentRegistry::PatternLanguageFunctions::add("alignTo", 2, [](auto &ctx, auto params) -> ASTNode* {
|
||||
auto alignment = AS_TYPE(ASTNodeIntegerLiteral, params[0])->getValue();
|
||||
auto value = AS_TYPE(ASTNodeIntegerLiteral, params[1])->getValue();
|
||||
|
||||
auto result = std::visit([](auto &&alignment, auto &&value) {
|
||||
u64 remainder = u64(value) % u64(alignment);
|
||||
return remainder != 0 ? u64(value) + (u64(alignment) - remainder) : u64(value);
|
||||
}, alignment, value);
|
||||
|
||||
return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned64Bit, u64(result) });
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
9
plugins/builtin/source/content/settings_entries.cpp
Normal file
9
plugins/builtin/source/content/settings_entries.cpp
Normal file
@@ -0,0 +1,9 @@
|
||||
#include <hex/plugin.hpp>
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
void registerSettings() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
298
plugins/builtin/source/content/tools_entries.cpp
Normal file
298
plugins/builtin/source/content/tools_entries.cpp
Normal file
@@ -0,0 +1,298 @@
|
||||
#include <hex/plugin.hpp>
|
||||
|
||||
#include <regex>
|
||||
|
||||
#include <llvm/Demangle/Demangle.h>
|
||||
#include "math_evaluator.hpp"
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
namespace {
|
||||
|
||||
void drawDemangler() {
|
||||
static std::vector<char> mangledBuffer(0xF'FFFF, 0x00);
|
||||
static std::string demangledName;
|
||||
|
||||
if (ImGui::InputText("Mangled name", mangledBuffer.data(), 0xF'FFFF)) {
|
||||
demangledName = llvm::demangle(mangledBuffer.data());
|
||||
}
|
||||
|
||||
ImGui::InputText("Demangled name", demangledName.data(), demangledName.size(), ImGuiInputTextFlags_ReadOnly);
|
||||
ImGui::NewLine();
|
||||
}
|
||||
|
||||
void drawASCIITable() {
|
||||
static bool asciiTableShowOctal = false;
|
||||
|
||||
ImGui::BeginTable("##asciitable", 4);
|
||||
ImGui::TableSetupColumn("");
|
||||
ImGui::TableSetupColumn("");
|
||||
ImGui::TableSetupColumn("");
|
||||
ImGui::TableSetupColumn("");
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
for (u8 tablePart = 0; tablePart < 4; tablePart++) {
|
||||
ImGui::BeginTable("##asciitablepart", asciiTableShowOctal ? 4 : 3, ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_RowBg);
|
||||
ImGui::TableSetupColumn("dec");
|
||||
if (asciiTableShowOctal)
|
||||
ImGui::TableSetupColumn("oct");
|
||||
ImGui::TableSetupColumn("hex");
|
||||
ImGui::TableSetupColumn("char");
|
||||
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
u32 rowCount = 0;
|
||||
for (u8 i = 0; i < 0x80 / 4; i++) {
|
||||
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%02d", i + 32 * tablePart);
|
||||
|
||||
if (asciiTableShowOctal) {
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0o%02o", i + 32 * tablePart);
|
||||
}
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%02x", i + 32 * tablePart);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", makePrintable(i + 32 * tablePart).c_str());
|
||||
|
||||
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, ((rowCount % 2) == 0) ? 0xFF101010 : 0xFF303030);
|
||||
|
||||
rowCount++;
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
ImGui::TableNextColumn();
|
||||
}
|
||||
ImGui::EndTable();
|
||||
|
||||
ImGui::Checkbox("Show octal", &asciiTableShowOctal);
|
||||
ImGui::NewLine();
|
||||
}
|
||||
|
||||
void drawRegexReplacer() {
|
||||
static std::vector<char> regexInput(0xF'FFFF, 0x00);;
|
||||
static std::vector<char> regexPattern(0xF'FFFF, 0x00);;
|
||||
static std::vector<char> replacePattern(0xF'FFFF, 0x00);;
|
||||
static std::string regexOutput(0xF'FFFF, 0x00);;
|
||||
|
||||
bool shouldInvalidate;
|
||||
|
||||
shouldInvalidate = ImGui::InputText("Regex pattern", regexPattern.data(), regexPattern.size());
|
||||
shouldInvalidate = ImGui::InputText("Replace pattern", replacePattern.data(), replacePattern.size()) || shouldInvalidate;
|
||||
shouldInvalidate = ImGui::InputTextMultiline("Input", regexInput.data(), regexInput.size()) || shouldInvalidate;
|
||||
|
||||
if (shouldInvalidate) {
|
||||
try {
|
||||
regexOutput = std::regex_replace(regexInput.data(), std::regex(regexPattern.data()), replacePattern.data());
|
||||
} catch (std::regex_error&) {}
|
||||
}
|
||||
|
||||
ImGui::InputTextMultiline("Output", regexOutput.data(), regexOutput.size(), ImVec2(0, 0), ImGuiInputTextFlags_ReadOnly);
|
||||
ImGui::NewLine();
|
||||
}
|
||||
|
||||
void drawColorPicker() {
|
||||
static std::array<float, 4> pickedColor = { 0 };
|
||||
|
||||
ImGui::SetNextItemWidth(300.0F);
|
||||
ImGui::ColorPicker4("Color Picker", pickedColor.data(),
|
||||
ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_AlphaBar | ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_DisplayHex);
|
||||
ImGui::NewLine();
|
||||
}
|
||||
|
||||
void drawMathEvaluator() {
|
||||
static std::vector<long double> mathHistory;
|
||||
static std::string lastMathError;
|
||||
static std::vector<char> mathInput(0xF'FFFF, 0x00);
|
||||
|
||||
static MathEvaluator mathEvaluator = [&]{
|
||||
MathEvaluator evaluator;
|
||||
|
||||
evaluator.registerStandardVariables();
|
||||
evaluator.registerStandardFunctions();
|
||||
|
||||
evaluator.setFunction("clear", [&](auto args) -> std::optional<long double> {
|
||||
mathHistory.clear();
|
||||
lastMathError.clear();
|
||||
mathEvaluator.getVariables().clear();
|
||||
mathEvaluator.registerStandardVariables();
|
||||
std::memset(mathInput.data(), 0x00, mathInput.size());
|
||||
|
||||
return { };
|
||||
}, 0, 0);
|
||||
|
||||
evaluator.setFunction("read", [](auto args) -> std::optional<long double> {
|
||||
u8 value = 0;
|
||||
|
||||
auto provider = SharedData::currentProvider;
|
||||
if (provider == nullptr || !provider->isReadable() || args[0] >= provider->getActualSize())
|
||||
return { };
|
||||
|
||||
provider->read(args[0], &value, sizeof(u8));
|
||||
|
||||
return value;
|
||||
}, 1, 1);
|
||||
|
||||
evaluator.setFunction("write", [](auto args) -> std::optional<long double> {
|
||||
auto provider = SharedData::currentProvider;
|
||||
if (provider == nullptr || !provider->isWritable() || args[0] >= provider->getActualSize())
|
||||
return { };
|
||||
|
||||
if (args[1] > 0xFF)
|
||||
return { };
|
||||
|
||||
u8 value = args[1];
|
||||
provider->write(args[0], &value, sizeof(u8));
|
||||
|
||||
return { };
|
||||
}, 2, 2);
|
||||
|
||||
return std::move(evaluator);
|
||||
}();
|
||||
|
||||
if (ImGui::InputText("Input", mathInput.data(), mathInput.size(), ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll)) {
|
||||
ImGui::SetKeyboardFocusHere();
|
||||
std::optional<long double> result;
|
||||
|
||||
try {
|
||||
result = mathEvaluator.evaluate(mathInput.data());
|
||||
} catch (std::invalid_argument &e) {
|
||||
lastMathError = e.what();
|
||||
}
|
||||
|
||||
if (result.has_value()) {
|
||||
mathHistory.push_back(result.value());
|
||||
std::memset(mathInput.data(), 0x00, mathInput.size());
|
||||
lastMathError.clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!lastMathError.empty())
|
||||
ImGui::TextColored(ImColor(0xA00040FF), "Last Error: %s", lastMathError.c_str());
|
||||
else
|
||||
ImGui::NewLine();
|
||||
|
||||
enum class MathDisplayType { Standard, Scientific, Engineering, Programmer } mathDisplayType;
|
||||
if (ImGui::BeginTabBar("##mathFormatTabBar")) {
|
||||
if (ImGui::BeginTabItem("Standard")) {
|
||||
mathDisplayType = MathDisplayType::Standard;
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("Scientific")) {
|
||||
mathDisplayType = MathDisplayType::Scientific;
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("Engineering")) {
|
||||
mathDisplayType = MathDisplayType::Engineering;
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("Programmer")) {
|
||||
mathDisplayType = MathDisplayType::Programmer;
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
|
||||
if (ImGui::BeginTable("##mathWrapper", 2)) {
|
||||
ImGui::TableSetupColumn("##results");
|
||||
ImGui::TableSetupColumn("##variables", ImGuiTableColumnFlags_WidthStretch, 0.7);
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::BeginTable("##mathHistory", 1, ImGuiTableFlags_ScrollY | ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg, ImVec2(0, 400))) {
|
||||
ImGui::TableSetupScrollFreeze(0, 1);
|
||||
ImGui::TableSetupColumn("History");
|
||||
|
||||
ImGuiListClipper clipper;
|
||||
clipper.Begin(mathHistory.size());
|
||||
|
||||
ImGui::TableHeadersRow();
|
||||
while (clipper.Step()) {
|
||||
for (u64 i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) {
|
||||
if (i == 0)
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImU32(ImColor(0xA5, 0x45, 0x45)));
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
switch (mathDisplayType) {
|
||||
case MathDisplayType::Standard:
|
||||
ImGui::Text("%.3Lf", mathHistory[(mathHistory.size() - 1) - i]);
|
||||
break;
|
||||
case MathDisplayType::Scientific:
|
||||
ImGui::Text("%.6Le", mathHistory[(mathHistory.size() - 1) - i]);
|
||||
break;
|
||||
case MathDisplayType::Engineering:
|
||||
ImGui::Text("%s", hex::toEngineeringString(mathHistory[(mathHistory.size() - 1) - i]).c_str());
|
||||
break;
|
||||
case MathDisplayType::Programmer:
|
||||
ImGui::Text("0x%llX (%llu)",
|
||||
u64(mathHistory[(mathHistory.size() - 1) - i]),
|
||||
u64(mathHistory[(mathHistory.size() - 1) - i]));
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == 0)
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
}
|
||||
|
||||
clipper.End();
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::BeginTable("##mathVariables", 2, ImGuiTableFlags_ScrollY | ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg, ImVec2(0, 400))) {
|
||||
ImGui::TableSetupScrollFreeze(0, 1);
|
||||
ImGui::TableSetupColumn("Name");
|
||||
ImGui::TableSetupColumn("Value");
|
||||
|
||||
ImGui::TableHeadersRow();
|
||||
for (const auto &[name, value] : mathEvaluator.getVariables()) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(name.c_str());
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
switch (mathDisplayType) {
|
||||
case MathDisplayType::Standard:
|
||||
ImGui::Text("%.3Lf", value);
|
||||
break;
|
||||
case MathDisplayType::Scientific:
|
||||
ImGui::Text("%.6Le", value);
|
||||
break;
|
||||
case MathDisplayType::Engineering:
|
||||
ImGui::Text("%s", hex::toEngineeringString(value).c_str());
|
||||
break;
|
||||
case MathDisplayType::Programmer:
|
||||
ImGui::Text("0x%llX (%llu)", u64(value), u64(value));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void registerToolEntries() {
|
||||
ContentRegistry::Tools::add("Itanium/MSVC demangler", drawDemangler);
|
||||
ContentRegistry::Tools::add("ASCII table", drawASCIITable);
|
||||
ContentRegistry::Tools::add("Regex replacer", drawRegexReplacer);
|
||||
ContentRegistry::Tools::add("Color picker", drawColorPicker);
|
||||
ContentRegistry::Tools::add("Calculator", drawMathEvaluator);
|
||||
}
|
||||
|
||||
}
|
||||
390
plugins/builtin/source/math_evaluator.cpp
Normal file
390
plugins/builtin/source/math_evaluator.cpp
Normal file
@@ -0,0 +1,390 @@
|
||||
#include "math_evaluator.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <queue>
|
||||
#include <stack>
|
||||
#include <stdexcept>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <numbers>
|
||||
|
||||
namespace hex {
|
||||
|
||||
s16 comparePrecedence(const Operator& a, const Operator& b) {
|
||||
return (static_cast<s8>(a) & 0x0F0) - (static_cast<s8>(b) & 0x0F0);
|
||||
}
|
||||
|
||||
bool isLeftAssociative(const Operator op) {
|
||||
return (static_cast<u32>(op) & 0xF00) == 0;
|
||||
}
|
||||
|
||||
std::pair<Operator, size_t> toOperator(std::string input) {
|
||||
if (input.starts_with("##")) return { Operator::Combine, 2 };
|
||||
if (input.starts_with("==")) return { Operator::Equals, 2 };
|
||||
if (input.starts_with("!=")) return { Operator::NotEquals, 2 };
|
||||
if (input.starts_with(">=")) return { Operator::GreaterThanOrEquals, 2 };
|
||||
if (input.starts_with("<=")) return { Operator::LessThanOrEquals, 2 };
|
||||
if (input.starts_with(">>")) return { Operator::ShiftRight, 2 };
|
||||
if (input.starts_with("<<")) return { Operator::ShiftLeft, 2 };
|
||||
if (input.starts_with("||")) return { Operator::Or, 2 };
|
||||
if (input.starts_with("^^")) return { Operator::Xor, 2 };
|
||||
if (input.starts_with("&&")) return { Operator::And, 2 };
|
||||
if (input.starts_with("**")) return { Operator::Exponentiation, 2 };
|
||||
if (input.starts_with(">")) return { Operator::GreaterThan, 1 };
|
||||
if (input.starts_with("<")) return { Operator::LessThan, 1 };
|
||||
if (input.starts_with("!")) return { Operator::Not, 1 };
|
||||
if (input.starts_with("|")) return { Operator::BitwiseOr, 1 };
|
||||
if (input.starts_with("^")) return { Operator::BitwiseXor, 1 };
|
||||
if (input.starts_with("&")) return { Operator::BitwiseAnd, 1 };
|
||||
if (input.starts_with("~")) return { Operator::BitwiseNot, 1 };
|
||||
if (input.starts_with("+")) return { Operator::Addition, 1 };
|
||||
if (input.starts_with("-")) return { Operator::Subtraction, 1 };
|
||||
if (input.starts_with("*")) return { Operator::Multiplication, 1 };
|
||||
if (input.starts_with("/")) return { Operator::Division, 1 };
|
||||
if (input.starts_with("%")) return { Operator::Modulus, 1 };
|
||||
if (input.starts_with("=")) return { Operator::Assign, 1 };
|
||||
|
||||
return { Operator::Invalid, 0 };
|
||||
}
|
||||
|
||||
std::queue<Token> MathEvaluator::parseInput(const char *input) {
|
||||
std::queue<Token> inputQueue;
|
||||
|
||||
char *prevPos = const_cast<char*>(input);
|
||||
for (char *pos = prevPos; *pos != 0x00;) {
|
||||
if (std::isdigit(*pos) || *pos == '.') {
|
||||
auto number = std::strtold(pos, &pos);
|
||||
|
||||
if (*pos == 'x') {
|
||||
pos--;
|
||||
number = std::strtoull(pos, &pos, 0);
|
||||
}
|
||||
|
||||
inputQueue.push(Token{ .type = TokenType::Number, .number = number });
|
||||
} else if (*pos == '(') {
|
||||
inputQueue.push(Token{ .type = TokenType::Bracket, .bracketType = BracketType::Left});
|
||||
pos++;
|
||||
} else if (*pos == ')') {
|
||||
inputQueue.push(Token{ .type = TokenType::Bracket, .bracketType = BracketType::Right});
|
||||
pos++;
|
||||
} else if (std::isspace(*pos)) {
|
||||
pos++;
|
||||
} else {
|
||||
auto [op, width] = toOperator(pos);
|
||||
|
||||
if (op != Operator::Invalid) {
|
||||
inputQueue.push(Token{ .type = TokenType::Operator, .op = op });
|
||||
pos += width;
|
||||
} else {
|
||||
Token token;
|
||||
|
||||
while (std::isalpha(*pos) || *pos == '_') {
|
||||
token.name += *pos;
|
||||
pos++;
|
||||
}
|
||||
|
||||
if (*pos == '(') {
|
||||
pos++;
|
||||
|
||||
u32 depth = 1;
|
||||
std::vector<std::string> expressions;
|
||||
expressions.emplace_back();
|
||||
|
||||
while (*pos != 0x00) {
|
||||
if (*pos == '(') depth++;
|
||||
else if (*pos == ')') depth--;
|
||||
|
||||
if (depth == 0)
|
||||
break;
|
||||
|
||||
if (depth == 1 && *pos == ',') {
|
||||
expressions.emplace_back();
|
||||
pos++;
|
||||
}
|
||||
|
||||
expressions.back() += *pos;
|
||||
|
||||
pos++;
|
||||
}
|
||||
|
||||
pos++;
|
||||
|
||||
for (const auto &expression : expressions) {
|
||||
if (expression == "" && expressions.size() > 1)
|
||||
throw std::invalid_argument("Invalid function call syntax!");
|
||||
else if (expression == "")
|
||||
break;
|
||||
|
||||
auto inputQueue = parseInput(expression.c_str());
|
||||
auto postfixTokens = toPostfix(inputQueue);
|
||||
auto result = evaluate(postfixTokens);
|
||||
|
||||
if (!result.has_value())
|
||||
throw std::invalid_argument("Invalid argument for function!");
|
||||
|
||||
token.arguments.push_back(result.value());
|
||||
}
|
||||
|
||||
token.type = TokenType::Function;
|
||||
inputQueue.push(token);
|
||||
|
||||
} else {
|
||||
token.type = TokenType::Variable;
|
||||
inputQueue.push(token);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
if (prevPos == pos)
|
||||
throw std::invalid_argument("Invalid syntax!");
|
||||
|
||||
prevPos = pos;
|
||||
}
|
||||
|
||||
return inputQueue;
|
||||
}
|
||||
|
||||
std::queue<Token> MathEvaluator::toPostfix(std::queue<Token> inputQueue) {
|
||||
std::queue<Token> outputQueue;
|
||||
std::stack<Token> operatorStack;
|
||||
|
||||
while (!inputQueue.empty()) {
|
||||
Token currToken = inputQueue.front();
|
||||
inputQueue.pop();
|
||||
|
||||
if (currToken.type == TokenType::Number || currToken.type == TokenType::Variable || currToken.type == TokenType::Function)
|
||||
outputQueue.push(currToken);
|
||||
else if (currToken.type == TokenType::Operator) {
|
||||
while ((!operatorStack.empty())
|
||||
&& (operatorStack.top().type == TokenType::Operator && currToken.type == TokenType::Operator && (comparePrecedence(operatorStack.top().op, currToken.op) > 0) || (comparePrecedence(operatorStack.top().op, currToken.op) == 0 && isLeftAssociative(currToken.op)))
|
||||
&& operatorStack.top().type != TokenType::Bracket) {
|
||||
outputQueue.push(operatorStack.top());
|
||||
operatorStack.pop();
|
||||
}
|
||||
operatorStack.push(currToken);
|
||||
} else if (currToken.type == TokenType::Bracket) {
|
||||
if (currToken.bracketType == BracketType::Left)
|
||||
operatorStack.push(currToken);
|
||||
else {
|
||||
if (operatorStack.empty())
|
||||
throw std::invalid_argument("Mismatching parenthesis!");
|
||||
|
||||
while (operatorStack.top().type != TokenType::Bracket || (operatorStack.top().type == TokenType::Bracket && operatorStack.top().bracketType != BracketType::Left)) {
|
||||
if (operatorStack.empty())
|
||||
throw std::invalid_argument("Mismatching parenthesis!");
|
||||
|
||||
outputQueue.push(operatorStack.top());
|
||||
operatorStack.pop();
|
||||
}
|
||||
|
||||
operatorStack.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (!operatorStack.empty()) {
|
||||
auto top = operatorStack.top();
|
||||
|
||||
if (top.type == TokenType::Bracket)
|
||||
throw std::invalid_argument("Mismatching parenthesis!");
|
||||
|
||||
outputQueue.push(top);
|
||||
operatorStack.pop();
|
||||
}
|
||||
|
||||
return outputQueue;
|
||||
}
|
||||
|
||||
std::optional<long double> MathEvaluator::evaluate(std::queue<Token> postfixTokens) {
|
||||
std::stack<long double> evaluationStack;
|
||||
|
||||
while (!postfixTokens.empty()) {
|
||||
auto front = postfixTokens.front();
|
||||
postfixTokens.pop();
|
||||
|
||||
if (front.type == TokenType::Number)
|
||||
evaluationStack.push(front.number);
|
||||
else if (front.type == TokenType::Operator) {
|
||||
long double rightOperand, leftOperand;
|
||||
if (evaluationStack.size() < 2) {
|
||||
if ((front.op == Operator::Addition || front.op == Operator::Subtraction || front.op == Operator::Not || front.op == Operator::BitwiseNot) && evaluationStack.size() == 1) {
|
||||
rightOperand = evaluationStack.top(); evaluationStack.pop();
|
||||
leftOperand = 0;
|
||||
}
|
||||
else throw std::invalid_argument("Not enough operands for operator!");
|
||||
} else {
|
||||
rightOperand = evaluationStack.top(); evaluationStack.pop();
|
||||
leftOperand = evaluationStack.top(); evaluationStack.pop();
|
||||
}
|
||||
|
||||
long double result = std::numeric_limits<long double>::quiet_NaN();
|
||||
switch (front.op) {
|
||||
default:
|
||||
case Operator::Invalid:
|
||||
throw std::invalid_argument("Invalid operator!");
|
||||
case Operator::And:
|
||||
result = static_cast<s64>(leftOperand) && static_cast<s64>(rightOperand);
|
||||
break;
|
||||
case Operator::Or:
|
||||
result = static_cast<s64>(leftOperand) && static_cast<s64>(rightOperand);
|
||||
break;
|
||||
case Operator::Xor:
|
||||
result = (static_cast<s64>(leftOperand) ^ static_cast<s64>(rightOperand)) > 0;
|
||||
break;
|
||||
case Operator::GreaterThan:
|
||||
result = leftOperand > rightOperand;
|
||||
break;
|
||||
case Operator::LessThan:
|
||||
result = leftOperand < rightOperand;
|
||||
break;
|
||||
case Operator::GreaterThanOrEquals:
|
||||
result = leftOperand >= rightOperand;
|
||||
break;
|
||||
case Operator::LessThanOrEquals:
|
||||
result = leftOperand <= rightOperand;
|
||||
break;
|
||||
case Operator::Equals:
|
||||
result = leftOperand == rightOperand;
|
||||
break;
|
||||
case Operator::NotEquals:
|
||||
result = leftOperand != rightOperand;
|
||||
break;
|
||||
case Operator::Not:
|
||||
result = !static_cast<s64>(rightOperand);
|
||||
break;
|
||||
case Operator::BitwiseOr:
|
||||
result = static_cast<s64>(leftOperand) | static_cast<s64>(rightOperand);
|
||||
break;
|
||||
case Operator::BitwiseXor:
|
||||
result = static_cast<s64>(leftOperand) ^ static_cast<s64>(rightOperand);
|
||||
break;
|
||||
case Operator::BitwiseAnd:
|
||||
result = static_cast<s64>(leftOperand) & static_cast<s64>(rightOperand);
|
||||
break;
|
||||
case Operator::BitwiseNot:
|
||||
result = ~static_cast<s64>(rightOperand);
|
||||
break;
|
||||
case Operator::ShiftLeft:
|
||||
result = static_cast<s64>(leftOperand) << static_cast<s64>(rightOperand);
|
||||
break;
|
||||
case Operator::ShiftRight:
|
||||
result = static_cast<s64>(leftOperand) >> static_cast<s64>(rightOperand);
|
||||
break;
|
||||
case Operator::Addition:
|
||||
result = leftOperand + rightOperand;
|
||||
break;
|
||||
case Operator::Subtraction:
|
||||
result = leftOperand - rightOperand;
|
||||
break;
|
||||
case Operator::Multiplication:
|
||||
result = leftOperand * rightOperand;
|
||||
break;
|
||||
case Operator::Division:
|
||||
result = leftOperand / rightOperand;
|
||||
break;
|
||||
case Operator::Modulus:
|
||||
result = std::fmod(leftOperand, rightOperand);
|
||||
break;
|
||||
case Operator::Exponentiation:
|
||||
result = std::pow(leftOperand, rightOperand);
|
||||
break;
|
||||
case Operator::Combine:
|
||||
result = (static_cast<u64>(leftOperand) << (64 - __builtin_clzll(static_cast<u64>(rightOperand)))) | static_cast<u64>(rightOperand);
|
||||
break;
|
||||
}
|
||||
|
||||
evaluationStack.push(result);
|
||||
} else if (front.type == TokenType::Variable) {
|
||||
if (this->m_variables.contains(front.name))
|
||||
evaluationStack.push(this->m_variables.at(front.name));
|
||||
else
|
||||
throw std::invalid_argument("Unknown variable!");
|
||||
} else if (front.type == TokenType::Function) {
|
||||
if (!this->m_functions[front.name])
|
||||
throw std::invalid_argument("Unknown function called!");
|
||||
|
||||
auto result = this->m_functions[front.name](front.arguments);
|
||||
|
||||
if (result.has_value())
|
||||
evaluationStack.push(result.value());
|
||||
} else
|
||||
throw std::invalid_argument("Parenthesis in postfix expression!");
|
||||
|
||||
}
|
||||
|
||||
if (evaluationStack.empty())
|
||||
return { };
|
||||
else if (evaluationStack.size() > 1)
|
||||
throw std::invalid_argument("Undigested input left!");
|
||||
else
|
||||
return evaluationStack.top();
|
||||
}
|
||||
|
||||
|
||||
std::optional<long double> MathEvaluator::evaluate(std::string input) {
|
||||
auto inputQueue = parseInput(input.c_str());
|
||||
|
||||
std::string resultVariable = "ans";
|
||||
|
||||
{
|
||||
std::queue<Token> queueCopy = inputQueue;
|
||||
if (queueCopy.front().type == TokenType::Variable) {
|
||||
resultVariable = queueCopy.front().name;
|
||||
queueCopy.pop();
|
||||
if (queueCopy.front().type != TokenType::Operator || queueCopy.front().op != Operator::Assign)
|
||||
resultVariable = "ans";
|
||||
else {
|
||||
inputQueue.pop();
|
||||
inputQueue.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto postfixTokens = toPostfix(inputQueue);
|
||||
|
||||
auto result = evaluate(postfixTokens);
|
||||
|
||||
if (result.has_value()) {
|
||||
this->setVariable(resultVariable, result.value());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void MathEvaluator::setVariable(std::string name, long double value) {
|
||||
this->m_variables[name] = value;
|
||||
}
|
||||
|
||||
void MathEvaluator::setFunction(std::string name, std::function<std::optional<long double>(std::vector<long double>)> function, size_t minNumArgs, size_t maxNumArgs) {
|
||||
this->m_functions[name] = [minNumArgs, maxNumArgs, function](auto args) {
|
||||
if (args.size() < minNumArgs || args.size() > maxNumArgs)
|
||||
throw std::invalid_argument("Invalid number of function arguments!");
|
||||
|
||||
return function(args);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
void MathEvaluator::registerStandardVariables() {
|
||||
this->setVariable("ans", 0);
|
||||
}
|
||||
|
||||
void MathEvaluator::registerStandardFunctions() {
|
||||
this->setFunction("sin", [](auto args){ return std::sin(args[0]); }, 1, 1);
|
||||
this->setFunction("cos", [](auto args){ return std::cos(args[0]); }, 1, 1);
|
||||
this->setFunction("tan", [](auto args){ return std::tan(args[0]); }, 1, 1);
|
||||
this->setFunction("sqrt", [](auto args){ return std::sqrt(args[0]); }, 1, 1);
|
||||
this->setFunction("ceil", [](auto args){ return std::ceil(args[0]); }, 1, 1);
|
||||
this->setFunction("floor", [](auto args){ return std::floor(args[0]); }, 1, 1);
|
||||
this->setFunction("sign", [](auto args){ return (args[0] > 0) ? 1 : (args[0] == 0) ? 0 : -1; }, 1, 1);
|
||||
this->setFunction("abs", [](auto args){ return std::abs(args[0]); }, 1, 1);
|
||||
this->setFunction("ln", [](auto args){ return std::log(args[0]); }, 1, 1);
|
||||
this->setFunction("lb", [](auto args){ return std::log2(args[0]); }, 1, 1);
|
||||
this->setFunction("log", [](auto args){ return args.size() == 1 ? std::log10(args[0]) : std::log(args[1]) / std::log(args[0]); }, 1, 2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
25
plugins/builtin/source/plugin_builtin.cpp
Normal file
25
plugins/builtin/source/plugin_builtin.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#include <hex/plugin.hpp>
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
void registerDataInspectorEntries();
|
||||
void registerToolEntries();
|
||||
void registerPatternLanguageFunctions();
|
||||
void registerCommandPaletteCommands();
|
||||
void registerSettings();
|
||||
|
||||
}
|
||||
|
||||
IMHEX_PLUGIN_SETUP {
|
||||
|
||||
using namespace hex::plugin::builtin;
|
||||
|
||||
registerDataInspectorEntries();
|
||||
registerToolEntries();
|
||||
registerPatternLanguageFunctions();
|
||||
registerCommandPaletteCommands();
|
||||
registerSettings();
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user