diff --git a/include/lang/evaluator.hpp b/include/lang/evaluator.hpp index 4326fe4a9..c8cc659b5 100644 --- a/include/lang/evaluator.hpp +++ b/include/lang/evaluator.hpp @@ -16,12 +16,12 @@ namespace hex::lang { class Evaluator { public: - Evaluator(prv::Provider* &provider, std::endian defaultDataEndian); - - std::optional> evaluate(const std::vector& ast); - - const std::pair& getError() { return this->m_error; } - + enum ConsoleLogLevel { + Debug, + Info, + Warning, + Error + }; struct Function { constexpr static u32 UnlimitedParameters = 0xFFFF'FFFF; @@ -33,6 +33,11 @@ namespace hex::lang { std::function)> func; }; + Evaluator(prv::Provider* &provider, std::endian defaultDataEndian); + + std::optional> evaluate(const std::vector& ast); + const auto& getConsoleLog() { return this->m_consoleLog; } + private: std::map m_types; prv::Provider* &m_provider; @@ -43,12 +48,24 @@ namespace hex::lang { std::vector*> m_currMembers; std::map m_functions; - std::pair m_error; + std::vector> m_consoleLog; - using EvaluateError = std::pair; + using EvaluateError = std::string; - [[noreturn]] static void throwEvaluateError(std::string_view error, u32 lineNumber) { - throw EvaluateError(lineNumber, "Evaluator: " + std::string(error)); + void emmitDebugInfo(std::string_view message) { + this->m_consoleLog.emplace_back(ConsoleLogLevel::Debug, "[-] " + std::string(message)); + } + + void emmitInfo(std::string_view message) { + this->m_consoleLog.emplace_back(ConsoleLogLevel::Info, "[i] " + std::string(message)); + } + + void emmitWaring(std::string_view message) { + this->m_consoleLog.emplace_back(ConsoleLogLevel::Warning, "[*] " + std::string(message)); + } + + [[noreturn]] static void throwEvaluateError(std::string_view message) { + throw EvaluateError("[!] " + std::string(message)); } [[nodiscard]] std::endian getCurrentEndian() const { @@ -57,7 +74,7 @@ namespace hex::lang { void addFunction(std::string_view name, u32 parameterCount, std::function)> func) { if (this->m_functions.contains(name.data())) - throwEvaluateError(hex::format("redefinition of function '%s'", name.data()), 1); + throwEvaluateError(hex::format("redefinition of function '%s'", name.data())); this->m_functions[name.data()] = { parameterCount, func }; } @@ -87,7 +104,7 @@ namespace hex::lang { if (auto evaluatedParam = dynamic_cast(param); evaluatedParam != nullptr) return evaluatedParam; else - throwEvaluateError("function got wrong type of parameter", 1); + throwEvaluateError("function got wrong type of parameter"); } @@ -99,6 +116,8 @@ namespace hex::lang { BUILTIN_FUNCTION(readSigned); BUILTIN_FUNCTION(assert); + BUILTIN_FUNCTION(warnAssert); + BUILTIN_FUNCTION(print); #undef BUILTIN_FUNCTION }; diff --git a/include/views/view_pattern.hpp b/include/views/view_pattern.hpp index fa29aaa13..b3a33f7b3 100644 --- a/include/views/view_pattern.hpp +++ b/include/views/view_pattern.hpp @@ -1,9 +1,8 @@ #pragma once -#include "lang/ast_node.hpp" - #include "views/view.hpp" #include "lang/pattern_data.hpp" +#include "lang/evaluator.hpp" #include "providers/provider.hpp" @@ -29,7 +28,7 @@ namespace hex { std::filesystem::path m_possiblePatternFile; TextEditor m_textEditor; - std::vector m_console; + std::vector> m_console; imgui_addons::ImGuiFileBrowser m_fileBrowser; void loadPatternFile(std::string path); diff --git a/source/lang/builtin_functions.cpp b/source/lang/builtin_functions.cpp index a675c4780..824fc2c50 100644 --- a/source/lang/builtin_functions.cpp +++ b/source/lang/builtin_functions.cpp @@ -14,7 +14,7 @@ namespace hex::lang { if (value <= 0xFF) return value; else - throwEvaluateError("sequence bytes need to fit into 1 byte", 1); + throwEvaluateError("sequence bytes need to fit into 1 byte"); }, asType(params[i])->getValue())); } @@ -33,7 +33,7 @@ namespace hex::lang { } } - throwEvaluateError("failed to find sequence", 1); + throwEvaluateError("failed to find sequence"); } BUILTIN_FUNCTION(readUnsigned) { @@ -41,11 +41,11 @@ namespace hex::lang { auto size = asType(params[1])->getValue(); if (LITERAL_COMPARE(address, address >= this->m_provider->getActualSize())) - throwEvaluateError("address out of range", 1); + throwEvaluateError("address out of range"); return std::visit([this](auto &&address, auto &&size) { if (size <= 0 || size > 16) - throwEvaluateError("invalid read size", 1); + throwEvaluateError("invalid read size"); u8 value[(u8)size]; this->m_provider->read(address, value, size); @@ -56,7 +56,7 @@ namespace hex::lang { case 4: return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned32Bit, hex::changeEndianess(*reinterpret_cast(value), 4, this->getCurrentEndian()) }); case 8: return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned64Bit, hex::changeEndianess(*reinterpret_cast(value), 8, this->getCurrentEndian()) }); case 16: return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned128Bit, hex::changeEndianess(*reinterpret_cast(value), 16, this->getCurrentEndian()) }); - default: throwEvaluateError("invalid read size", 1); + default: throwEvaluateError("invalid read size"); } }, address, size); } @@ -66,11 +66,11 @@ namespace hex::lang { auto size = asType(params[1])->getValue(); if (LITERAL_COMPARE(address, address >= this->m_provider->getActualSize())) - throwEvaluateError("address out of range", 1); + throwEvaluateError("address out of range"); return std::visit([this](auto &&address, auto &&size) { if (size <= 0 || size > 16) - throwEvaluateError("invalid read size", 1); + throwEvaluateError("invalid read size"); u8 value[(u8)size]; this->m_provider->read(address, value, size); @@ -81,17 +81,59 @@ namespace hex::lang { case 4: return new ASTNodeIntegerLiteral({ Token::ValueType::Signed32Bit, hex::changeEndianess(*reinterpret_cast(value), 4, this->getCurrentEndian()) }); case 8: return new ASTNodeIntegerLiteral({ Token::ValueType::Signed64Bit, hex::changeEndianess(*reinterpret_cast(value), 8, this->getCurrentEndian()) }); case 16: return new ASTNodeIntegerLiteral({ Token::ValueType::Signed128Bit, hex::changeEndianess(*reinterpret_cast(value), 16, this->getCurrentEndian()) }); - default: throwEvaluateError("invalid read size", 1); + default: throwEvaluateError("invalid read size"); } }, address, size); } BUILTIN_FUNCTION(assert) { auto condition = asType(params[0])->getValue(); - auto error = asType(params[1])->getString(); + auto message = asType(params[1])->getString(); if (LITERAL_COMPARE(condition, condition == 0)) - throwEvaluateError(hex::format("assertion failed: '%s'", error.data()), 1); + throwEvaluateError(hex::format("assert failed \"%s\"", message.data())); + + return nullptr; + } + + BUILTIN_FUNCTION(warnAssert) { + auto condition = asType(params[0])->getValue(); + auto message = asType(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(param); integerLiteral != nullptr) { + switch (integerLiteral->getType()) { + case Token::ValueType::Character: message += std::get(integerLiteral->getValue()); break; + case Token::ValueType::Unsigned8Bit: message += std::to_string(std::get(integerLiteral->getValue())); break; + case Token::ValueType::Signed8Bit: message += std::to_string(std::get(integerLiteral->getValue())); break; + case Token::ValueType::Unsigned16Bit: message += std::to_string(std::get(integerLiteral->getValue())); break; + case Token::ValueType::Signed16Bit: message += std::to_string(std::get(integerLiteral->getValue())); break; + case Token::ValueType::Unsigned32Bit: message += std::to_string(std::get(integerLiteral->getValue())); break; + case Token::ValueType::Signed32Bit: message += std::to_string(std::get(integerLiteral->getValue())); break; + case Token::ValueType::Unsigned64Bit: message += std::to_string(std::get(integerLiteral->getValue())); break; + case Token::ValueType::Signed64Bit: message += std::to_string(std::get(integerLiteral->getValue())); break; + //case Token::ValueType::Unsigned128Bit: message += std::to_string(std::get(integerLiteral->getValue())); break; + //case Token::ValueType::Signed128Bit: message += std::to_string(std::get(integerLiteral->getValue())); break; + case Token::ValueType::Float: message += std::to_string(std::get(integerLiteral->getValue())); break; + case Token::ValueType::Double: message += std::to_string(std::get(integerLiteral->getValue())); break; + case Token::ValueType::Boolean: message += std::get(integerLiteral->getValue()) ? "true" : "false"; break; + case Token::ValueType::CustomType: message += "< Custom Type >"; break; + } + } + else if (auto stringLiteral = dynamic_cast(param); stringLiteral != nullptr) + message += stringLiteral->getString(); + } + + this->emmitInfo(message); return nullptr; } diff --git a/source/lang/evaluator.cpp b/source/lang/evaluator.cpp index 1f1eab496..e32915c7f 100644 --- a/source/lang/evaluator.cpp +++ b/source/lang/evaluator.cpp @@ -28,6 +28,14 @@ namespace hex::lang { 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); + }); } ASTNodeIntegerLiteral* Evaluator::evaluateScopeResolution(ASTNodeScopeResolution *node) { @@ -46,12 +54,12 @@ namespace hex::lang { } } - throwEvaluateError("failed to find identifier", node->getLineNumber()); + throwEvaluateError("failed to find identifier"); } ASTNodeIntegerLiteral* Evaluator::evaluateRValue(ASTNodeRValue *node) { if (this->m_currMembers.empty() && this->m_globalMembers.empty()) - throwEvaluateError("no variables available", node->getLineNumber()); + throwEvaluateError("no variables available"); std::vector currMembers; @@ -74,7 +82,7 @@ namespace hex::lang { continue; } else if (currPattern != nullptr) - throwEvaluateError("tried to access member of a non-struct/union type", node->getLineNumber()); + throwEvaluateError("tried to access member of a non-struct/union type"); auto candidate = std::find_if(currMembers.begin(), currMembers.end(), [&](auto member) { return member->getVariableName() == identifier; @@ -83,7 +91,7 @@ namespace hex::lang { if (candidate != currMembers.end()) currPattern = *candidate; else - throwEvaluateError(hex::format("could not find identifier '%s'", identifier.c_str()), node->getLineNumber()); + throwEvaluateError(hex::format("could not find identifier '%s'", identifier.c_str())); } if (auto pointerPattern = dynamic_cast(currPattern); pointerPattern != nullptr) @@ -99,7 +107,7 @@ namespace hex::lang { case 4: return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned32Bit, hex::changeEndianess(*reinterpret_cast(value), 4, this->getCurrentEndian()) }); case 8: return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned64Bit, hex::changeEndianess(*reinterpret_cast(value), 8, this->getCurrentEndian()) }); case 16: return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned128Bit, hex::changeEndianess(*reinterpret_cast(value), 16, this->getCurrentEndian()) }); - default: throwEvaluateError("invalid rvalue size", node->getLineNumber()); + default: throwEvaluateError("invalid rvalue size"); } } else if (auto signedPattern = dynamic_cast(currPattern); signedPattern != nullptr) { u8 value[unsignedPattern->getSize()]; @@ -111,7 +119,7 @@ namespace hex::lang { case 4: return new ASTNodeIntegerLiteral({ Token::ValueType::Signed32Bit, hex::changeEndianess(*reinterpret_cast(value), 4, this->getCurrentEndian()) }); case 8: return new ASTNodeIntegerLiteral({ Token::ValueType::Signed64Bit, hex::changeEndianess(*reinterpret_cast(value), 8, this->getCurrentEndian()) }); case 16: return new ASTNodeIntegerLiteral({ Token::ValueType::Signed128Bit, hex::changeEndianess(*reinterpret_cast(value), 16, this->getCurrentEndian()) }); - default: throwEvaluateError("invalid rvalue size", node->getLineNumber()); + default: throwEvaluateError("invalid rvalue size"); } } else if (auto enumPattern = dynamic_cast(currPattern); enumPattern != nullptr) { u8 value[enumPattern->getSize()]; @@ -123,10 +131,10 @@ namespace hex::lang { case 4: return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned32Bit, hex::changeEndianess(*reinterpret_cast(value), 4, this->getCurrentEndian()) }); case 8: return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned64Bit, hex::changeEndianess(*reinterpret_cast(value), 8, this->getCurrentEndian()) }); case 16: return new ASTNodeIntegerLiteral({ Token::ValueType::Unsigned128Bit, hex::changeEndianess(*reinterpret_cast(value), 16, this->getCurrentEndian()) }); - default: throwEvaluateError("invalid rvalue size", node->getLineNumber()); + default: throwEvaluateError("invalid rvalue size"); } } else - throwEvaluateError("tried to use non-integer value in numeric expression", node->getLineNumber()); + throwEvaluateError("tried to use non-integer value in numeric expression"); } ASTNodeIntegerLiteral* Evaluator::evaluateFunctionCall(ASTNodeFunctionCall *node) { @@ -144,7 +152,7 @@ namespace hex::lang { } if (!this->m_functions.contains(node->getFunctionName().data())) - throwEvaluateError(hex::format("no function named '%s' found", node->getFunctionName().data()), node->getLineNumber()); + throwEvaluateError(hex::format("no function named '%s' found", node->getFunctionName().data())); auto &function = this->m_functions[node->getFunctionName().data()]; @@ -153,12 +161,12 @@ namespace hex::lang { } 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), node->getLineNumber()); + 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), node->getLineNumber()); + throwEvaluateError(hex::format("too few parameters for function '%s'. Expected %d", node->getFunctionName().data(), function.parameterCount & ~Function::MoreParametersThan)); } else if (function.parameterCount != evaluatedParams.size()) { - throwEvaluateError(hex::format("invalid number of parameters for function '%s'. Expected %d", node->getFunctionName().data(), function.parameterCount), node->getLineNumber()); + throwEvaluateError(hex::format("invalid number of parameters for function '%s'. Expected %d", node->getFunctionName().data(), function.parameterCount)); } return function.func(evaluatedParams); @@ -266,12 +274,12 @@ namespace hex::lang { case Token::Operator::BoolNot: return new ASTNodeIntegerLiteral({ newType, !rightValue }); default: - throwEvaluateError("invalid operator used in mathematical expression", left->getLineNumber()); + throwEvaluateError("invalid operator used in mathematical expression"); } }, left->getValue(), right->getValue()); } catch (std::runtime_error &e) { - throwEvaluateError("bitwise operations on floating point numbers are forbidden", left->getLineNumber()); + throwEvaluateError("bitwise operations on floating point numbers are forbidden"); } } @@ -290,12 +298,12 @@ namespace hex::lang { auto returnValue = evaluateFunctionCall(exprFunctionCall); if (returnValue == nullptr) - throwEvaluateError("function returning void used in expression", node->getLineNumber()); + throwEvaluateError("function returning void used in expression"); else return returnValue; } else - throwEvaluateError("invalid operand", node->getLineNumber()); + throwEvaluateError("invalid operand"); } ASTNodeIntegerLiteral* Evaluator::evaluateTernaryExpression(ASTNodeTernaryExpression *node) { @@ -310,7 +318,7 @@ namespace hex::lang { return this->evaluateOperand(node->getThirdOperand()); } default: - throwEvaluateError("invalid operator used in ternary expression", node->getLineNumber()); + throwEvaluateError("invalid operator used in ternary expression"); } } @@ -338,7 +346,7 @@ namespace hex::lang { else if (Token::isFloatingPoint(type)) pattern = new PatternDataFloat(this->m_currOffset, typeSize); else - throwEvaluateError("invalid builtin type", node->getLineNumber()); + throwEvaluateError("invalid builtin type"); this->m_currOffset += typeSize; @@ -376,7 +384,7 @@ namespace hex::lang { return patterns; } else - throwEvaluateError("invalid struct member", node->getLineNumber()); + throwEvaluateError("invalid struct member"); } PatternData* Evaluator::evaluateStruct(ASTNodeStruct *node) { @@ -418,7 +426,7 @@ namespace hex::lang { for (auto &[name, value] : node->getEntries()) { auto expression = dynamic_cast(value); if (expression == nullptr) - throwEvaluateError("invalid expression in enum value", value->getLineNumber()); + throwEvaluateError("invalid expression in enum value"); auto valueNode = evaluateMathematicalExpression(expression); SCOPE_EXIT( delete valueNode; ); @@ -430,7 +438,7 @@ namespace hex::lang { if (auto underlyingType = dynamic_cast(node->getUnderlyingType()); underlyingType != nullptr) size = Token::getTypeSize(underlyingType->getType()); else - throwEvaluateError("invalid enum underlying type", node->getLineNumber()); + throwEvaluateError("invalid enum underlying type"); return new PatternDataEnum(startOffset, size, entryPatterns); } @@ -443,19 +451,19 @@ namespace hex::lang { for (auto &[name, value] : node->getEntries()) { auto expression = dynamic_cast(value); if (expression == nullptr) - throwEvaluateError("invalid expression in bitfield field size", value->getLineNumber()); + throwEvaluateError("invalid expression in bitfield field size"); auto valueNode = evaluateMathematicalExpression(expression); SCOPE_EXIT( delete valueNode; ); auto fieldBits = std::visit([node, type = valueNode->getType()] (auto &&value) { if (Token::isFloatingPoint(type)) - throwEvaluateError("bitfield entry size must be an integer value", node->getLineNumber()); + throwEvaluateError("bitfield entry size must be an integer value"); return static_cast(value); }, valueNode->getValue()); if (fieldBits > 64 || fieldBits <= 0) - throwEvaluateError("bitfield entry must occupy between 1 and 64 bits", value->getLineNumber()); + throwEvaluateError("bitfield entry must occupy between 1 and 64 bits"); bits += fieldBits; @@ -485,7 +493,7 @@ namespace hex::lang { else if (auto bitfieldNode = dynamic_cast(type); bitfieldNode != nullptr) pattern = this->evaluateBitfield(bitfieldNode); else - throwEvaluateError("type could not be evaluated", node->getLineNumber()); + throwEvaluateError("type could not be evaluated"); if (!node->getName().empty()) pattern->setTypeName(node->getName().data()); @@ -505,12 +513,12 @@ namespace hex::lang { this->m_currOffset = std::visit([node, type = valueNode->getType()] (auto &&value) { if (Token::isFloatingPoint(type)) - throwEvaluateError("placement offset must be an integer value", node->getLineNumber()); + throwEvaluateError("placement offset must be an integer value"); return static_cast(value); }, valueNode->getValue()); } if (this->m_currOffset >= this->m_provider->getActualSize()) - throwEvaluateError("variable placed out of range", node->getLineNumber()); + throwEvaluateError("variable placed out of range"); PatternData *pattern; if (auto typeDecl = dynamic_cast(node->getType()); typeDecl != nullptr) @@ -518,7 +526,7 @@ namespace hex::lang { else if (auto builtinTypeDecl = dynamic_cast(node->getType()); builtinTypeDecl != nullptr) pattern = this->evaluateBuiltinType(builtinTypeDecl); else - throwEvaluateError("ASTNodeVariableDecl had an invalid type. This is a bug!", 1); + throwEvaluateError("ASTNodeVariableDecl had an invalid type. This is a bug!"); pattern->setVariableName(node->getName().data()); @@ -533,7 +541,7 @@ namespace hex::lang { this->m_currOffset = std::visit([node, type = valueNode->getType()] (auto &&value) { if (Token::isFloatingPoint(type)) - throwEvaluateError("placement offset must be an integer value", node->getLineNumber()); + throwEvaluateError("placement offset must be an integer value"); return static_cast(value); }, valueNode->getValue()); } @@ -547,13 +555,13 @@ namespace hex::lang { if (auto sizeNumericExpression = dynamic_cast(node->getSize()); sizeNumericExpression != nullptr) valueNode = evaluateMathematicalExpression(sizeNumericExpression); else - throwEvaluateError("array size not a numeric expression", node->getLineNumber()); + throwEvaluateError("array size not a numeric expression"); SCOPE_EXIT( delete valueNode; ); arraySize = std::visit([node, type = valueNode->getType()] (auto &&value) { if (Token::isFloatingPoint(type)) - throwEvaluateError("array size must be an integer value", node->getLineNumber()); + throwEvaluateError("array size must be an integer value"); return static_cast(value); }, valueNode->getValue()); @@ -586,7 +594,7 @@ namespace hex::lang { entry = this->evaluateBuiltinType(builtinTypeDecl); } else - throwEvaluateError("ASTNodeVariableDecl had an invalid type. This is a bug!", 1); + throwEvaluateError("ASTNodeVariableDecl had an invalid type. This is a bug!"); entry->setVariableName(hex::format("[%llu]", (u64)i)); entry->setEndian(this->getCurrentEndian()); @@ -598,7 +606,7 @@ namespace hex::lang { entries.push_back(entry); if (this->m_currOffset >= this->m_provider->getActualSize()) - throwEvaluateError("array exceeds size of file", node->getLineNumber()); + throwEvaluateError("array exceeds size of file"); } PatternData *pattern; @@ -609,7 +617,7 @@ namespace hex::lang { pattern = new PatternDataString(startOffset, (this->m_currOffset - startOffset), color.value_or(0)); else { if (node->getSize() == nullptr) - throwEvaluateError("no bounds provided for array", node->getLineNumber()); + throwEvaluateError("no bounds provided for array"); pattern = new PatternDataArray(startOffset, (this->m_currOffset - startOffset), entries, color.value_or(0)); } @@ -626,7 +634,7 @@ namespace hex::lang { pointerOffset = std::visit([node, type = valueNode->getType()] (auto &&value) { if (Token::isFloatingPoint(type)) - throwEvaluateError("pointer offset must be an integer value", node->getLineNumber()); + throwEvaluateError("pointer offset must be an integer value"); return static_cast(value); }, valueNode->getValue()); this->m_currOffset = pointerOffset; @@ -638,12 +646,12 @@ namespace hex::lang { auto underlyingType = dynamic_cast(node->getSizeType()); if (underlyingType == nullptr) - throwEvaluateError("underlying type is not ASTNodeTypeDecl. This is a bug", node->getLineNumber()); + throwEvaluateError("underlying type is not ASTNodeTypeDecl. This is a bug"); if (auto builtinTypeNode = dynamic_cast(underlyingType->getType()); builtinTypeNode != nullptr) { sizeType = evaluateBuiltinType(builtinTypeNode); } else - throwEvaluateError("pointer size is not a builtin type", node->getLineNumber()); + throwEvaluateError("pointer size is not a builtin type"); size_t pointerSize = sizeType->getSize(); @@ -655,7 +663,7 @@ namespace hex::lang { if (this->m_currOffset > this->m_provider->getActualSize()) - throwEvaluateError("pointer points past the end of the data", 1); + throwEvaluateError("pointer points past the end of the data"); PatternData *pointedAt; if (auto typeDecl = dynamic_cast(node->getType()); typeDecl != nullptr) @@ -663,7 +671,7 @@ namespace hex::lang { else if (auto builtinTypeDecl = dynamic_cast(node->getType()); builtinTypeDecl != nullptr) pointedAt = this->evaluateBuiltinType(builtinTypeDecl); else - throwEvaluateError("ASTNodeVariableDecl had an invalid type. This is a bug!", 1); + throwEvaluateError("ASTNodeVariableDecl had an invalid type. This is a bug!"); this->m_currOffset = pointerOffset + pointerSize; @@ -697,7 +705,7 @@ namespace hex::lang { this->m_endianStack.pop_back(); } } catch (EvaluateError &e) { - this->m_error = e; + this->m_consoleLog.emplace_back(ConsoleLogLevel::Error, e); this->m_endianStack.clear(); return { }; diff --git a/source/views/view_pattern.cpp b/source/views/view_pattern.cpp index d905e4082..02b833aaf 100644 --- a/source/views/view_pattern.cpp +++ b/source/views/view_pattern.cpp @@ -28,6 +28,7 @@ namespace hex { { "s8", 1 }, { "s16", 2 }, { "s32", 4 }, { "s64", 8 }, { "s128", 16 }, { "float", 4 }, { "double", 8 }, { "char", 1 }, { "bool", 1 }, { "padding", 1 } }; + for (const auto &[name, size] : builtInTypes) { TextEditor::Identifier id; id.mDeclaration = std::to_string(size); @@ -52,6 +53,8 @@ namespace hex { paletteIndex = TextEditor::PaletteIndex::Number; else if (TokenizeCStyleCharacterLiteral(inBegin, inEnd, outBegin, outEnd)) paletteIndex = TextEditor::PaletteIndex::CharLiteral; + else if (TokenizeCStyleString(inBegin, inEnd, outBegin, outEnd)) + paletteIndex = TextEditor::PaletteIndex::String; return paletteIndex != TextEditor::PaletteIndex::Max; }; @@ -191,15 +194,33 @@ namespace hex { auto consoleSize = ImGui::GetContentRegionAvail(); ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.0, 0.0, 0.0, 1.0)); - ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0, 1.0, 1.0, 1.0)); if (ImGui::BeginChild("##console", consoleSize, true, ImGuiWindowFlags_AlwaysVerticalScrollbar)) { - for (auto &line : this->m_console) - ImGui::TextUnformatted(line.c_str()); + for (auto &[level, message] : this->m_console) { + switch (level) { + case lang::Evaluator::ConsoleLogLevel::Debug: + ImGui::PushStyleColor(ImGuiCol_Text, ImColor(0x1F, 0xA9, 0x49, 0xFF).Value); + break; + case lang::Evaluator::ConsoleLogLevel::Info: + ImGui::PushStyleColor(ImGuiCol_Text, ImColor(0x00, 0x70, 0xB4, 0xFF).Value); + break; + case lang::Evaluator::ConsoleLogLevel::Warning: + ImGui::PushStyleColor(ImGuiCol_Text, ImColor(0xFF, 0xC8, 0x01, 0xFF).Value); + break; + case lang::Evaluator::ConsoleLogLevel::Error: + ImGui::PushStyleColor(ImGuiCol_Text, ImColor(0xAE, 0x0C, 0x00, 0xFF).Value); + break; + default: continue; + } + + ImGui::TextUnformatted(message.c_str()); + + ImGui::PopStyleColor(); + } } ImGui::EndChild(); - ImGui::PopStyleColor(2); + ImGui::PopStyleColor(1); if (this->m_textEditor.IsTextChanged()) { this->parsePattern(this->m_textEditor.GetText().data()); @@ -320,11 +341,11 @@ namespace hex { auto provider = *SharedData::get().currentProvider; hex::lang::Evaluator evaluator(provider, defaultDataEndianess); + auto patternData = evaluator.evaluate(ast.value()); - if (!patternData.has_value()) { - this->m_console.push_back(evaluator.getError().second); + this->m_console = evaluator.getConsoleLog(); + if (!patternData.has_value()) return; - } this->m_patternData = patternData.value(); this->postEvent(Events::PatternChanged);