New upstream version 0.5.1

This commit is contained in:
alan (NyxTrail) 2024-04-16 18:03:00 +00:00
parent bb479d7f9c
commit c602a9207e
7 changed files with 221 additions and 12 deletions

View File

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.19)
set(HYPRLANG_VERSION "0.5.0")
set(HYPRLANG_VERSION "0.5.1")
project(hyprlang
VERSION ${HYPRLANG_VERSION}

55
src/VarList.cpp Normal file
View File

@ -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;
}

63
src/VarList.hpp Normal file
View File

@ -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;
};

View File

@ -9,8 +9,16 @@
#include <sstream>
#include <cstring>
#include "VarList.hpp"
using namespace Hyprlang;
#ifdef __APPLE__
#include <crt_externs.h>
#define environ (*_NSGetEnviron())
#else
extern "C" char** environ;
#endif
// defines
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;
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()) {
// it might be in a special category
bool found = false;
@ -474,11 +512,31 @@ CParseResult CConfig::parseVariable(const std::string& lhs, const std::string& r
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 result;
auto commentPos = line.find('#');
size_t lastHashPos = 0;
line = removeBeginEndSpacesTabs(line);
auto commentPos = line.find('#');
if (commentPos == 0) {
impl->parseComment(line.substr(1));
return result;
}
size_t lastHashPos = 0;
while (commentPos != std::string::npos) {
bool escaped = false;
@ -500,9 +558,12 @@ CParseResult CConfig::parseLine(std::string line, bool dynamic) {
line = removeBeginEndSpacesTabs(line);
if (line.empty())
return result;
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
result.setError("Invalid config line");
return result;
@ -519,14 +580,14 @@ CParseResult CConfig::parseLine(std::string line, bool dynamic) {
return result;
}
if (*LHS.begin() == '$')
return parseVariable(LHS, RHS, dynamic);
const bool ISVARIABLE = *LHS.begin() == '$';
// limit unwrapping iterations to 100. if exceeds, raise error
for (size_t i = 0; i < 100; ++i) {
bool anyMatch = false;
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);
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;
for (auto& h : impl->handlers) {
if (!h.options.allowFlags && h.name != LHS)
@ -568,7 +632,7 @@ CParseResult CConfig::parseLine(std::string line, bool dynamic) {
if (ret.error)
return ret;
} else if (!line.empty()) {
} else {
// has to be a set
if (line.contains("}")) {
// easiest. } or invalid.
@ -689,7 +753,7 @@ CParseResult CConfig::parseFile(const char* file) {
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())
impl->parseError += "\n";
impl->parseError += std::format("Config error in file {} at line {}: {}", file, linenum, RET.errorStdString);

View File

@ -87,4 +87,10 @@ class CConfigImpl {
std::string parseError = "";
Hyprlang::SConfigOptions configOptions;
void parseComment(const std::string& comment);
struct {
bool noError = false;
} currentFlags;
};

View File

@ -1,5 +1,10 @@
# 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
testFloat = 123.456
@ -17,6 +22,12 @@ customType = abc
testStringColon = ee:ee:ee
# hyprlang noerror true
errorVariable = true
# hyprlang noerror false
testCategory {
testValueInt = 123456
testValueHex = 0xF
@ -40,8 +51,7 @@ special {
value = $SPECIALVAL1
}
special {
key = b
special[b] {
value = 2
}
@ -67,6 +77,10 @@ specialAnonymous {
testCategory:testValueHex = 0xFFfFaAbB
$RECURSIVE1 = a
$RECURSIVE2 = $RECURSIVE1b
testStringRecursive = $RECURSIVE2c
testStringQuotes = "Hello World!"
#testDefault = 123

View File

@ -89,6 +89,7 @@ int main(int argc, char** argv, char** envp) {
config.addConfigValue("testEnv", "");
config.addConfigValue("testVar", (Hyprlang::INT)0);
config.addConfigValue("testStringQuotes", "");
config.addConfigValue("testStringRecursive", "");
config.addConfigValue("testCategory:testValueInt", (Hyprlang::INT)0);
config.addConfigValue("testCategory:testValueHex", (Hyprlang::INT)0xA);
config.addConfigValue("testCategory:nested1:testValueNest", (Hyprlang::INT)0);
@ -178,12 +179,16 @@ int main(int argc, char** argv, char** envp) {
// test variables
std::cout << " → Testing variables\n";
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
std::cout << " → Testing dynamic variables\n";
EXPECT(config.parseDynamic("$MY_VAR_2 = 420").error, false);
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
std::cout << " → Testing env variables\n";
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("specialGeneric:one", "value")), 1);
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
EXPECT(config.parseDynamic("$SPECIALVAL1 = 2").error, false);