diff --git a/CMakeLists.txt b/CMakeLists.txt index ebfdf004d..69fe6e6fe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,9 +20,10 @@ add_executable(ImHex source/utils.cpp source/crypto.cpp - source/parser/lexer.cpp - source/parser/parser.cpp - source/parser/validator.cpp + source/lang/preprocessor.cpp + source/lang/lexer.cpp + source/lang/parser.cpp + source/lang/validator.cpp source/provider/file_provider.cpp diff --git a/include/hex.hpp b/include/hex.hpp index 2fa60bbfb..7d95cf731 100644 --- a/include/hex.hpp +++ b/include/hex.hpp @@ -15,5 +15,5 @@ using s32 = std::int32_t; using s64 = std::int64_t; using s128 = __int128_t; -#include "parser/result.hpp" -#include "parser/results.hpp" \ No newline at end of file +#include "lang/result.hpp" +#include "lang/results.hpp" \ No newline at end of file diff --git a/include/parser/ast_node.hpp b/include/lang/ast_node.hpp similarity index 100% rename from include/parser/ast_node.hpp rename to include/lang/ast_node.hpp diff --git a/include/parser/lexer.hpp b/include/lang/lexer.hpp similarity index 100% rename from include/parser/lexer.hpp rename to include/lang/lexer.hpp diff --git a/include/parser/parser.hpp b/include/lang/parser.hpp similarity index 100% rename from include/parser/parser.hpp rename to include/lang/parser.hpp diff --git a/include/lang/preprocessor.hpp b/include/lang/preprocessor.hpp new file mode 100644 index 000000000..35640beba --- /dev/null +++ b/include/lang/preprocessor.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include + +#include "token.hpp" + +#include +#include +#include + +namespace hex::lang { + + class Preprocessor { + public: + Preprocessor(); + + std::pair preprocess(const std::string& code, bool applyDefines = true); + + private: + std::set> m_defines; + }; + +} \ No newline at end of file diff --git a/include/parser/result.hpp b/include/lang/result.hpp similarity index 100% rename from include/parser/result.hpp rename to include/lang/result.hpp diff --git a/include/lang/results.hpp b/include/lang/results.hpp new file mode 100644 index 000000000..8ba760b05 --- /dev/null +++ b/include/lang/results.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include "result.hpp" + +namespace hex::lang { + + constexpr Result ResultSuccess(0, 0); + + constexpr Result ResultPreprocessingError(1, 1); + constexpr Result ResultLexicalError(2, 1); + constexpr Result ResultParseError(3, 1); + +} \ No newline at end of file diff --git a/include/parser/token.hpp b/include/lang/token.hpp similarity index 100% rename from include/parser/token.hpp rename to include/lang/token.hpp diff --git a/include/parser/validator.hpp b/include/lang/validator.hpp similarity index 100% rename from include/parser/validator.hpp rename to include/lang/validator.hpp diff --git a/include/parser/results.hpp b/include/parser/results.hpp deleted file mode 100644 index eceac7304..000000000 --- a/include/parser/results.hpp +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include "result.hpp" - -namespace hex::lang { - - constexpr Result ResultSuccess(0, 0); - - constexpr Result ResultLexicalError(1, 1); - constexpr Result ResultParseError(2, 1); - -} \ No newline at end of file diff --git a/include/utils.hpp b/include/utils.hpp index 8f8c6d6b2..0e3aa27af 100644 --- a/include/utils.hpp +++ b/include/utils.hpp @@ -8,7 +8,7 @@ #include #include -#include "parser/token.hpp" +#include "lang/token.hpp" namespace hex { diff --git a/include/views/view_pattern.hpp b/include/views/view_pattern.hpp index 952a34d7f..8bcaa1a58 100644 --- a/include/views/view_pattern.hpp +++ b/include/views/view_pattern.hpp @@ -1,6 +1,6 @@ #pragma once -#include "parser/ast_node.hpp" +#include "lang/ast_node.hpp" #include "views/view.hpp" #include "views/pattern_data.hpp" diff --git a/source/parser/lexer.cpp b/source/lang/lexer.cpp similarity index 99% rename from source/parser/lexer.cpp rename to source/lang/lexer.cpp index b97623180..11aa9d3d4 100644 --- a/source/parser/lexer.cpp +++ b/source/lang/lexer.cpp @@ -1,4 +1,4 @@ -#include "parser/lexer.hpp" +#include "lang/lexer.hpp" #include #include diff --git a/source/parser/parser.cpp b/source/lang/parser.cpp similarity index 99% rename from source/parser/parser.cpp rename to source/lang/parser.cpp index 4695ed54e..ca78c6f38 100644 --- a/source/parser/parser.cpp +++ b/source/lang/parser.cpp @@ -1,4 +1,4 @@ -#include "parser/parser.hpp" +#include "lang/parser.hpp" #include diff --git a/source/lang/preprocessor.cpp b/source/lang/preprocessor.cpp new file mode 100644 index 000000000..d54090b9a --- /dev/null +++ b/source/lang/preprocessor.cpp @@ -0,0 +1,120 @@ +#include "lang/preprocessor.hpp" + +namespace hex::lang { + + Preprocessor::Preprocessor() { + + } + + std::pair Preprocessor::preprocess(const std::string& code, bool applyDefines) { + u32 offset = 0; + + if (applyDefines) + this->m_defines.clear(); + + std::string output; + output.reserve(code.length()); + + while (offset < code.length()) { + if (code[offset] == '#') { + offset += 1; + + if (code.substr(offset, 7) == "include") { + offset += 7; + + while (std::isblank(code[offset]) || std::isspace(code[offset])) + offset += 1; + + if (code[offset] != '<' && code[offset] != '"') + return { ResultPreprocessingError, "" }; + + char endChar = code[offset]; + if (endChar == '<') endChar = '>'; + + offset += 1; + + std::string includeFile; + while (code[offset] != endChar) { + includeFile += code[offset]; + offset += 1; + + if (offset >= code.length()) + return { ResultPreprocessingError, "" }; + } + offset += 1; + + if (includeFile[0] != '/') + includeFile = "include/" + includeFile; + + FILE *file = fopen(includeFile.c_str(), "r"); + if (file == nullptr) + return { ResultPreprocessingError, "" }; + + fseek(file, 0, SEEK_END); + size_t size = ftell(file); + char *buffer = new char[size + 1]; + rewind(file); + + fread(buffer, size, 1, file); + buffer[size] = 0x00; + + auto [result, preprocessedInclude] = this->preprocess(buffer, false); + if (result.failed()) + return { ResultPreprocessingError, "" }; + + output += preprocessedInclude; + + delete[] buffer; + + fclose(file); + } else if (code.substr(offset, 6) == "define") { + offset += 6; + + while (std::isblank(code[offset])) + offset += 1; + + std::string defineName; + while (!std::isblank(code[offset])) { + defineName += code[offset]; + offset += 1; + + if (offset >= code.length() || code[offset] == '\n' || code[offset] == '\r') + return { ResultPreprocessingError, "" }; + } + + while (std::isblank(code[offset])) + offset += 1; + + std::string replaceValue; + do { + if (offset >= code.length()) + return { ResultPreprocessingError, "" }; + + replaceValue += code[offset]; + offset += 1; + } while (code[offset] != '\n' && code[offset] != '\r'); + + this->m_defines.emplace(defineName, replaceValue); + } + } + + output += code[offset]; + offset += 1; + } + + if (applyDefines) { + for (const auto &[define, value] : this->m_defines) { + s32 index = 0; + while((index = output.find(define, index)) != std::string::npos) { + if (index > 0) { + output.replace(index, define.length(), value); + index += value.length(); + } + } + } + } + + return { ResultSuccess, output }; + } + +} \ No newline at end of file diff --git a/source/views/view_pattern.cpp b/source/views/view_pattern.cpp index c9859eb18..053df8f68 100644 --- a/source/views/view_pattern.cpp +++ b/source/views/view_pattern.cpp @@ -1,9 +1,9 @@ #include "views/view_pattern.hpp" -#include -#include "parser/parser.hpp" -#include "parser/lexer.hpp" -#include "parser/validator.hpp" +#include "lang/preprocessor.hpp" +#include "lang/parser.hpp" +#include "lang/lexer.hpp" +#include "lang/validator.hpp" #include "utils.hpp" namespace hex { @@ -98,6 +98,7 @@ namespace hex { delete data; this->m_patternData.clear(); + PatternData::resetPalette(); } template T> @@ -112,6 +113,7 @@ namespace hex { } void ViewPattern::parsePattern(char *buffer) { + static hex::lang::Preprocessor preprocessor; static hex::lang::Lexer lexer; static hex::lang::Parser parser; static hex::lang::Validator validator; @@ -119,8 +121,11 @@ namespace hex { this->clearPatternData(); this->postEvent(Events::PatternChanged); - auto [lexResult, tokens] = lexer.lex(buffer); + auto [preprocessingResult, preprocesedCode] = preprocessor.preprocess(buffer); + if (preprocessingResult.failed()) + return; + auto [lexResult, tokens] = lexer.lex(preprocesedCode); if (lexResult.failed()) { return; }