diff --git a/src/config.cpp b/src/config.cpp index 4569723..3dbd5a7 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -501,16 +501,71 @@ CParseResult CConfig::parseVariable(const std::string& lhs, const std::string& r return result; } -void CConfigImpl::parseComment(const std::string& comment) { +SVariable* CConfigImpl::getVariable(const std::string& name) { + for (auto& v : envVariables) { + if (v.name == name) + return &v; + } + + for (auto& v : variables) { + if (v.name == name) + return &v; + } + + return nullptr; +} + +std::optional CConfigImpl::parseComment(const std::string& comment) { const auto COMMENT = trim(comment); if (!COMMENT.starts_with("hyprlang")) - return; + return std::nullopt; - CVarList args(COMMENT, 0, 's', true); + CConstVarList args(COMMENT, 0, 's', true); - if (args[1] == "noerror") - currentFlags.noError = args[2] == "true" || args[2] == "yes" || args[2] == "enable" || args[2] == "enabled" || args[2] == "set"; + bool negated = false; + std::string ifBlockVariable = ""; + + for (size_t i = 1; i < args.size(); ++i) { + if (args[i] == "noerror") { + if (negated) + currentFlags.noError = false; + else + currentFlags.noError = args[2] == "true" || args[2] == "yes" || args[2] == "enable" || args[2] == "enabled" || args[2] == "set" || args[2].empty(); + break; + } + + if (args[i] == "!") { + negated = true; + continue; + } + + if (args[i] == "endif") { + if (!currentFlags.inAnIfBlock) + return "stray endif"; + currentFlags.inAnIfBlock = false; + break; + } + + if (args[i] == "if") { + ifBlockVariable = args[++i]; + break; + } + } + + if (!ifBlockVariable.empty()) { + if (currentFlags.inAnIfBlock) + return "nested if statements are not allowed"; + + currentFlags.inAnIfBlock = true; + + if (const auto VAR = getVariable(ifBlockVariable); VAR) + currentFlags.ifBlockFailed = !VAR->truthy(); + else + currentFlags.ifBlockFailed = true; + } + + return std::nullopt; } std::expected CConfigImpl::parseExpression(const std::string& s) { @@ -571,10 +626,15 @@ CParseResult CConfig::parseLine(std::string line, bool dynamic) { auto commentPos = line.find('#'); if (commentPos == 0) { - impl->parseComment(line.substr(1)); + const auto COMMENT_RESULT = impl->parseComment(line.substr(1)); + if (COMMENT_RESULT.has_value()) + result.setError(*COMMENT_RESULT); return result; } + if (impl->currentFlags.inAnIfBlock && impl->currentFlags.ifBlockFailed) + return result; + size_t lastHashPos = 0; while (commentPos != std::string::npos) { diff --git a/src/config.hpp b/src/config.hpp index c6e9c1e..63d9840 100644 --- a/src/config.hpp +++ b/src/config.hpp @@ -23,6 +23,10 @@ struct SVariable { }; std::vector linesContainingVar; // for dynamic updates + + bool truthy() { + return value.length() > 0; + } }; // remember to also edit CConfigValue if editing @@ -95,10 +99,13 @@ class CConfigImpl { Hyprlang::SConfigOptions configOptions; - void parseComment(const std::string& comment); + std::optional parseComment(const std::string& comment); std::expected parseExpression(const std::string& s); + SVariable* getVariable(const std::string& name); struct { - bool noError = false; + bool noError = false; + bool inAnIfBlock = false; + bool ifBlockFailed = false; } currentFlags; }; diff --git a/tests/config/config.conf b/tests/config/config.conf index fcbf620..516ff23 100644 --- a/tests/config/config.conf +++ b/tests/config/config.conf @@ -45,9 +45,19 @@ errorVariable = true # hyprlang noerror false +# hyprlang if NONEXISTENT_VAR + +customType = bcd + +# hyprlang endif + +# hyprlang if MY_VAR + categoryKeyword = oops, this one shouldn't call the handler, not fun testUseKeyword = yes +# hyprlang endif + testCategory { testValueInt = 123456 testValueHex = 0xF