mirror of
https://github.com/WerWolv/ImHex.git
synced 2026-03-30 21:05:56 -05:00
Added hex editor and basic pattern parsing/highlighting
This commit is contained in:
15
source/main.cpp
Normal file
15
source/main.cpp
Normal file
@@ -0,0 +1,15 @@
|
||||
#include "window.hpp"
|
||||
|
||||
#include "views/view_hexeditor.hpp"
|
||||
#include "views/view_pattern.hpp"
|
||||
|
||||
int main() {
|
||||
hex::Window window;
|
||||
|
||||
auto* hexEditor = window.addView<hex::ViewHexEditor>();
|
||||
window.addView<hex::ViewPattern>(hexEditor);
|
||||
|
||||
window.loop();
|
||||
|
||||
return 0;
|
||||
}
|
||||
155
source/parser/lexer.cpp
Normal file
155
source/parser/lexer.cpp
Normal file
@@ -0,0 +1,155 @@
|
||||
#include "parser/lexer.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
namespace hex::lang {
|
||||
|
||||
Lexer::Lexer() { }
|
||||
|
||||
std::string matchTillInvalid(const char* characters, std::function<bool(char)> predicate) {
|
||||
std::string ret;
|
||||
|
||||
while (*characters != 0x00) {
|
||||
ret += *characters;
|
||||
characters++;
|
||||
|
||||
if (!predicate(*characters))
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::optional<u64> parseInt(std::string_view string) {
|
||||
u64 integer = 0;
|
||||
u8 base = 10;
|
||||
|
||||
std::string_view numberData;
|
||||
|
||||
if (string.starts_with("0x")) {
|
||||
numberData = string.substr(2);
|
||||
base = 16;
|
||||
|
||||
if (numberData.find_first_not_of("0123456789ABCDEFabcdef") != std::string_view::npos)
|
||||
return { };
|
||||
} else if (string.starts_with("0b")) {
|
||||
numberData = string.substr(2);
|
||||
base = 2;
|
||||
|
||||
if (numberData.find_first_not_of("01") != std::string_view::npos)
|
||||
return { };
|
||||
} else if (isdigit(string[0])) {
|
||||
numberData = string;
|
||||
base = 10;
|
||||
|
||||
if (numberData.find_first_not_of("0123456789") != std::string_view::npos)
|
||||
return { };
|
||||
} else return { };
|
||||
|
||||
if (numberData.length() == 0)
|
||||
return { };
|
||||
|
||||
for (const char& c : numberData) {
|
||||
integer *= base;
|
||||
|
||||
if (isdigit(c))
|
||||
integer += (c - '0');
|
||||
else if (c >= 'A' && c <= 'F')
|
||||
integer += (c - 'A');
|
||||
else if (c >= 'a' && c <= 'f')
|
||||
integer += (c - 'a');
|
||||
else return { };
|
||||
}
|
||||
|
||||
return integer;
|
||||
}
|
||||
|
||||
std::pair<Result, std::vector<Token>> Lexer::lex(const std::string& code) {
|
||||
std::vector<Token> tokens;
|
||||
u32 offset = 0;
|
||||
|
||||
while (offset < code.length()) {
|
||||
const char& c = code[offset];
|
||||
|
||||
if (std::isblank(c) || std::isspace(c)) {
|
||||
offset += 1;
|
||||
} else if (c == ';') {
|
||||
tokens.push_back({.type = Token::Type::EndOfExpression});
|
||||
offset += 1;
|
||||
} else if (c == '{') {
|
||||
tokens.push_back({.type = Token::Type::ScopeOpen});
|
||||
offset += 1;
|
||||
} else if (c == '}') {
|
||||
tokens.push_back({.type = Token::Type::ScopeClose});
|
||||
offset += 1;
|
||||
} else if (c == ',') {
|
||||
tokens.push_back({.type = Token::Type::Separator});
|
||||
offset += 1;
|
||||
} else if (c == '@') {
|
||||
tokens.push_back({.type = Token::Type::Operator, .operatorToken = { .op = Token::OperatorToken::Operator::AtDeclaration}});
|
||||
offset += 1;
|
||||
} else if (c == '=') {
|
||||
tokens.push_back({.type = Token::Type::Operator, .operatorToken = { .op = Token::OperatorToken::Operator::Assignment}});
|
||||
offset += 1;
|
||||
} else if (std::isalpha(c)) {
|
||||
std::string identifier = matchTillInvalid(&code[offset], [](char c) -> bool { return std::isalnum(c) || c == '_'; });
|
||||
|
||||
// Check for reserved keywords
|
||||
|
||||
if (identifier == "struct")
|
||||
tokens.push_back({.type = Token::Type::Keyword, .keywordToken = { .keyword = Token::KeywordToken::Keyword::Struct}});
|
||||
else if (identifier == "using")
|
||||
tokens.push_back({.type = Token::Type::Keyword, .keywordToken = { .keyword = Token::KeywordToken::Keyword::Using}});
|
||||
|
||||
// Check for built-in types
|
||||
else if (identifier == "u8")
|
||||
tokens.push_back({.type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Unsigned8Bit }});
|
||||
else if (identifier == "s8")
|
||||
tokens.push_back({.type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Signed8Bit }});
|
||||
else if (identifier == "u16")
|
||||
tokens.push_back({.type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Unsigned16Bit }});
|
||||
else if (identifier == "s16")
|
||||
tokens.push_back({.type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Signed16Bit }});
|
||||
else if (identifier == "u32")
|
||||
tokens.push_back({.type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Unsigned32Bit }});
|
||||
else if (identifier == "s32")
|
||||
tokens.push_back({.type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Signed32Bit }});
|
||||
else if (identifier == "u64")
|
||||
tokens.push_back({.type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Unsigned64Bit }});
|
||||
else if (identifier == "s64")
|
||||
tokens.push_back({.type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Signed64Bit }});
|
||||
else if (identifier == "u128")
|
||||
tokens.push_back({.type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Unsigned128Bit }});
|
||||
else if (identifier == "s128")
|
||||
tokens.push_back({.type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Signed128Bit }});
|
||||
else if (identifier == "float")
|
||||
tokens.push_back({.type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Float }});
|
||||
else if (identifier == "double")
|
||||
tokens.push_back({.type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Double }});
|
||||
|
||||
// If it's not a keyword and a builtin type, it has to be an identifier
|
||||
|
||||
else
|
||||
tokens.push_back({.type = Token::Type::Identifier, .identifierToken = { .identifier = identifier}});
|
||||
|
||||
offset += identifier.length();
|
||||
} else if (std::isdigit(c)) {
|
||||
char *end = nullptr;
|
||||
std::strtoull(&code[offset], &end, 0);
|
||||
|
||||
auto integer = parseInt(std::string_view(&code[offset], end));// matchTillInvalid(&code[offset], [](char c) -> bool { return std::isdigit(c); });
|
||||
|
||||
if (!integer.has_value())
|
||||
return { ResultLexicalError, {}};
|
||||
|
||||
tokens.push_back({.type = Token::Type::Integer, .integerToken = { .integer = integer.value() }});
|
||||
offset += (end - &code[offset]);
|
||||
} else return { ResultLexicalError, {}};
|
||||
}
|
||||
|
||||
tokens.push_back({.type = Token::Type::EndOfProgram});
|
||||
|
||||
return { ResultSuccess, tokens };
|
||||
}
|
||||
}
|
||||
178
source/parser/parser.cpp
Normal file
178
source/parser/parser.cpp
Normal file
@@ -0,0 +1,178 @@
|
||||
#include "parser/parser.hpp"
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace hex::lang {
|
||||
|
||||
Parser::Parser() {
|
||||
|
||||
}
|
||||
|
||||
using TokenIter = std::vector<Token>::const_iterator;
|
||||
|
||||
std::vector<ASTNode*> parseTillToken(TokenIter &curr, Token::Type endTokenType);
|
||||
|
||||
bool tryConsume(TokenIter &curr, std::initializer_list<Token::Type> tokenTypes) {
|
||||
std::vector<Token>::const_iterator originalPosition = curr;
|
||||
|
||||
for (const auto& type : tokenTypes) {
|
||||
if (curr->type != type) {
|
||||
curr = originalPosition;
|
||||
return false;
|
||||
}
|
||||
curr++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
ASTNode* parseBuiltinVariableDecl(TokenIter &curr) {
|
||||
return new ASTNodeVariableDecl(curr[-3].typeToken.type, curr[-2].identifierToken.identifier);
|
||||
}
|
||||
|
||||
ASTNode* parseCustomTypeVariableDecl(TokenIter &curr) {
|
||||
return new ASTNodeVariableDecl(Token::TypeToken::Type::CustomType, curr[-2].identifierToken.identifier, curr[-3].identifierToken.identifier);
|
||||
}
|
||||
|
||||
std::optional<ASTNode*> parseStruct(TokenIter &curr) {
|
||||
const std::string &structName = curr[-2].identifierToken.identifier;
|
||||
std::vector<ASTNode*> nodes;
|
||||
std::optional<u64> offset = { };
|
||||
|
||||
while (!tryConsume(curr, {Token::Type::ScopeClose})) {
|
||||
if (tryConsume(curr, {Token::Type::Type, Token::Type::Identifier, Token::Type::EndOfExpression}))
|
||||
nodes.push_back(parseBuiltinVariableDecl(curr));
|
||||
else if (tryConsume(curr, {Token::Type::Identifier, Token::Type::Identifier, Token::Type::EndOfExpression}))
|
||||
nodes.push_back(parseCustomTypeVariableDecl(curr));
|
||||
else break;
|
||||
}
|
||||
|
||||
if (tryConsume(curr, {Token::Type::Operator})) {
|
||||
if (curr[-1].operatorToken.op == Token::OperatorToken::Operator::AtDeclaration) {
|
||||
if (tryConsume(curr, {Token::Type::Integer})) {
|
||||
offset = curr[-1].integerToken.integer;
|
||||
} else {
|
||||
for(auto &node : nodes) delete node;
|
||||
return { };
|
||||
}
|
||||
} else {
|
||||
for(auto &node : nodes) delete node;
|
||||
return { };
|
||||
}
|
||||
}
|
||||
|
||||
if (!tryConsume(curr, {Token::Type::EndOfExpression})) {
|
||||
for(auto &node : nodes) delete node;
|
||||
return { };
|
||||
}
|
||||
|
||||
return new ASTNodeStruct(structName, nodes, offset);
|
||||
}
|
||||
|
||||
ASTNode *parseScope(TokenIter &curr) {
|
||||
return new ASTNodeScope(parseTillToken(curr, Token::Type::ScopeClose));
|
||||
}
|
||||
|
||||
std::optional<ASTNode*> parseUsingDeclaration(TokenIter &curr) {
|
||||
auto keyword = curr[-5].keywordToken;
|
||||
auto name = curr[-4].identifierToken;
|
||||
auto op = curr[-3].operatorToken;
|
||||
|
||||
if (keyword.keyword != Token::KeywordToken::Keyword::Using)
|
||||
return { };
|
||||
|
||||
if (op.op != Token::OperatorToken::Operator::Assignment)
|
||||
return { };
|
||||
|
||||
if (curr[-2].type == Token::Type::Type) {
|
||||
auto type = curr[-2].typeToken;
|
||||
|
||||
return new ASTNodeTypeDecl(type.type, name.identifier);
|
||||
} else if (curr[-2].type == Token::Type::Identifier) {
|
||||
auto customType = curr[-2].identifierToken;
|
||||
|
||||
return new ASTNodeTypeDecl(Token::TypeToken::Type::CustomType, name.identifier, customType.identifier);
|
||||
}
|
||||
|
||||
return { };
|
||||
}
|
||||
|
||||
std::optional<std::vector<ASTNode*>> parseStatement(TokenIter &curr) {
|
||||
std::vector<ASTNode*> program;
|
||||
|
||||
if (tryConsume(curr, { Token::Type::Keyword, Token::Type::Identifier, Token::Type::ScopeOpen })) {
|
||||
if (curr[-3].keywordToken.keyword == Token::KeywordToken::Keyword::Struct) {
|
||||
auto structAst = parseStruct(curr);
|
||||
|
||||
if (!structAst.has_value()) {
|
||||
for(auto &node : program) delete node;
|
||||
return { };
|
||||
}
|
||||
|
||||
program.push_back(structAst.value());
|
||||
}
|
||||
|
||||
return program;
|
||||
} else if (tryConsume(curr, { Token::Type::ScopeOpen })) {
|
||||
program.push_back(parseScope(curr));
|
||||
|
||||
return program;
|
||||
} else if (tryConsume(curr, { Token::Type::Keyword, Token::Type::Identifier, Token::Type::Operator, Token::Type::Type, Token::Type::EndOfExpression})) {
|
||||
auto usingDecl = parseUsingDeclaration(curr);
|
||||
|
||||
if (!usingDecl.has_value()) {
|
||||
for(auto &node : program) delete node;
|
||||
return { };
|
||||
}
|
||||
|
||||
program.push_back(usingDecl.value());
|
||||
|
||||
return program;
|
||||
} else if (tryConsume(curr, { Token::Type::Keyword, Token::Type::Identifier, Token::Type::Operator, Token::Type::Identifier, Token::Type::EndOfExpression})) {
|
||||
auto usingDecl = parseUsingDeclaration(curr);
|
||||
|
||||
if (!usingDecl.has_value()) {
|
||||
for(auto &node : program) delete node;
|
||||
return { };
|
||||
}
|
||||
|
||||
program.push_back(usingDecl.value());
|
||||
|
||||
return program;
|
||||
}
|
||||
else {
|
||||
for(auto &node : program) delete node;
|
||||
return { };
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<ASTNode*> parseTillToken(TokenIter &curr, Token::Type endTokenType) {
|
||||
std::vector<ASTNode*> program;
|
||||
|
||||
while (curr->type != endTokenType) {
|
||||
auto newTokens = parseStatement(curr);
|
||||
|
||||
if (!newTokens.has_value())
|
||||
break;
|
||||
|
||||
program.insert(program.end(), newTokens->begin(), newTokens->end());
|
||||
}
|
||||
|
||||
curr++;
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
std::pair<Result, std::vector<ASTNode*>> Parser::parse(const std::vector<Token> &tokens) {
|
||||
auto currentToken = tokens.begin();
|
||||
|
||||
auto program = parseTillToken(currentToken, Token::Type::EndOfProgram);
|
||||
|
||||
if (program.empty())
|
||||
return { ResultParseError, { } };
|
||||
|
||||
return { ResultSuccess, program };
|
||||
}
|
||||
|
||||
}
|
||||
136
source/window.cpp
Normal file
136
source/window.cpp
Normal file
@@ -0,0 +1,136 @@
|
||||
#include "window.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "imgui.h"
|
||||
#include "imgui_impl_glfw.h"
|
||||
#include "imgui_impl_opengl3.h"
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
namespace hex {
|
||||
|
||||
Window::Window() {
|
||||
this->initGLFW();
|
||||
this->initImGui();
|
||||
}
|
||||
|
||||
Window::~Window() {
|
||||
this->deinitImGui();
|
||||
this->deinitGLFW();
|
||||
|
||||
for (auto &view : this->m_views)
|
||||
delete view;
|
||||
}
|
||||
|
||||
void Window::loop() {
|
||||
while (!glfwWindowShouldClose(this->m_window)) {
|
||||
this->frameBegin();
|
||||
|
||||
for (auto &view : this->m_views)
|
||||
view->createView();
|
||||
|
||||
this->frameEnd();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Window::frameBegin() {
|
||||
glfwPollEvents();
|
||||
|
||||
ImGui_ImplOpenGL3_NewFrame();
|
||||
ImGui_ImplGlfw_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
|
||||
ImGuiWindowFlags windowFlags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking;
|
||||
ImGuiViewport* viewport = ImGui::GetMainViewport();
|
||||
ImGui::SetNextWindowPos(viewport->GetWorkPos());
|
||||
ImGui::SetNextWindowSize(viewport->GetWorkSize());
|
||||
ImGui::SetNextWindowViewport(viewport->ID);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
|
||||
windowFlags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove;
|
||||
windowFlags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus;
|
||||
|
||||
ImGui::Begin("DockSpace", nullptr, windowFlags);
|
||||
ImGui::PopStyleVar(2);
|
||||
ImGui::DockSpace(ImGui::GetID("MainDock"), ImVec2(0.0f, 0.0f), ImGuiDockNodeFlags_None);
|
||||
|
||||
ImGui::BeginMenuBar();
|
||||
|
||||
for (auto &view : this->m_views)
|
||||
view->createMenu();
|
||||
|
||||
ImGui::EndMenuBar();
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void Window::frameEnd() {
|
||||
ImGui::Render();
|
||||
int display_w, display_h;
|
||||
glfwGetFramebufferSize(this->m_window, &display_w, &display_h);
|
||||
glViewport(0, 0, display_w, display_h);
|
||||
glClearColor(0.45f, 0.55f, 0.60f, 1.00f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
||||
|
||||
glfwSwapBuffers(this->m_window);
|
||||
}
|
||||
|
||||
static void glfw_error_callback(int error, const char* description)
|
||||
{
|
||||
fprintf(stderr, "Glfw Error %d: %s\n", error, description);
|
||||
}
|
||||
|
||||
|
||||
void Window::initGLFW() {
|
||||
glfwSetErrorCallback(glfw_error_callback);
|
||||
if (!glfwInit())
|
||||
throw std::runtime_error("Failed to initialize GLFW!");
|
||||
|
||||
#ifdef __APPLE__
|
||||
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
|
||||
#endif
|
||||
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
|
||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||
|
||||
this->m_window = glfwCreateWindow(1280, 720, "ImHex", nullptr, nullptr);
|
||||
|
||||
if (this->m_window == nullptr)
|
||||
throw std::runtime_error("Failed to create window!");
|
||||
|
||||
glfwMakeContextCurrent(this->m_window);
|
||||
glfwSwapInterval(1);
|
||||
|
||||
if (gladLoadGL() == 0)
|
||||
throw std::runtime_error("Failed to initialize OpenGL loader!");
|
||||
}
|
||||
|
||||
void Window::initImGui() {
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
ImGuiIO& io = ImGui::GetIO(); (void)io;
|
||||
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
|
||||
|
||||
ImGui::StyleColorsDark();
|
||||
|
||||
ImGui_ImplGlfw_InitForOpenGL(this->m_window, true);
|
||||
ImGui_ImplOpenGL3_Init("#version 150");
|
||||
}
|
||||
|
||||
void Window::deinitGLFW() {
|
||||
glfwDestroyWindow(this->m_window);
|
||||
glfwTerminate();
|
||||
}
|
||||
|
||||
void Window::deinitImGui() {
|
||||
ImGui_ImplOpenGL3_Shutdown();
|
||||
ImGui_ImplGlfw_Shutdown();
|
||||
ImGui::DestroyContext();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user