mirror of
https://github.com/hyprwm/hyprlang.git
synced 2025-12-20 03:50:02 +01:00
parser: add support for basic arithmetic
Adds support for expressions that take left and right hand side and an operation (+-*/) -> e.g. fixes #67
This commit is contained in:
parent
397600c42b
commit
6726cfd54b
5 changed files with 89 additions and 6 deletions
|
|
@ -37,7 +37,7 @@ add_compile_options(
|
||||||
set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE)
|
set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE)
|
||||||
|
|
||||||
find_package(PkgConfig REQUIRED)
|
find_package(PkgConfig REQUIRED)
|
||||||
pkg_check_modules(deps REQUIRED IMPORTED_TARGET hyprutils>=0.1.1)
|
pkg_check_modules(deps REQUIRED IMPORTED_TARGET hyprutils>=0.7.1)
|
||||||
|
|
||||||
file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp" "include/hyprlang.hpp")
|
file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp" "include/hyprlang.hpp")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <hyprutils/string/VarList.hpp>
|
#include <hyprutils/string/VarList.hpp>
|
||||||
#include <hyprutils/string/String.hpp>
|
#include <hyprutils/string/String.hpp>
|
||||||
|
#include <hyprutils/string/ConstVarList.hpp>
|
||||||
|
|
||||||
using namespace Hyprlang;
|
using namespace Hyprlang;
|
||||||
using namespace Hyprutils::String;
|
using namespace Hyprutils::String;
|
||||||
|
|
@ -510,6 +511,56 @@ void CConfigImpl::parseComment(const std::string& comment) {
|
||||||
currentFlags.noError = args[2] == "true" || args[2] == "yes" || args[2] == "enable" || args[2] == "enabled" || args[2] == "set";
|
currentFlags.noError = args[2] == "true" || args[2] == "yes" || args[2] == "enable" || args[2] == "enabled" || args[2] == "set";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::expected<float, std::string> CConfigImpl::parseExpression(const std::string& s) {
|
||||||
|
// for now, we only support very basic expressions.
|
||||||
|
// + - * / and only one per $()
|
||||||
|
// TODO: something better
|
||||||
|
|
||||||
|
if (s.empty())
|
||||||
|
return std::unexpected("Expression is empty");
|
||||||
|
|
||||||
|
CConstVarList args(s, 0, 's', true);
|
||||||
|
|
||||||
|
if (args[1] != "+" && args[1] != "-" && args[1] != "*" && args[1] != "/")
|
||||||
|
return std::unexpected("Invalid expression type: supported +, -, *, /");
|
||||||
|
|
||||||
|
auto LHS_VAR = std::ranges::find_if(variables, [&](const auto& v) { return v.name == args[0]; });
|
||||||
|
auto RHS_VAR = std::ranges::find_if(variables, [&](const auto& v) { return v.name == args[2]; });
|
||||||
|
|
||||||
|
float left = 0;
|
||||||
|
float right = 0;
|
||||||
|
|
||||||
|
if (LHS_VAR != variables.end()) {
|
||||||
|
try {
|
||||||
|
left = std::stof(LHS_VAR->value);
|
||||||
|
} catch (...) { return std::unexpected("Failed to parse expression: value 1 holds a variable that does not look like a number"); }
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
left = std::stof(std::string{args[0]});
|
||||||
|
} catch (...) { return std::unexpected("Failed to parse expression: value 1 does not look like a number or the variable doesn't exist"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (RHS_VAR != variables.end()) {
|
||||||
|
try {
|
||||||
|
right = std::stof(RHS_VAR->value);
|
||||||
|
} catch (...) { return std::unexpected("Failed to parse expression: value 1 holds a variable that does not look like a number"); }
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
right = std::stof(std::string{args[2]});
|
||||||
|
} catch (...) { return std::unexpected("Failed to parse expression: value 1 does not look like a number or the variable doesn't exist"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (args[1][0]) {
|
||||||
|
case '+': return left + right;
|
||||||
|
case '-': return left - right;
|
||||||
|
case '*': return left * right;
|
||||||
|
case '/': return left / right;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::unexpected("Unknown error while parsing expression");
|
||||||
|
}
|
||||||
|
|
||||||
CParseResult CConfig::parseLine(std::string line, bool dynamic) {
|
CParseResult CConfig::parseLine(std::string line, bool dynamic) {
|
||||||
CParseResult result;
|
CParseResult result;
|
||||||
|
|
||||||
|
|
@ -571,6 +622,8 @@ CParseResult CConfig::parseLine(std::string line, bool dynamic) {
|
||||||
// limit unwrapping iterations to 100. if exceeds, raise error
|
// limit unwrapping iterations to 100. if exceeds, raise error
|
||||||
for (size_t i = 0; i < 100; ++i) {
|
for (size_t i = 0; i < 100; ++i) {
|
||||||
bool anyMatch = false;
|
bool anyMatch = false;
|
||||||
|
|
||||||
|
// parse variables
|
||||||
for (auto& var : impl->variables) {
|
for (auto& var : impl->variables) {
|
||||||
// don't parse LHS variables if this is a variable...
|
// don't parse LHS variables if this is a variable...
|
||||||
const auto LHSIT = ISVARIABLE ? std::string::npos : LHS.find("$" + var.name);
|
const auto LHSIT = ISVARIABLE ? std::string::npos : LHS.find("$" + var.name);
|
||||||
|
|
@ -589,6 +642,24 @@ CParseResult CConfig::parseLine(std::string line, bool dynamic) {
|
||||||
anyMatch = true;
|
anyMatch = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parse expressions $(somevar + 2)
|
||||||
|
// We only support single expressions for now
|
||||||
|
while (RHS.contains("$(")) {
|
||||||
|
const auto BEGIN_EXPR = RHS.find("$(");
|
||||||
|
const auto END_EXPR = RHS.find(')', BEGIN_EXPR + 2);
|
||||||
|
if (END_EXPR != std::string::npos) {
|
||||||
|
// try to parse the expression
|
||||||
|
const auto RESULT = impl->parseExpression(RHS.substr(BEGIN_EXPR + 2, END_EXPR - BEGIN_EXPR - 2));
|
||||||
|
if (!RESULT.has_value()) {
|
||||||
|
result.setError(RESULT.error());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
RHS = RHS.substr(0, BEGIN_EXPR) + std::format("{}", RESULT.value()) + RHS.substr(END_EXPR + 1);
|
||||||
|
} else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (!anyMatch)
|
if (!anyMatch)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
@ -728,8 +799,7 @@ CParseResult CConfig::parseRawStream(const std::string& stream) {
|
||||||
|
|
||||||
if (!line) {
|
if (!line) {
|
||||||
switch (line.error()) {
|
switch (line.error()) {
|
||||||
case GETNEXTLINEFAILURE_EOF:
|
case GETNEXTLINEFAILURE_EOF: break;
|
||||||
break;
|
|
||||||
case GETNEXTLINEFAILURE_BACKSLASH:
|
case GETNEXTLINEFAILURE_BACKSLASH:
|
||||||
if (!impl->parseError.empty())
|
if (!impl->parseError.empty())
|
||||||
impl->parseError += "\n";
|
impl->parseError += "\n";
|
||||||
|
|
@ -781,8 +851,7 @@ CParseResult CConfig::parseFile(const char* file) {
|
||||||
|
|
||||||
if (!line) {
|
if (!line) {
|
||||||
switch (line.error()) {
|
switch (line.error()) {
|
||||||
case GETNEXTLINEFAILURE_EOF:
|
case GETNEXTLINEFAILURE_EOF: break;
|
||||||
break;
|
|
||||||
case GETNEXTLINEFAILURE_BACKSLASH:
|
case GETNEXTLINEFAILURE_BACKSLASH:
|
||||||
if (!impl->parseError.empty())
|
if (!impl->parseError.empty())
|
||||||
impl->parseError += "\n";
|
impl->parseError += "\n";
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <expected>
|
||||||
|
|
||||||
struct SHandler {
|
struct SHandler {
|
||||||
std::string name = "";
|
std::string name = "";
|
||||||
|
|
@ -95,6 +96,7 @@ class CConfigImpl {
|
||||||
Hyprlang::SConfigOptions configOptions;
|
Hyprlang::SConfigOptions configOptions;
|
||||||
|
|
||||||
void parseComment(const std::string& comment);
|
void parseComment(const std::string& comment);
|
||||||
|
std::expected<float, std::string> parseExpression(const std::string& s);
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
bool noError = false;
|
bool noError = false;
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,9 @@ $MY_VAR = 1337
|
||||||
$MY_VAR_2 = $MY_VAR
|
$MY_VAR_2 = $MY_VAR
|
||||||
testVar = $MY_VAR$MY_VAR_2
|
testVar = $MY_VAR$MY_VAR_2
|
||||||
|
|
||||||
|
$EXPR_VAR = $(MY_VAR + 2)
|
||||||
|
testExpr = $(EXPR_VAR - 4)
|
||||||
|
|
||||||
testEnv = $SHELL
|
testEnv = $SHELL
|
||||||
|
|
||||||
source = ./colors.conf
|
source = ./colors.conf
|
||||||
|
|
|
||||||
|
|
@ -107,6 +107,7 @@ int main(int argc, char** argv, char** envp) {
|
||||||
|
|
||||||
// setup config
|
// setup config
|
||||||
config.addConfigValue("testInt", (Hyprlang::INT)0);
|
config.addConfigValue("testInt", (Hyprlang::INT)0);
|
||||||
|
config.addConfigValue("testExpr", (Hyprlang::INT)0);
|
||||||
config.addConfigValue("testFloat", 0.F);
|
config.addConfigValue("testFloat", 0.F);
|
||||||
config.addConfigValue("testVec", Hyprlang::SVector2D{.x = 69, .y = 420});
|
config.addConfigValue("testVec", Hyprlang::SVector2D{.x = 69, .y = 420});
|
||||||
config.addConfigValue("testString", "");
|
config.addConfigValue("testString", "");
|
||||||
|
|
@ -197,6 +198,10 @@ int main(int argc, char** argv, char** envp) {
|
||||||
EXPECT(*T3, EXP);
|
EXPECT(*T3, EXP);
|
||||||
EXPECT(*T4, "Hello World! # This is not a comment!");
|
EXPECT(*T4, "Hello World! # This is not a comment!");
|
||||||
|
|
||||||
|
// test expressions
|
||||||
|
std::cout << " → Testing expressions\n";
|
||||||
|
EXPECT(std::any_cast<int64_t>(config.getConfigValue("testExpr")), 1335);
|
||||||
|
|
||||||
// test static values
|
// test static values
|
||||||
std::cout << " → Testing static values\n";
|
std::cout << " → Testing static values\n";
|
||||||
static auto* const PTESTINT = config.getConfigValuePtr("testInt")->getDataStaticPtr();
|
static auto* const PTESTINT = config.getConfigValuePtr("testInt")->getDataStaticPtr();
|
||||||
|
|
@ -243,6 +248,10 @@ int main(int argc, char** argv, char** envp) {
|
||||||
EXPECT(config.parseDynamic("$RECURSIVE1 = d").error, false);
|
EXPECT(config.parseDynamic("$RECURSIVE1 = d").error, false);
|
||||||
EXPECT(std::any_cast<const char*>(config.getConfigValue("testStringRecursive")), std::string{"dbc"});
|
EXPECT(std::any_cast<const char*>(config.getConfigValue("testStringRecursive")), std::string{"dbc"});
|
||||||
|
|
||||||
|
// test dynamic exprs
|
||||||
|
EXPECT(config.parseDynamic("testExpr = $(EXPR_VAR * 2)").error, false);
|
||||||
|
EXPECT(std::any_cast<int64_t>(config.getConfigValue("testExpr")), 1339L * 2);
|
||||||
|
|
||||||
// test env variables
|
// test env variables
|
||||||
std::cout << " → Testing env variables\n";
|
std::cout << " → Testing env variables\n";
|
||||||
EXPECT(std::any_cast<const char*>(config.getConfigValue("testEnv")), std::string{getenv("SHELL")});
|
EXPECT(std::any_cast<const char*>(config.getConfigValue("testEnv")), std::string{getenv("SHELL")});
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue