New upstream version 0.5.1
This commit is contained in:
parent
bb479d7f9c
commit
c602a9207e
|
|
@ -1,6 +1,6 @@
|
||||||
cmake_minimum_required(VERSION 3.19)
|
cmake_minimum_required(VERSION 3.19)
|
||||||
|
|
||||||
set(HYPRLANG_VERSION "0.5.0")
|
set(HYPRLANG_VERSION "0.5.1")
|
||||||
|
|
||||||
project(hyprlang
|
project(hyprlang
|
||||||
VERSION ${HYPRLANG_VERSION}
|
VERSION ${HYPRLANG_VERSION}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
#include "VarList.hpp"
|
||||||
|
#include <ranges>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
static std::string removeBeginEndSpacesTabs(std::string str) {
|
||||||
|
if (str.empty())
|
||||||
|
return str;
|
||||||
|
|
||||||
|
int countBefore = 0;
|
||||||
|
while (str[countBefore] == ' ' || str[countBefore] == '\t') {
|
||||||
|
countBefore++;
|
||||||
|
}
|
||||||
|
|
||||||
|
int countAfter = 0;
|
||||||
|
while ((int)str.length() - countAfter - 1 >= 0 && (str[str.length() - countAfter - 1] == ' ' || str[str.length() - 1 - countAfter] == '\t')) {
|
||||||
|
countAfter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
str = str.substr(countBefore, str.length() - countBefore - countAfter);
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
CVarList::CVarList(const std::string& in, const size_t lastArgNo, const char delim, const bool removeEmpty) {
|
||||||
|
if (in.empty())
|
||||||
|
m_vArgs.emplace_back("");
|
||||||
|
|
||||||
|
std::string args{in};
|
||||||
|
size_t idx = 0;
|
||||||
|
size_t pos = 0;
|
||||||
|
std::ranges::replace_if(
|
||||||
|
args, [&](const char& c) { return delim == 's' ? std::isspace(c) : c == delim; }, 0);
|
||||||
|
|
||||||
|
for (const auto& s : args | std::views::split(0)) {
|
||||||
|
if (removeEmpty && s.empty())
|
||||||
|
continue;
|
||||||
|
if (++idx == lastArgNo) {
|
||||||
|
m_vArgs.emplace_back(removeBeginEndSpacesTabs(in.substr(pos)));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pos += s.size() + 1;
|
||||||
|
m_vArgs.emplace_back(removeBeginEndSpacesTabs(std::string_view{s}.data()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string CVarList::join(const std::string& joiner, size_t from, size_t to) const {
|
||||||
|
size_t last = to == 0 ? size() : to;
|
||||||
|
|
||||||
|
std::string rolling;
|
||||||
|
for (size_t i = from; i < last; ++i) {
|
||||||
|
rolling += m_vArgs[i] + (i + 1 < last ? joiner : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
return rolling;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
#pragma once
|
||||||
|
#include <functional>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class CVarList {
|
||||||
|
public:
|
||||||
|
/** Split string into arg list
|
||||||
|
@param lastArgNo stop splitting after argv reaches maximum size, last arg will contain rest of unsplit args
|
||||||
|
@param delim if delimiter is 's', use std::isspace
|
||||||
|
@param removeEmpty remove empty args from argv
|
||||||
|
*/
|
||||||
|
CVarList(const std::string& in, const size_t maxSize = 0, const char delim = ',', const bool removeEmpty = false);
|
||||||
|
|
||||||
|
~CVarList() = default;
|
||||||
|
|
||||||
|
size_t size() const {
|
||||||
|
return m_vArgs.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string join(const std::string& joiner, size_t from = 0, size_t to = 0) const;
|
||||||
|
|
||||||
|
void map(std::function<void(std::string&)> func) {
|
||||||
|
for (auto& s : m_vArgs)
|
||||||
|
func(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
void append(const std::string arg) {
|
||||||
|
m_vArgs.emplace_back(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string operator[](const size_t& idx) const {
|
||||||
|
if (idx >= m_vArgs.size())
|
||||||
|
return "";
|
||||||
|
return m_vArgs[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
// for range-based loops
|
||||||
|
std::vector<std::string>::iterator begin() {
|
||||||
|
return m_vArgs.begin();
|
||||||
|
}
|
||||||
|
std::vector<std::string>::const_iterator begin() const {
|
||||||
|
return m_vArgs.begin();
|
||||||
|
}
|
||||||
|
std::vector<std::string>::iterator end() {
|
||||||
|
return m_vArgs.end();
|
||||||
|
}
|
||||||
|
std::vector<std::string>::const_iterator end() const {
|
||||||
|
return m_vArgs.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool contains(const std::string& el) {
|
||||||
|
for (auto& a : m_vArgs) {
|
||||||
|
if (a == el)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<std::string> m_vArgs;
|
||||||
|
};
|
||||||
|
|
@ -9,8 +9,16 @@
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "VarList.hpp"
|
||||||
|
|
||||||
using namespace Hyprlang;
|
using namespace Hyprlang;
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#include <crt_externs.h>
|
||||||
|
#define environ (*_NSGetEnviron())
|
||||||
|
#else
|
||||||
extern "C" char** environ;
|
extern "C" char** environ;
|
||||||
|
#endif
|
||||||
|
|
||||||
// defines
|
// defines
|
||||||
inline constexpr const char* ANONYMOUS_KEY = "__hyprlang_internal_anonymous_key";
|
inline constexpr const char* ANONYMOUS_KEY = "__hyprlang_internal_anonymous_key";
|
||||||
|
|
@ -295,7 +303,37 @@ CParseResult CConfig::configSetValueSafe(const std::string& command, const std::
|
||||||
|
|
||||||
const auto VALUEONLYNAME = command.starts_with(catPrefix) ? command.substr(catPrefix.length()) : command;
|
const auto VALUEONLYNAME = command.starts_with(catPrefix) ? command.substr(catPrefix.length()) : command;
|
||||||
|
|
||||||
auto VALUEIT = impl->values.find(valueName);
|
// FIXME: this will bug with nested.
|
||||||
|
if (valueName.contains('[') && valueName.contains(']')) {
|
||||||
|
const auto L = valueName.find_first_of('[');
|
||||||
|
const auto R = valueName.find_last_of(']');
|
||||||
|
|
||||||
|
if (L < R) {
|
||||||
|
const auto CATKEY = valueName.substr(L + 1, R - L - 1);
|
||||||
|
impl->currentSpecialKey = CATKEY;
|
||||||
|
|
||||||
|
valueName = valueName.substr(0, L) + valueName.substr(R + 1);
|
||||||
|
|
||||||
|
// if it doesn't exist, make it
|
||||||
|
for (auto& sc : impl->specialCategoryDescriptors) {
|
||||||
|
if (sc->key.empty() || !valueName.starts_with(sc->name))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// bingo
|
||||||
|
const auto PCAT = impl->specialCategories.emplace_back(std::make_unique<SSpecialCategory>()).get();
|
||||||
|
PCAT->descriptor = sc.get();
|
||||||
|
PCAT->name = sc->name;
|
||||||
|
PCAT->key = sc->key;
|
||||||
|
addSpecialConfigValue(sc->name.c_str(), sc->key.c_str(), CConfigValue(CATKEY.c_str()));
|
||||||
|
|
||||||
|
applyDefaultsToCat(*PCAT);
|
||||||
|
|
||||||
|
PCAT->values[sc->key].setFrom(CATKEY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto VALUEIT = impl->values.find(valueName);
|
||||||
if (VALUEIT == impl->values.end()) {
|
if (VALUEIT == impl->values.end()) {
|
||||||
// it might be in a special category
|
// it might be in a special category
|
||||||
bool found = false;
|
bool found = false;
|
||||||
|
|
@ -474,11 +512,31 @@ CParseResult CConfig::parseVariable(const std::string& lhs, const std::string& r
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CConfigImpl::parseComment(const std::string& comment) {
|
||||||
|
const auto COMMENT = removeBeginEndSpacesTabs(comment);
|
||||||
|
|
||||||
|
if (!COMMENT.starts_with("hyprlang"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
CVarList 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";
|
||||||
|
}
|
||||||
|
|
||||||
CParseResult CConfig::parseLine(std::string line, bool dynamic) {
|
CParseResult CConfig::parseLine(std::string line, bool dynamic) {
|
||||||
CParseResult result;
|
CParseResult result;
|
||||||
|
|
||||||
auto commentPos = line.find('#');
|
line = removeBeginEndSpacesTabs(line);
|
||||||
size_t lastHashPos = 0;
|
|
||||||
|
auto commentPos = line.find('#');
|
||||||
|
|
||||||
|
if (commentPos == 0) {
|
||||||
|
impl->parseComment(line.substr(1));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t lastHashPos = 0;
|
||||||
|
|
||||||
while (commentPos != std::string::npos) {
|
while (commentPos != std::string::npos) {
|
||||||
bool escaped = false;
|
bool escaped = false;
|
||||||
|
|
@ -500,9 +558,12 @@ CParseResult CConfig::parseLine(std::string line, bool dynamic) {
|
||||||
|
|
||||||
line = removeBeginEndSpacesTabs(line);
|
line = removeBeginEndSpacesTabs(line);
|
||||||
|
|
||||||
|
if (line.empty())
|
||||||
|
return result;
|
||||||
|
|
||||||
auto equalsPos = line.find('=');
|
auto equalsPos = line.find('=');
|
||||||
|
|
||||||
if (equalsPos == std::string::npos && !line.ends_with("{") && line != "}" && !line.empty()) {
|
if (equalsPos == std::string::npos && !line.ends_with("{") && line != "}") {
|
||||||
// invalid line
|
// invalid line
|
||||||
result.setError("Invalid config line");
|
result.setError("Invalid config line");
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -519,14 +580,14 @@ CParseResult CConfig::parseLine(std::string line, bool dynamic) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*LHS.begin() == '$')
|
const bool ISVARIABLE = *LHS.begin() == '$';
|
||||||
return parseVariable(LHS, RHS, 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;
|
||||||
for (auto& var : impl->variables) {
|
for (auto& var : impl->variables) {
|
||||||
const auto LHSIT = LHS.find("$" + var.name);
|
// don't parse LHS variables if this is a variable...
|
||||||
|
const auto LHSIT = ISVARIABLE ? std::string::npos : LHS.find("$" + var.name);
|
||||||
const auto RHSIT = RHS.find("$" + var.name);
|
const auto RHSIT = RHS.find("$" + var.name);
|
||||||
|
|
||||||
if (LHSIT != std::string::npos)
|
if (LHSIT != std::string::npos)
|
||||||
|
|
@ -551,6 +612,9 @@ CParseResult CConfig::parseLine(std::string line, bool dynamic) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ISVARIABLE)
|
||||||
|
return parseVariable(LHS, RHS, dynamic);
|
||||||
|
|
||||||
bool found = false;
|
bool found = false;
|
||||||
for (auto& h : impl->handlers) {
|
for (auto& h : impl->handlers) {
|
||||||
if (!h.options.allowFlags && h.name != LHS)
|
if (!h.options.allowFlags && h.name != LHS)
|
||||||
|
|
@ -568,7 +632,7 @@ CParseResult CConfig::parseLine(std::string line, bool dynamic) {
|
||||||
|
|
||||||
if (ret.error)
|
if (ret.error)
|
||||||
return ret;
|
return ret;
|
||||||
} else if (!line.empty()) {
|
} else {
|
||||||
// has to be a set
|
// has to be a set
|
||||||
if (line.contains("}")) {
|
if (line.contains("}")) {
|
||||||
// easiest. } or invalid.
|
// easiest. } or invalid.
|
||||||
|
|
@ -689,7 +753,7 @@ CParseResult CConfig::parseFile(const char* file) {
|
||||||
|
|
||||||
const auto RET = parseLine(line);
|
const auto RET = parseLine(line);
|
||||||
|
|
||||||
if (RET.error && (impl->parseError.empty() || impl->configOptions.throwAllErrors)) {
|
if (!impl->currentFlags.noError && RET.error && (impl->parseError.empty() || impl->configOptions.throwAllErrors)) {
|
||||||
if (!impl->parseError.empty())
|
if (!impl->parseError.empty())
|
||||||
impl->parseError += "\n";
|
impl->parseError += "\n";
|
||||||
impl->parseError += std::format("Config error in file {} at line {}: {}", file, linenum, RET.errorStdString);
|
impl->parseError += std::format("Config error in file {} at line {}: {}", file, linenum, RET.errorStdString);
|
||||||
|
|
|
||||||
|
|
@ -87,4 +87,10 @@ class CConfigImpl {
|
||||||
std::string parseError = "";
|
std::string parseError = "";
|
||||||
|
|
||||||
Hyprlang::SConfigOptions configOptions;
|
Hyprlang::SConfigOptions configOptions;
|
||||||
|
|
||||||
|
void parseComment(const std::string& comment);
|
||||||
|
|
||||||
|
struct {
|
||||||
|
bool noError = false;
|
||||||
|
} currentFlags;
|
||||||
};
|
};
|
||||||
|
|
@ -1,5 +1,10 @@
|
||||||
|
|
||||||
# Test comment
|
# Test comment
|
||||||
|
## This is also a comment
|
||||||
|
## This is a comment with space as a first character
|
||||||
|
## This is a comment with tab as a first character
|
||||||
|
## This is a comment with leading spaces and tabs
|
||||||
|
##### Comment with more hash tags
|
||||||
|
|
||||||
testInt = 123
|
testInt = 123
|
||||||
testFloat = 123.456
|
testFloat = 123.456
|
||||||
|
|
@ -17,6 +22,12 @@ customType = abc
|
||||||
|
|
||||||
testStringColon = ee:ee:ee
|
testStringColon = ee:ee:ee
|
||||||
|
|
||||||
|
# hyprlang noerror true
|
||||||
|
|
||||||
|
errorVariable = true
|
||||||
|
|
||||||
|
# hyprlang noerror false
|
||||||
|
|
||||||
testCategory {
|
testCategory {
|
||||||
testValueInt = 123456
|
testValueInt = 123456
|
||||||
testValueHex = 0xF
|
testValueHex = 0xF
|
||||||
|
|
@ -40,8 +51,7 @@ special {
|
||||||
value = $SPECIALVAL1
|
value = $SPECIALVAL1
|
||||||
}
|
}
|
||||||
|
|
||||||
special {
|
special[b] {
|
||||||
key = b
|
|
||||||
value = 2
|
value = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -67,6 +77,10 @@ specialAnonymous {
|
||||||
|
|
||||||
testCategory:testValueHex = 0xFFfFaAbB
|
testCategory:testValueHex = 0xFFfFaAbB
|
||||||
|
|
||||||
|
$RECURSIVE1 = a
|
||||||
|
$RECURSIVE2 = $RECURSIVE1b
|
||||||
|
testStringRecursive = $RECURSIVE2c
|
||||||
|
|
||||||
testStringQuotes = "Hello World!"
|
testStringQuotes = "Hello World!"
|
||||||
#testDefault = 123
|
#testDefault = 123
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,7 @@ int main(int argc, char** argv, char** envp) {
|
||||||
config.addConfigValue("testEnv", "");
|
config.addConfigValue("testEnv", "");
|
||||||
config.addConfigValue("testVar", (Hyprlang::INT)0);
|
config.addConfigValue("testVar", (Hyprlang::INT)0);
|
||||||
config.addConfigValue("testStringQuotes", "");
|
config.addConfigValue("testStringQuotes", "");
|
||||||
|
config.addConfigValue("testStringRecursive", "");
|
||||||
config.addConfigValue("testCategory:testValueInt", (Hyprlang::INT)0);
|
config.addConfigValue("testCategory:testValueInt", (Hyprlang::INT)0);
|
||||||
config.addConfigValue("testCategory:testValueHex", (Hyprlang::INT)0xA);
|
config.addConfigValue("testCategory:testValueHex", (Hyprlang::INT)0xA);
|
||||||
config.addConfigValue("testCategory:nested1:testValueNest", (Hyprlang::INT)0);
|
config.addConfigValue("testCategory:nested1:testValueNest", (Hyprlang::INT)0);
|
||||||
|
|
@ -178,12 +179,16 @@ int main(int argc, char** argv, char** envp) {
|
||||||
// test variables
|
// test variables
|
||||||
std::cout << " → Testing variables\n";
|
std::cout << " → Testing variables\n";
|
||||||
EXPECT(std::any_cast<int64_t>(config.getConfigValue("testVar")), 13371337);
|
EXPECT(std::any_cast<int64_t>(config.getConfigValue("testVar")), 13371337);
|
||||||
|
EXPECT(std::any_cast<const char*>(config.getConfigValue("testStringRecursive")), std::string{"abc"});
|
||||||
|
|
||||||
// test dynamic variables
|
// test dynamic variables
|
||||||
std::cout << " → Testing dynamic variables\n";
|
std::cout << " → Testing dynamic variables\n";
|
||||||
EXPECT(config.parseDynamic("$MY_VAR_2 = 420").error, false);
|
EXPECT(config.parseDynamic("$MY_VAR_2 = 420").error, false);
|
||||||
EXPECT(std::any_cast<int64_t>(config.getConfigValue("testVar")), 1337420);
|
EXPECT(std::any_cast<int64_t>(config.getConfigValue("testVar")), 1337420);
|
||||||
|
|
||||||
|
EXPECT(config.parseDynamic("$RECURSIVE1 = d").error, false);
|
||||||
|
EXPECT(std::any_cast<const char*>(config.getConfigValue("testStringRecursive")), std::string{"dbc"});
|
||||||
|
|
||||||
// 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")});
|
||||||
|
|
@ -194,6 +199,8 @@ int main(int argc, char** argv, char** envp) {
|
||||||
EXPECT(std::any_cast<int64_t>(config.getSpecialConfigValue("special", "value", "b")), 2);
|
EXPECT(std::any_cast<int64_t>(config.getSpecialConfigValue("special", "value", "b")), 2);
|
||||||
EXPECT(std::any_cast<int64_t>(config.getSpecialConfigValue("specialGeneric:one", "value")), 1);
|
EXPECT(std::any_cast<int64_t>(config.getSpecialConfigValue("specialGeneric:one", "value")), 1);
|
||||||
EXPECT(std::any_cast<int64_t>(config.getSpecialConfigValue("specialGeneric:two", "value")), 2);
|
EXPECT(std::any_cast<int64_t>(config.getSpecialConfigValue("specialGeneric:two", "value")), 2);
|
||||||
|
EXPECT(config.parseDynamic("special[b]:value = 3").error, false);
|
||||||
|
EXPECT(std::any_cast<int64_t>(config.getSpecialConfigValue("special", "value", "b")), 3);
|
||||||
|
|
||||||
// test dynamic special variable
|
// test dynamic special variable
|
||||||
EXPECT(config.parseDynamic("$SPECIALVAL1 = 2").error, false);
|
EXPECT(config.parseDynamic("$SPECIALVAL1 = 2").error, false);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue