mirror of
https://github.com/hyprwm/hyprlang.git
synced 2025-05-13 13:50:38 +01:00
Compare commits
No commits in common. "main" and "v0.5.1" have entirely different histories.
21 changed files with 385 additions and 752 deletions
101
.clang-tidy
101
.clang-tidy
|
@ -1,101 +0,0 @@
|
|||
WarningsAsErrors: '*'
|
||||
HeaderFilterRegex: '.*\.hpp'
|
||||
FormatStyle: 'file'
|
||||
Checks: >
|
||||
-*,
|
||||
bugprone-*,
|
||||
-bugprone-easily-swappable-parameters,
|
||||
-bugprone-forward-declaration-namespace,
|
||||
-bugprone-forward-declaration-namespace,
|
||||
-bugprone-macro-parentheses,
|
||||
-bugprone-narrowing-conversions,
|
||||
-bugprone-branch-clone,
|
||||
-bugprone-assignment-in-if-condition,
|
||||
concurrency-*,
|
||||
-concurrency-mt-unsafe,
|
||||
cppcoreguidelines-*,
|
||||
-cppcoreguidelines-owning-memory,
|
||||
-cppcoreguidelines-avoid-magic-numbers,
|
||||
-cppcoreguidelines-pro-bounds-constant-array-index,
|
||||
-cppcoreguidelines-avoid-const-or-ref-data-members,
|
||||
-cppcoreguidelines-non-private-member-variables-in-classes,
|
||||
-cppcoreguidelines-avoid-goto,
|
||||
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
|
||||
-cppcoreguidelines-avoid-do-while,
|
||||
-cppcoreguidelines-avoid-non-const-global-variables,
|
||||
-cppcoreguidelines-special-member-functions,
|
||||
-cppcoreguidelines-explicit-virtual-functions,
|
||||
-cppcoreguidelines-avoid-c-arrays,
|
||||
-cppcoreguidelines-pro-bounds-pointer-arithmetic,
|
||||
-cppcoreguidelines-narrowing-conversions,
|
||||
-cppcoreguidelines-pro-type-union-access,
|
||||
-cppcoreguidelines-pro-type-member-init,
|
||||
-cppcoreguidelines-macro-usage,
|
||||
-cppcoreguidelines-macro-to-enum,
|
||||
-cppcoreguidelines-init-variables,
|
||||
-cppcoreguidelines-pro-type-cstyle-cast,
|
||||
-cppcoreguidelines-pro-type-vararg,
|
||||
-cppcoreguidelines-pro-type-reinterpret-cast,
|
||||
google-global-names-in-headers,
|
||||
-google-readability-casting,
|
||||
google-runtime-operator,
|
||||
misc-*,
|
||||
-misc-unused-parameters,
|
||||
-misc-no-recursion,
|
||||
-misc-non-private-member-variables-in-classes,
|
||||
-misc-include-cleaner,
|
||||
-misc-use-anonymous-namespace,
|
||||
-misc-const-correctness,
|
||||
modernize-*,
|
||||
-modernize-return-braced-init-list,
|
||||
-modernize-use-trailing-return-type,
|
||||
-modernize-use-using,
|
||||
-modernize-use-override,
|
||||
-modernize-avoid-c-arrays,
|
||||
-modernize-macro-to-enum,
|
||||
-modernize-loop-convert,
|
||||
-modernize-use-nodiscard,
|
||||
-modernize-pass-by-value,
|
||||
-modernize-use-auto,
|
||||
performance-*,
|
||||
-performance-avoid-endl,
|
||||
-performance-unnecessary-value-param,
|
||||
portability-std-allocator-const,
|
||||
readability-*,
|
||||
-readability-function-cognitive-complexity,
|
||||
-readability-function-size,
|
||||
-readability-identifier-length,
|
||||
-readability-magic-numbers,
|
||||
-readability-uppercase-literal-suffix,
|
||||
-readability-braces-around-statements,
|
||||
-readability-redundant-access-specifiers,
|
||||
-readability-else-after-return,
|
||||
-readability-container-data-pointer,
|
||||
-readability-implicit-bool-conversion,
|
||||
-readability-avoid-nested-conditional-operator,
|
||||
-readability-redundant-member-init,
|
||||
-readability-redundant-string-init,
|
||||
-readability-avoid-const-params-in-decls,
|
||||
-readability-named-parameter,
|
||||
-readability-convert-member-functions-to-static,
|
||||
-readability-qualified-auto,
|
||||
-readability-make-member-function-const,
|
||||
-readability-isolate-declaration,
|
||||
-readability-inconsistent-declaration-parameter-name,
|
||||
-clang-diagnostic-error,
|
||||
|
||||
CheckOptions:
|
||||
performance-for-range-copy.WarnOnAllAutoCopies: true
|
||||
performance-inefficient-string-concatenation.StrictMode: true
|
||||
readability-braces-around-statements.ShortStatementLines: 0
|
||||
readability-identifier-naming.ClassCase: CamelCase
|
||||
readability-identifier-naming.ClassIgnoredRegexp: I.*
|
||||
readability-identifier-naming.ClassPrefix: C # We can't use regex here?!?!?!?
|
||||
readability-identifier-naming.EnumCase: CamelCase
|
||||
readability-identifier-naming.EnumPrefix: e
|
||||
readability-identifier-naming.EnumConstantCase: UPPER_CASE
|
||||
readability-identifier-naming.FunctionCase: camelBack
|
||||
readability-identifier-naming.NamespaceCase: CamelCase
|
||||
readability-identifier-naming.NamespacePrefix: N
|
||||
readability-identifier-naming.StructPrefix: S
|
||||
readability-identifier-naming.StructCase: CamelCase
|
59
.github/workflows/arch.yml
vendored
59
.github/workflows/arch.yml
vendored
|
@ -2,6 +2,39 @@ name: Build & Test (Arch)
|
|||
|
||||
on: [push, pull_request, workflow_dispatch]
|
||||
jobs:
|
||||
gcc:
|
||||
name: "gcc build / clang test"
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: archlinux
|
||||
steps:
|
||||
- name: Checkout repository actions
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: .github/actions
|
||||
|
||||
- name: Get required pkgs
|
||||
run: |
|
||||
sed -i 's/SigLevel = Required DatabaseOptional/SigLevel = Optional TrustAll/' /etc/pacman.conf
|
||||
pacman --noconfirm --noprogressbar -Syyu
|
||||
pacman --noconfirm --noprogressbar -Sy gcc base-devel cmake clang
|
||||
|
||||
- name: Build hyprlang with gcc
|
||||
run: |
|
||||
CC="/usr/bin/gcc" CXX="/usr/bin/g++" cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -S . -B ./build
|
||||
CC="/usr/bin/gcc" CXX="/usr/bin/g++" cmake --build ./build --config Release --target hyprlang -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
|
||||
cmake --install ./build
|
||||
|
||||
- name: Build tests with clang
|
||||
run: |
|
||||
rm -rf ./build
|
||||
CC="/usr/bin/clang" CXX="/usr/bin/clang++" cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -S . -B ./build
|
||||
CC="/usr/bin/clang" CXX="/usr/bin/clang++" cmake --build ./build --config Release --target tests -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
cd ./build && ctest --output-on-failure
|
||||
|
||||
asan:
|
||||
name: "gcc build / ASan tests"
|
||||
runs-on: ubuntu-latest
|
||||
|
@ -17,11 +50,7 @@ jobs:
|
|||
run: |
|
||||
sed -i 's/SigLevel = Required DatabaseOptional/SigLevel = Optional TrustAll/' /etc/pacman.conf
|
||||
pacman --noconfirm --noprogressbar -Syyu
|
||||
pacman --noconfirm --noprogressbar -Sy gcc base-devel cmake clang git pixman
|
||||
|
||||
- name: Get hyprutils-git
|
||||
run: |
|
||||
git clone https://github.com/hyprwm/hyprutils && cd hyprutils && cmake -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -B build && cmake --build build --target hyprutils && cmake --install build
|
||||
pacman --noconfirm --noprogressbar -Sy gcc base-devel cmake clang
|
||||
|
||||
- name: Build with gcc
|
||||
run: |
|
||||
|
@ -48,11 +77,7 @@ jobs:
|
|||
run: |
|
||||
sed -i 's/SigLevel = Required DatabaseOptional/SigLevel = Optional TrustAll/' /etc/pacman.conf
|
||||
pacman --noconfirm --noprogressbar -Syyu
|
||||
pacman --noconfirm --noprogressbar -Sy gcc base-devel cmake clang git pixman
|
||||
|
||||
- name: Get hyprutils-git
|
||||
run: |
|
||||
git clone https://github.com/hyprwm/hyprutils && cd hyprutils && cmake -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -B build && cmake --build build --target hyprutils && cmake --install build
|
||||
pacman --noconfirm --noprogressbar -Sy gcc base-devel cmake clang
|
||||
|
||||
- name: Build with gcc
|
||||
run: |
|
||||
|
@ -79,11 +104,7 @@ jobs:
|
|||
run: |
|
||||
sed -i 's/SigLevel = Required DatabaseOptional/SigLevel = Optional TrustAll/' /etc/pacman.conf
|
||||
pacman --noconfirm --noprogressbar -Syyu
|
||||
pacman --noconfirm --noprogressbar -Sy gcc base-devel cmake clang git pixman
|
||||
|
||||
- name: Get hyprutils-git
|
||||
run: |
|
||||
git clone https://github.com/hyprwm/hyprutils && cd hyprutils && cmake -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -B build && cmake --build build --target hyprutils && cmake --install build
|
||||
pacman --noconfirm --noprogressbar -Sy gcc base-devel cmake clang
|
||||
|
||||
- name: Build with gcc
|
||||
run: |
|
||||
|
@ -110,15 +131,11 @@ jobs:
|
|||
run: |
|
||||
sed -i 's/SigLevel = Required DatabaseOptional/SigLevel = Optional TrustAll/' /etc/pacman.conf
|
||||
pacman --noconfirm --noprogressbar -Syyu
|
||||
pacman --noconfirm --noprogressbar -Sy gcc base-devel cmake clang libc++ git pixman
|
||||
|
||||
- name: Get hyprutils-git
|
||||
run: |
|
||||
git clone https://github.com/hyprwm/hyprutils && cd hyprutils && cmake -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -B build && cmake --build build --target hyprutils && cmake --install build
|
||||
pacman --noconfirm --noprogressbar -Sy gcc base-devel cmake clang
|
||||
|
||||
- name: Build hyprlang with clang
|
||||
run: |
|
||||
CC="/usr/bin/clang" CXX="/usr/bin/clang++" cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_CXX_FLAGS="-stdlib=libc++" -S . -B ./build
|
||||
CC="/usr/bin/clang" CXX="/usr/bin/clang++" cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -S . -B ./build
|
||||
CC="/usr/bin/clang" CXX="/usr/bin/clang++" cmake --build ./build --config Release --target hyprlang -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
|
||||
cmake --install ./build
|
||||
|
||||
|
|
1
.github/workflows/nix.yml
vendored
1
.github/workflows/nix.yml
vendored
|
@ -14,6 +14,7 @@ jobs:
|
|||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: DeterminateSystems/nix-installer-action@main
|
||||
- uses: DeterminateSystems/magic-nix-cache-action@main
|
||||
|
||||
# not needed (yet)
|
||||
# - uses: cachix/cachix-action@v12
|
||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -13,4 +13,3 @@ _deps
|
|||
build/
|
||||
doxygen/
|
||||
doxygen-awesome-css/
|
||||
.cache/
|
|
@ -1,12 +1,11 @@
|
|||
cmake_minimum_required(VERSION 3.19)
|
||||
|
||||
file(READ "${CMAKE_SOURCE_DIR}/VERSION" VER_RAW)
|
||||
string(STRIP ${VER_RAW} HYPRLANG_VERSION)
|
||||
set(HYPRLANG_VERSION "0.5.1")
|
||||
|
||||
project(
|
||||
hyprlang
|
||||
VERSION ${HYPRLANG_VERSION}
|
||||
DESCRIPTION "A library to parse hypr config files")
|
||||
project(hyprlang
|
||||
VERSION ${HYPRLANG_VERSION}
|
||||
DESCRIPTION "A library to parse hypr config files"
|
||||
)
|
||||
|
||||
include(CTest)
|
||||
include(GNUInstallDirs)
|
||||
|
@ -18,68 +17,52 @@ set(LIBDIR ${CMAKE_INSTALL_FULL_LIBDIR})
|
|||
configure_file(hyprlang.pc.in hyprlang.pc @ONLY)
|
||||
|
||||
if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG)
|
||||
message(STATUS "Configuring hyprlang in Debug")
|
||||
add_compile_definitions(HYPRLAND_DEBUG)
|
||||
message(STATUS "Configuring hyprlang in Debug")
|
||||
add_compile_definitions(HYPRLAND_DEBUG)
|
||||
else()
|
||||
add_compile_options(-O3)
|
||||
message(STATUS "Configuring hyprlang in Release")
|
||||
add_compile_options(-O3)
|
||||
message(STATUS "Configuring hyprlang in Release")
|
||||
endif()
|
||||
|
||||
add_compile_definitions(HYPRLANG_INTERNAL)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
add_compile_options(
|
||||
-Wall
|
||||
-Wextra
|
||||
-Wpedantic
|
||||
-Wno-unused-parameter
|
||||
-Wno-missing-field-initializers)
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE)
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(deps REQUIRED IMPORTED_TARGET hyprutils>=0.7.1)
|
||||
|
||||
file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp" "include/hyprlang.hpp")
|
||||
|
||||
add_library(hyprlang SHARED ${SRCFILES})
|
||||
target_include_directories(
|
||||
hyprlang
|
||||
PUBLIC "./include"
|
||||
PRIVATE "./src")
|
||||
set_target_properties(
|
||||
hyprlang
|
||||
PROPERTIES VERSION ${HYPRLANG_VERSION}
|
||||
SOVERSION 2
|
||||
PUBLIC_HEADER include/hyprlang.hpp)
|
||||
target_include_directories( hyprlang
|
||||
PUBLIC "./include"
|
||||
PRIVATE "./src"
|
||||
)
|
||||
set_target_properties(hyprlang PROPERTIES
|
||||
VERSION ${HYPRLANG_VERSION}
|
||||
SOVERSION 2
|
||||
PUBLIC_HEADER include/hyprlang.hpp)
|
||||
|
||||
target_link_libraries(hyprlang PkgConfig::deps)
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
# for std::expected.
|
||||
# probably evil. Arch's clang is very outdated tho...
|
||||
target_compile_options(hyprlang PUBLIC -std=gnu++2b -D__cpp_concepts=202002L -Wno-macro-redefined)
|
||||
endif()
|
||||
|
||||
add_library(hypr::hyprlang ALIAS hyprlang)
|
||||
install(TARGETS hyprlang)
|
||||
|
||||
# tests
|
||||
add_custom_target(tests)
|
||||
|
||||
add_executable(hyprlang_test "tests/parse/main.cpp")
|
||||
target_link_libraries(hyprlang_test PRIVATE hypr::hyprlang)
|
||||
add_test(
|
||||
NAME "Parsing"
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests
|
||||
COMMAND hyprlang_test "parse")
|
||||
target_link_libraries(hyprlang_test PRIVATE hyprlang)
|
||||
add_test(NAME "Parsing" WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests COMMAND hyprlang_test "parse")
|
||||
add_dependencies(tests hyprlang_test)
|
||||
|
||||
add_executable(hyprlang_fuzz "tests/fuzz/main.cpp")
|
||||
target_link_libraries(hyprlang_fuzz PRIVATE hypr::hyprlang)
|
||||
add_test(
|
||||
NAME "Fuzz"
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests
|
||||
COMMAND hyprlang_fuzz "fuzz")
|
||||
target_link_libraries(hyprlang_fuzz PRIVATE hyprlang)
|
||||
add_test(NAME "Fuzz" WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests COMMAND hyprlang_fuzz "fuzz")
|
||||
add_dependencies(tests hyprlang_fuzz)
|
||||
|
||||
# Installation
|
||||
install(
|
||||
TARGETS hyprlang
|
||||
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
install(FILES ${CMAKE_BINARY_DIR}/hyprlang.pc
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
|
||||
install(TARGETS hyprlang
|
||||
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
install(FILES ${CMAKE_BINARY_DIR}/hyprlang.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
|
||||
|
|
|
@ -10,11 +10,11 @@ It's user-friendly, easy to grasp, and easy to implement.
|
|||
Building is done via CMake:
|
||||
```sh
|
||||
cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -S . -B ./build
|
||||
cmake --build ./build --config Release --target hyprlang -j`nproc 2>/dev/null || getconf _NPROCESSORS_CONF`
|
||||
cmake --build ./build --config Release --target hyprlang -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
|
||||
```
|
||||
Install with:
|
||||
```sh
|
||||
sudo cmake --install ./build
|
||||
cmake --install ./build
|
||||
```
|
||||
|
||||
## Example config
|
||||
|
@ -54,4 +54,4 @@ Visit [hyprland.org/hyprlang](https://hyprland.org/hyprlang) to see the document
|
|||
|
||||
### Example implementation
|
||||
|
||||
For an example implementation, take a look at the `tests/` directory.
|
||||
For an example implmentation, take a look at the `tests/` directory.
|
||||
|
|
1
VERSION
1
VERSION
|
@ -1 +0,0 @@
|
|||
0.6.3
|
30
flake.lock
30
flake.lock
|
@ -1,35 +1,12 @@
|
|||
{
|
||||
"nodes": {
|
||||
"hyprutils": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"systems": [
|
||||
"systems"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1746635225,
|
||||
"narHash": "sha256-W9G9bb0zRYDBRseHbVez0J8qVpD5QbizX67H/vsudhM=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprutils",
|
||||
"rev": "674ea57373f08b7609ce93baff131117a0dfe70d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprutils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1746461020,
|
||||
"narHash": "sha256-7+pG1I9jvxNlmln4YgnlW4o+w0TZX24k688mibiFDUE=",
|
||||
"lastModified": 1708475490,
|
||||
"narHash": "sha256-g1v0TsWBQPX97ziznfJdWhgMyMGtoBFs102xSYO4syU=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "3730d8a308f94996a9ba7c7138ede69c1b9ac4ae",
|
||||
"rev": "0e74ca98a74bc7270d28838369593635a5db3260",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -41,7 +18,6 @@
|
|||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"hyprutils": "hyprutils",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"systems": "systems"
|
||||
}
|
||||
|
|
28
flake.nix
28
flake.nix
|
@ -4,20 +4,13 @@
|
|||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
systems.url = "github:nix-systems/default-linux";
|
||||
|
||||
hyprutils = {
|
||||
url = "github:hyprwm/hyprutils";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
inputs.systems.follows = "systems";
|
||||
};
|
||||
};
|
||||
|
||||
outputs = {
|
||||
self,
|
||||
nixpkgs,
|
||||
systems,
|
||||
...
|
||||
} @ inputs: let
|
||||
}: let
|
||||
inherit (nixpkgs) lib;
|
||||
eachSystem = lib.genAttrs (import systems);
|
||||
pkgsFor = eachSystem (system:
|
||||
|
@ -30,21 +23,16 @@
|
|||
(builtins.substring 4 2 longDate)
|
||||
(builtins.substring 6 2 longDate)
|
||||
]);
|
||||
|
||||
version = lib.removeSuffix "\n" (builtins.readFile ./VERSION);
|
||||
in {
|
||||
overlays = {
|
||||
default = self.overlays.hyprlang;
|
||||
hyprlang = lib.composeManyExtensions [
|
||||
inputs.hyprutils.overlays.default
|
||||
(final: prev: {
|
||||
hyprlang = final.callPackage ./nix/default.nix {
|
||||
stdenv = final.gcc14Stdenv;
|
||||
version = version + "+date=" + (mkDate (self.lastModifiedDate or "19700101")) + "_" + (self.shortRev or "dirty");
|
||||
};
|
||||
hyprlang-with-tests = final.hyprlang.override {doCheck = true;};
|
||||
})
|
||||
];
|
||||
hyprlang = final: prev: {
|
||||
hyprlang = final.callPackage ./nix/default.nix {
|
||||
stdenv = final.gcc13Stdenv;
|
||||
version = "0.pre" + "+date=" + (mkDate (self.lastModifiedDate or "19700101")) + "_" + (self.shortRev or "dirty");
|
||||
};
|
||||
hyprlang-with-tests = final.hyprlang.override {doCheck = true;};
|
||||
};
|
||||
};
|
||||
|
||||
packages = eachSystem (system: {
|
||||
|
|
|
@ -3,13 +3,11 @@
|
|||
#ifndef HYPRLANG_HPP
|
||||
#define HYPRLANG_HPP
|
||||
|
||||
#include <typeindex>
|
||||
#include <any>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <ostream>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
#include <print>
|
||||
#include <cstdlib>
|
||||
|
||||
class CConfigImpl;
|
||||
struct SConfigDefaultValue;
|
||||
|
@ -254,7 +252,7 @@ namespace Hyprlang {
|
|||
/*!
|
||||
Get the contained value as an std::any.
|
||||
For strings, this is a const char*.
|
||||
For custom data types, this is a void* representing the data ptr stored by it.
|
||||
For custom data types, this is a CConfigCustomValueType*.
|
||||
*/
|
||||
std::any getValue() const {
|
||||
switch (m_eType) {
|
||||
|
@ -454,87 +452,6 @@ namespace Hyprlang {
|
|||
void retrieveKeysForCat(const char* category, const char*** out, size_t* len);
|
||||
CParseResult parseRawStream(const std::string& stream);
|
||||
};
|
||||
|
||||
/*!
|
||||
Templated wrapper for Hyprlang values. Much more straightforward to use.
|
||||
|
||||
\since 0.6.0
|
||||
*/
|
||||
template <typename T>
|
||||
class CSimpleConfigValue {
|
||||
public:
|
||||
CSimpleConfigValue(CConfig* const pConfig, const char* val) {
|
||||
const auto VAL = pConfig->getConfigValuePtr(val);
|
||||
|
||||
if (!VAL) {
|
||||
std::println("CSimpleConfigValue: value not found");
|
||||
abort();
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
p_ = VAL->getDataStaticPtr();
|
||||
|
||||
#ifdef HYPRLAND_DEBUG
|
||||
// verify type
|
||||
const auto ANY = VAL->getValue();
|
||||
const auto TYPE = std::type_index(ANY.type());
|
||||
|
||||
// exceptions
|
||||
const bool STRINGEX = (typeid(T) == typeid(std::string) && TYPE == typeid(Hyprlang::STRING));
|
||||
const bool CUSTOMEX = (typeid(T) == typeid(Hyprlang::CUSTOMTYPE) && (TYPE == typeid(Hyprlang::CUSTOMTYPE*) || TYPE == typeid(void*) /* dunno why it does this? */));
|
||||
|
||||
if (typeid(T) != TYPE && !STRINGEX && !CUSTOMEX) {
|
||||
std::println("CSimpleConfigValue: Mismatched type in CConfigValue<T>, got {} but has {}", typeid(T).name(), TYPE.name());
|
||||
abort();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
T* ptr() const {
|
||||
return *(T* const*)p_;
|
||||
}
|
||||
|
||||
T operator*() const {
|
||||
return *ptr();
|
||||
}
|
||||
|
||||
private:
|
||||
void* const* p_ = nullptr;
|
||||
};
|
||||
|
||||
template <>
|
||||
inline std::string* CSimpleConfigValue<std::string>::ptr() const {
|
||||
std::print("Impossible to implement ptr() of CConfigValue<std::string>");
|
||||
abort();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline std::string CSimpleConfigValue<std::string>::operator*() const {
|
||||
return std::string{*(Hyprlang::STRING*)p_};
|
||||
}
|
||||
|
||||
template <>
|
||||
inline Hyprlang::STRING* CSimpleConfigValue<Hyprlang::STRING>::ptr() const {
|
||||
return (Hyprlang::STRING*)p_;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline Hyprlang::STRING CSimpleConfigValue<Hyprlang::STRING>::operator*() const {
|
||||
return *(Hyprlang::STRING*)p_;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline Hyprlang::CUSTOMTYPE* CSimpleConfigValue<Hyprlang::CUSTOMTYPE>::ptr() const {
|
||||
return *(Hyprlang::CUSTOMTYPE* const*)p_;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline Hyprlang::CUSTOMTYPE CSimpleConfigValue<Hyprlang::CUSTOMTYPE>::operator*() const {
|
||||
std::print("Impossible to implement operator* of CConfigValue<Hyprlang::CUSTOMTYPE>, use ptr()");
|
||||
abort();
|
||||
return *ptr();
|
||||
}
|
||||
};
|
||||
|
||||
#ifndef HYPRLANG_INTERNAL
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
lib,
|
||||
stdenv,
|
||||
cmake,
|
||||
hyprutils,
|
||||
pkg-config,
|
||||
version ? "git",
|
||||
doCheck ? false,
|
||||
}:
|
||||
|
@ -12,12 +10,7 @@ stdenv.mkDerivation {
|
|||
inherit version doCheck;
|
||||
src = ../.;
|
||||
|
||||
nativeBuildInputs = [
|
||||
cmake
|
||||
pkg-config
|
||||
];
|
||||
|
||||
buildInputs = [hyprutils];
|
||||
nativeBuildInputs = [cmake];
|
||||
|
||||
outputs = ["out" "dev"];
|
||||
|
||||
|
|
55
src/VarList.cpp
Normal file
55
src/VarList.cpp
Normal 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
63
src/VarList.hpp
Normal 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;
|
||||
};
|
|
@ -1,6 +1,6 @@
|
|||
#include "public.hpp"
|
||||
#include "config.hpp"
|
||||
#include <cstring>
|
||||
#include <string.h>
|
||||
|
||||
using namespace Hyprlang;
|
||||
|
||||
|
@ -30,28 +30,38 @@ CConfigValue::~CConfigValue() {
|
|||
}
|
||||
}
|
||||
|
||||
CConfigValue::CConfigValue(const int64_t value) : m_eType(CONFIGDATATYPE_INT), m_pData(new int64_t) {
|
||||
CConfigValue::CConfigValue(const int64_t value) {
|
||||
m_pData = new int64_t;
|
||||
*reinterpret_cast<int64_t*>(m_pData) = value;
|
||||
m_eType = CONFIGDATATYPE_INT;
|
||||
}
|
||||
|
||||
CConfigValue::CConfigValue(const float value) : m_eType(CONFIGDATATYPE_FLOAT), m_pData(new float) {
|
||||
CConfigValue::CConfigValue(const float value) {
|
||||
m_pData = new float;
|
||||
*reinterpret_cast<float*>(m_pData) = value;
|
||||
m_eType = CONFIGDATATYPE_FLOAT;
|
||||
}
|
||||
|
||||
CConfigValue::CConfigValue(const SVector2D value) : m_eType(CONFIGDATATYPE_VEC2), m_pData(new SVector2D) {
|
||||
CConfigValue::CConfigValue(const SVector2D value) {
|
||||
m_pData = new SVector2D;
|
||||
*reinterpret_cast<SVector2D*>(m_pData) = value;
|
||||
m_eType = CONFIGDATATYPE_VEC2;
|
||||
}
|
||||
|
||||
CConfigValue::CConfigValue(const char* value) : m_eType(CONFIGDATATYPE_STR), m_pData(new char[strlen(value) + 1]) {
|
||||
CConfigValue::CConfigValue(const char* value) {
|
||||
m_pData = new char[strlen(value) + 1];
|
||||
strncpy((char*)m_pData, value, strlen(value));
|
||||
((char*)m_pData)[strlen(value)] = '\0';
|
||||
m_eType = CONFIGDATATYPE_STR;
|
||||
}
|
||||
|
||||
CConfigValue::CConfigValue(CConfigCustomValueType&& value) : m_eType(CONFIGDATATYPE_CUSTOM), m_pData(new CConfigCustomValueType(value)) {
|
||||
;
|
||||
CConfigValue::CConfigValue(CConfigCustomValueType&& value) {
|
||||
m_pData = new CConfigCustomValueType(value);
|
||||
m_eType = CONFIGDATATYPE_CUSTOM;
|
||||
}
|
||||
|
||||
CConfigValue::CConfigValue(const CConfigValue& other) : m_eType(other.m_eType) {
|
||||
CConfigValue::CConfigValue(const CConfigValue& other) {
|
||||
m_eType = other.m_eType;
|
||||
setFrom(&other);
|
||||
}
|
||||
|
||||
|
@ -67,9 +77,11 @@ void* const* CConfigValue::getDataStaticPtr() const {
|
|||
return &m_pData;
|
||||
}
|
||||
|
||||
CConfigCustomValueType::CConfigCustomValueType(PCONFIGCUSTOMVALUEHANDLERFUNC handler_, PCONFIGCUSTOMVALUEDESTRUCTOR dtor_, const char* def) :
|
||||
handler(handler_), dtor(dtor_), defaultVal(def), lastVal(def) {
|
||||
;
|
||||
CConfigCustomValueType::CConfigCustomValueType(PCONFIGCUSTOMVALUEHANDLERFUNC handler_, PCONFIGCUSTOMVALUEDESTRUCTOR dtor_, const char* def) {
|
||||
handler = handler_;
|
||||
dtor = dtor_;
|
||||
defaultVal = def;
|
||||
lastVal = def;
|
||||
}
|
||||
|
||||
CConfigCustomValueType::~CConfigCustomValueType() {
|
||||
|
|
353
src/config.cpp
353
src/config.cpp
|
@ -1,8 +1,6 @@
|
|||
#include "config.hpp"
|
||||
#include <exception>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <format>
|
||||
#include <algorithm>
|
||||
|
@ -10,62 +8,56 @@
|
|||
#include <expected>
|
||||
#include <sstream>
|
||||
#include <cstring>
|
||||
#include <hyprutils/string/VarList.hpp>
|
||||
#include <hyprutils/string/String.hpp>
|
||||
#include <hyprutils/string/ConstVarList.hpp>
|
||||
|
||||
#include "VarList.hpp"
|
||||
|
||||
using namespace Hyprlang;
|
||||
using namespace Hyprutils::String;
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <crt_externs.h>
|
||||
#define environ (*_NSGetEnviron())
|
||||
#else
|
||||
// NOLINTNEXTLINE
|
||||
extern "C" char** environ;
|
||||
#endif
|
||||
|
||||
// defines
|
||||
inline constexpr const char* ANONYMOUS_KEY = "__hyprlang_internal_anonymous_key";
|
||||
inline constexpr const char* MULTILINE_SPACE_CHARSET = " \t";
|
||||
inline constexpr const char* ANONYMOUS_KEY = "__hyprlang_internal_anonymous_key";
|
||||
//
|
||||
|
||||
static size_t seekABIStructSize(const void* begin, size_t startOffset, size_t maxSize) {
|
||||
for (size_t off = startOffset; off < maxSize; off += 4) {
|
||||
if (*(int*)((unsigned char*)begin + off) == HYPRLANG_END_MAGIC)
|
||||
if (*(int*)((unsigned char*)begin + off) == int{HYPRLANG_END_MAGIC})
|
||||
return off;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static std::expected<std::string, eGetNextLineFailure> getNextLine(std::istream& str, int& rawLineNum, int& lineNum) {
|
||||
std::string line = "";
|
||||
std::string nextLine = "";
|
||||
static std::string removeBeginEndSpacesTabs(std::string str) {
|
||||
if (str.empty())
|
||||
return str;
|
||||
|
||||
if (!std::getline(str, line))
|
||||
return std::unexpected(GETNEXTLINEFAILURE_EOF);
|
||||
|
||||
lineNum = ++rawLineNum;
|
||||
|
||||
while (line.length() > 0 && line.at(line.length() - 1) == '\\') {
|
||||
const auto lastNonSpace = line.length() < 2 ? -1 : line.find_last_not_of(MULTILINE_SPACE_CHARSET, line.length() - 2);
|
||||
line = line.substr(0, lastNonSpace + 1);
|
||||
|
||||
if (!std::getline(str, nextLine))
|
||||
return std::unexpected(GETNEXTLINEFAILURE_BACKSLASH);
|
||||
|
||||
++rawLineNum;
|
||||
line += nextLine;
|
||||
int countBefore = 0;
|
||||
while (str[countBefore] == ' ' || str[countBefore] == '\t') {
|
||||
countBefore++;
|
||||
}
|
||||
|
||||
return line;
|
||||
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;
|
||||
}
|
||||
|
||||
CConfig::CConfig(const char* path, const Hyprlang::SConfigOptions& options_) : impl(new CConfigImpl) {
|
||||
CConfig::CConfig(const char* path, const Hyprlang::SConfigOptions& options_) {
|
||||
SConfigOptions options;
|
||||
std::memcpy(&options, &options_, seekABIStructSize(&options_, 16, sizeof(SConfigOptions)));
|
||||
|
||||
impl = new CConfigImpl;
|
||||
|
||||
if (options.pathIsStream)
|
||||
impl->rawConfigString = path;
|
||||
else
|
||||
|
@ -84,7 +76,7 @@ CConfig::CConfig(const char* path, const Hyprlang::SConfigOptions& options_) : i
|
|||
impl->envVariables.push_back({VARIABLE, VALUE});
|
||||
}
|
||||
|
||||
std::ranges::sort(impl->envVariables, [&](const auto& a, const auto& b) { return a.name.length() > b.name.length(); });
|
||||
std::sort(impl->envVariables.begin(), impl->envVariables.end(), [&](const auto& a, const auto& b) { return a.name.length() > b.name.length(); });
|
||||
|
||||
impl->configOptions = options;
|
||||
}
|
||||
|
@ -98,42 +90,40 @@ void CConfig::addConfigValue(const char* name, const CConfigValue& value) {
|
|||
throw "Cannot addConfigValue after commence()";
|
||||
|
||||
if ((eDataType)value.m_eType != CONFIGDATATYPE_CUSTOM && (eDataType)value.m_eType != CONFIGDATATYPE_STR)
|
||||
impl->defaultValues.emplace(name, SConfigDefaultValue{.data = value.getValue(), .type = (eDataType)value.m_eType});
|
||||
impl->defaultValues.emplace(name, SConfigDefaultValue{value.getValue(), (eDataType)value.m_eType});
|
||||
else if ((eDataType)value.m_eType == CONFIGDATATYPE_STR)
|
||||
impl->defaultValues.emplace(name, SConfigDefaultValue{.data = std::string{std::any_cast<const char*>(value.getValue())}, .type = (eDataType)value.m_eType});
|
||||
impl->defaultValues.emplace(name, SConfigDefaultValue{std::string{std::any_cast<const char*>(value.getValue())}, (eDataType)value.m_eType});
|
||||
else
|
||||
impl->defaultValues.emplace(name,
|
||||
SConfigDefaultValue{.data = reinterpret_cast<CConfigCustomValueType*>(value.m_pData)->defaultVal,
|
||||
.type = (eDataType)value.m_eType,
|
||||
.handler = reinterpret_cast<CConfigCustomValueType*>(value.m_pData)->handler,
|
||||
.dtor = reinterpret_cast<CConfigCustomValueType*>(value.m_pData)->dtor});
|
||||
SConfigDefaultValue{reinterpret_cast<CConfigCustomValueType*>(value.m_pData)->defaultVal, (eDataType)value.m_eType,
|
||||
reinterpret_cast<CConfigCustomValueType*>(value.m_pData)->handler,
|
||||
reinterpret_cast<CConfigCustomValueType*>(value.m_pData)->dtor});
|
||||
}
|
||||
|
||||
void CConfig::addSpecialConfigValue(const char* cat, const char* name, const CConfigValue& value) {
|
||||
const auto IT = std::ranges::find_if(impl->specialCategoryDescriptors, [&](const auto& other) { return other->name == cat; });
|
||||
const auto IT = std::find_if(impl->specialCategoryDescriptors.begin(), impl->specialCategoryDescriptors.end(), [&](const auto& other) { return other->name == cat; });
|
||||
|
||||
if (IT == impl->specialCategoryDescriptors.end())
|
||||
throw "No such category";
|
||||
|
||||
if ((eDataType)value.m_eType != CONFIGDATATYPE_CUSTOM && (eDataType)value.m_eType != CONFIGDATATYPE_STR)
|
||||
IT->get()->defaultValues.emplace(name, SConfigDefaultValue{.data = value.getValue(), .type = (eDataType)value.m_eType});
|
||||
IT->get()->defaultValues.emplace(name, SConfigDefaultValue{value.getValue(), (eDataType)value.m_eType});
|
||||
else if ((eDataType)value.m_eType == CONFIGDATATYPE_STR)
|
||||
IT->get()->defaultValues.emplace(name, SConfigDefaultValue{.data = std::string{std::any_cast<const char*>(value.getValue())}, .type = (eDataType)value.m_eType});
|
||||
IT->get()->defaultValues.emplace(name, SConfigDefaultValue{std::string{std::any_cast<const char*>(value.getValue())}, (eDataType)value.m_eType});
|
||||
else
|
||||
IT->get()->defaultValues.emplace(name,
|
||||
SConfigDefaultValue{.data = reinterpret_cast<CConfigCustomValueType*>(value.m_pData)->defaultVal,
|
||||
.type = (eDataType)value.m_eType,
|
||||
.handler = reinterpret_cast<CConfigCustomValueType*>(value.m_pData)->handler,
|
||||
.dtor = reinterpret_cast<CConfigCustomValueType*>(value.m_pData)->dtor});
|
||||
SConfigDefaultValue{reinterpret_cast<CConfigCustomValueType*>(value.m_pData)->defaultVal, (eDataType)value.m_eType,
|
||||
reinterpret_cast<CConfigCustomValueType*>(value.m_pData)->handler,
|
||||
reinterpret_cast<CConfigCustomValueType*>(value.m_pData)->dtor});
|
||||
|
||||
const auto CAT = std::ranges::find_if(impl->specialCategories, [cat](const auto& other) { return other->name == cat && other->isStatic; });
|
||||
const auto CAT = std::find_if(impl->specialCategories.begin(), impl->specialCategories.end(), [cat, name](const auto& other) { return other->name == cat && other->isStatic; });
|
||||
|
||||
if (CAT != impl->specialCategories.end())
|
||||
CAT->get()->values[name].defaultFrom(IT->get()->defaultValues[name]);
|
||||
}
|
||||
|
||||
void CConfig::removeSpecialConfigValue(const char* cat, const char* name) {
|
||||
const auto IT = std::ranges::find_if(impl->specialCategoryDescriptors, [&](const auto& other) { return other->name == cat; });
|
||||
const auto IT = std::find_if(impl->specialCategoryDescriptors.begin(), impl->specialCategoryDescriptors.end(), [&](const auto& other) { return other->name == cat; });
|
||||
|
||||
if (IT == impl->specialCategoryDescriptors.end())
|
||||
throw "No such category";
|
||||
|
@ -164,8 +154,9 @@ void CConfig::addSpecialCategory(const char* name, SSpecialCategoryOptions optio
|
|||
}
|
||||
|
||||
// sort longest to shortest
|
||||
std::ranges::sort(impl->specialCategories, [](const auto& a, const auto& b) -> int { return a->name.length() > b->name.length(); });
|
||||
std::ranges::sort(impl->specialCategoryDescriptors, [](const auto& a, const auto& b) -> int { return a->name.length() > b->name.length(); });
|
||||
std::sort(impl->specialCategories.begin(), impl->specialCategories.end(), [](const auto& a, const auto& b) -> int { return a->name.length() > b->name.length(); });
|
||||
std::sort(impl->specialCategoryDescriptors.begin(), impl->specialCategoryDescriptors.end(),
|
||||
[](const auto& a, const auto& b) -> int { return a->name.length() > b->name.length(); });
|
||||
}
|
||||
|
||||
void CConfig::removeSpecialCategory(const char* name) {
|
||||
|
@ -186,77 +177,98 @@ void CConfig::commence() {
|
|||
}
|
||||
}
|
||||
|
||||
static bool isNumber(const std::string& str, bool allowfloat) {
|
||||
|
||||
std::string copy = str;
|
||||
if (*copy.begin() == '-')
|
||||
copy = copy.substr(1);
|
||||
|
||||
if (copy.empty())
|
||||
return false;
|
||||
|
||||
bool point = !allowfloat;
|
||||
for (auto& c : copy) {
|
||||
if (c == '.') {
|
||||
if (point)
|
||||
return false;
|
||||
point = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!std::isdigit(c))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void replaceAll(std::string& str, const std::string& from, const std::string& to) {
|
||||
if (from.empty())
|
||||
return;
|
||||
size_t pos = 0;
|
||||
while ((pos = str.find(from, pos)) != std::string::npos) {
|
||||
str.replace(pos, from.length(), to);
|
||||
pos += to.length();
|
||||
}
|
||||
}
|
||||
|
||||
static std::expected<int64_t, std::string> configStringToInt(const std::string& VALUE) {
|
||||
auto parseHex = [](const std::string& value) -> std::expected<int64_t, std::string> {
|
||||
try {
|
||||
size_t position;
|
||||
auto result = stoll(value, &position, 16);
|
||||
if (position == value.size())
|
||||
return result;
|
||||
} catch (const std::exception&) {}
|
||||
return std::unexpected("invalid hex " + value);
|
||||
};
|
||||
if (VALUE.starts_with("0x")) {
|
||||
// Values with 0x are hex
|
||||
return parseHex(VALUE);
|
||||
const auto VALUEWITHOUTHEX = VALUE.substr(2);
|
||||
return stoll(VALUEWITHOUTHEX, nullptr, 16);
|
||||
} else if (VALUE.starts_with("rgba(") && VALUE.ends_with(')')) {
|
||||
const auto VALUEWITHOUTFUNC = trim(VALUE.substr(5, VALUE.length() - 6));
|
||||
const auto VALUEWITHOUTFUNC = removeBeginEndSpacesTabs(VALUE.substr(5, VALUE.length() - 6));
|
||||
|
||||
// try doing it the comma way first
|
||||
if (std::count(VALUEWITHOUTFUNC.begin(), VALUEWITHOUTFUNC.end(), ',') == 3) {
|
||||
// cool
|
||||
std::string rolling = VALUEWITHOUTFUNC;
|
||||
auto r = configStringToInt(trim(rolling.substr(0, rolling.find(','))));
|
||||
auto r = configStringToInt(removeBeginEndSpacesTabs(rolling.substr(0, rolling.find(','))));
|
||||
rolling = rolling.substr(rolling.find(',') + 1);
|
||||
auto g = configStringToInt(trim(rolling.substr(0, rolling.find(','))));
|
||||
auto g = configStringToInt(removeBeginEndSpacesTabs(rolling.substr(0, rolling.find(','))));
|
||||
rolling = rolling.substr(rolling.find(',') + 1);
|
||||
auto b = configStringToInt(trim(rolling.substr(0, rolling.find(','))));
|
||||
auto b = configStringToInt(removeBeginEndSpacesTabs(rolling.substr(0, rolling.find(','))));
|
||||
rolling = rolling.substr(rolling.find(',') + 1);
|
||||
uint8_t a = 0;
|
||||
try {
|
||||
a = std::round(std::stof(trim(rolling.substr(0, rolling.find(',')))) * 255.f);
|
||||
a = std::round(std::stof(removeBeginEndSpacesTabs(rolling.substr(0, rolling.find(',')))) * 255.f);
|
||||
} catch (std::exception& e) { return std::unexpected("failed parsing " + VALUEWITHOUTFUNC); }
|
||||
|
||||
if (!r.has_value() || !g.has_value() || !b.has_value())
|
||||
return std::unexpected("failed parsing " + VALUEWITHOUTFUNC);
|
||||
|
||||
return (a * (Hyprlang::INT)0x1000000) + (r.value() * (Hyprlang::INT)0x10000) + (g.value() * (Hyprlang::INT)0x100) + b.value();
|
||||
return a * (Hyprlang::INT)0x1000000 + r.value() * (Hyprlang::INT)0x10000 + g.value() * (Hyprlang::INT)0x100 + b.value();
|
||||
} else if (VALUEWITHOUTFUNC.length() == 8) {
|
||||
const auto RGBA = parseHex(VALUEWITHOUTFUNC);
|
||||
|
||||
if (!RGBA.has_value())
|
||||
return RGBA;
|
||||
const auto RGBA = std::stoll(VALUEWITHOUTFUNC, nullptr, 16);
|
||||
|
||||
// now we need to RGBA -> ARGB. The config holds ARGB only.
|
||||
return (RGBA.value() >> 8) + (0x1000000 * (RGBA.value() & 0xFF));
|
||||
return (RGBA >> 8) + 0x1000000 * (RGBA & 0xFF);
|
||||
}
|
||||
|
||||
return std::unexpected("rgba() expects length of 8 characters (4 bytes) or 4 comma separated values");
|
||||
|
||||
} else if (VALUE.starts_with("rgb(") && VALUE.ends_with(')')) {
|
||||
const auto VALUEWITHOUTFUNC = trim(VALUE.substr(4, VALUE.length() - 5));
|
||||
const auto VALUEWITHOUTFUNC = removeBeginEndSpacesTabs(VALUE.substr(4, VALUE.length() - 5));
|
||||
|
||||
// try doing it the comma way first
|
||||
if (std::count(VALUEWITHOUTFUNC.begin(), VALUEWITHOUTFUNC.end(), ',') == 2) {
|
||||
// cool
|
||||
std::string rolling = VALUEWITHOUTFUNC;
|
||||
auto r = configStringToInt(trim(rolling.substr(0, rolling.find(','))));
|
||||
auto r = configStringToInt(removeBeginEndSpacesTabs(rolling.substr(0, rolling.find(','))));
|
||||
rolling = rolling.substr(rolling.find(',') + 1);
|
||||
auto g = configStringToInt(trim(rolling.substr(0, rolling.find(','))));
|
||||
auto g = configStringToInt(removeBeginEndSpacesTabs(rolling.substr(0, rolling.find(','))));
|
||||
rolling = rolling.substr(rolling.find(',') + 1);
|
||||
auto b = configStringToInt(trim(rolling.substr(0, rolling.find(','))));
|
||||
auto b = configStringToInt(removeBeginEndSpacesTabs(rolling.substr(0, rolling.find(','))));
|
||||
|
||||
if (!r.has_value() || !g.has_value() || !b.has_value())
|
||||
return std::unexpected("failed parsing " + VALUEWITHOUTFUNC);
|
||||
|
||||
return (Hyprlang::INT)0xFF000000 + (r.value() * (Hyprlang::INT)0x10000) + (g.value() * (Hyprlang::INT)0x100) + b.value();
|
||||
return (Hyprlang::INT)0xFF000000 + r.value() * (Hyprlang::INT)0x10000 + g.value() * (Hyprlang::INT)0x100 + b.value();
|
||||
} else if (VALUEWITHOUTFUNC.length() == 6) {
|
||||
const auto RGB = parseHex(VALUEWITHOUTFUNC);
|
||||
const auto RGB = std::stoll(VALUEWITHOUTFUNC, nullptr, 16);
|
||||
|
||||
if (!RGB.has_value())
|
||||
return RGB;
|
||||
|
||||
return RGB.value() + 0xFF000000;
|
||||
return RGB + 0xFF000000;
|
||||
}
|
||||
|
||||
return std::unexpected("rgb() expects length of 6 characters (3 bytes) or 3 comma separated values");
|
||||
|
@ -382,7 +394,8 @@ CParseResult CConfig::configSetValueSafe(const std::string& command, const std::
|
|||
// find suitable key
|
||||
size_t biggest = 0;
|
||||
for (auto& catt : impl->specialCategories) {
|
||||
biggest = std::max(catt->anonymousID, biggest);
|
||||
if (catt->anonymousID > biggest)
|
||||
biggest = catt->anonymousID;
|
||||
}
|
||||
|
||||
biggest++;
|
||||
|
@ -441,7 +454,7 @@ CParseResult CConfig::configSetValueSafe(const std::string& command, const std::
|
|||
if (LHS.contains(" ") || RHS.contains(" "))
|
||||
throw std::runtime_error("too many args");
|
||||
|
||||
VALUEIT->second.setFrom(SVector2D{.x = std::stof(LHS), .y = std::stof(RHS)});
|
||||
VALUEIT->second.setFrom(SVector2D{std::stof(LHS), std::stof(RHS)});
|
||||
} catch (std::exception& e) {
|
||||
result.setError(std::format("failed parsing a vec2: {}", e.what()));
|
||||
return result;
|
||||
|
@ -475,14 +488,14 @@ CParseResult CConfig::configSetValueSafe(const std::string& command, const std::
|
|||
}
|
||||
|
||||
CParseResult CConfig::parseVariable(const std::string& lhs, const std::string& rhs, bool dynamic) {
|
||||
auto IT = std::ranges::find_if(impl->variables, [&](const auto& v) { return v.name == lhs.substr(1); });
|
||||
auto IT = std::find_if(impl->variables.begin(), impl->variables.end(), [&](const auto& v) { return v.name == lhs.substr(1); });
|
||||
|
||||
if (IT != impl->variables.end())
|
||||
IT->value = rhs;
|
||||
else {
|
||||
impl->variables.push_back({lhs.substr(1), rhs});
|
||||
std::ranges::sort(impl->variables, [](const auto& lhs, const auto& rhs) { return lhs.name.length() > rhs.name.length(); });
|
||||
IT = std::ranges::find_if(impl->variables, [&](const auto& v) { return v.name == lhs.substr(1); });
|
||||
std::sort(impl->variables.begin(), impl->variables.end(), [](const auto& lhs, const auto& rhs) { return lhs.name.length() > rhs.name.length(); });
|
||||
IT = std::find_if(impl->variables.begin(), impl->variables.end(), [&](const auto& v) { return v.name == lhs.substr(1); });
|
||||
}
|
||||
|
||||
if (dynamic) {
|
||||
|
@ -500,7 +513,7 @@ CParseResult CConfig::parseVariable(const std::string& lhs, const std::string& r
|
|||
}
|
||||
|
||||
void CConfigImpl::parseComment(const std::string& comment) {
|
||||
const auto COMMENT = trim(comment);
|
||||
const auto COMMENT = removeBeginEndSpacesTabs(comment);
|
||||
|
||||
if (!COMMENT.starts_with("hyprlang"))
|
||||
return;
|
||||
|
@ -511,60 +524,10 @@ void CConfigImpl::parseComment(const std::string& comment) {
|
|||
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 result;
|
||||
|
||||
line = trim(line);
|
||||
line = removeBeginEndSpacesTabs(line);
|
||||
|
||||
auto commentPos = line.find('#');
|
||||
|
||||
|
@ -593,7 +556,7 @@ CParseResult CConfig::parseLine(std::string line, bool dynamic) {
|
|||
}
|
||||
}
|
||||
|
||||
line = trim(line);
|
||||
line = removeBeginEndSpacesTabs(line);
|
||||
|
||||
if (line.empty())
|
||||
return result;
|
||||
|
@ -609,8 +572,8 @@ CParseResult CConfig::parseLine(std::string line, bool dynamic) {
|
|||
if (equalsPos != std::string::npos) {
|
||||
// set value or call handler
|
||||
CParseResult ret;
|
||||
auto LHS = trim(line.substr(0, equalsPos));
|
||||
auto RHS = trim(line.substr(equalsPos + 1));
|
||||
auto LHS = removeBeginEndSpacesTabs(line.substr(0, equalsPos));
|
||||
auto RHS = removeBeginEndSpacesTabs(line.substr(equalsPos + 1));
|
||||
|
||||
if (LHS.empty()) {
|
||||
result.setError("Empty lhs.");
|
||||
|
@ -622,44 +585,24 @@ CParseResult CConfig::parseLine(std::string line, bool dynamic) {
|
|||
// limit unwrapping iterations to 100. if exceeds, raise error
|
||||
for (size_t i = 0; i < 100; ++i) {
|
||||
bool anyMatch = false;
|
||||
|
||||
// parse variables
|
||||
for (auto& var : impl->variables) {
|
||||
// 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)
|
||||
replaceInString(LHS, "$" + var.name, var.value);
|
||||
replaceAll(LHS, "$" + var.name, var.value);
|
||||
if (RHSIT != std::string::npos)
|
||||
replaceInString(RHS, "$" + var.name, var.value);
|
||||
replaceAll(RHS, "$" + var.name, var.value);
|
||||
|
||||
if (RHSIT == std::string::npos && LHSIT == std::string::npos)
|
||||
continue;
|
||||
else if (!dynamic)
|
||||
else
|
||||
var.linesContainingVar.push_back({line, impl->categories, impl->currentSpecialCategory});
|
||||
|
||||
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 + 2);
|
||||
} else
|
||||
break;
|
||||
}
|
||||
|
||||
if (!anyMatch)
|
||||
break;
|
||||
|
||||
|
@ -673,38 +616,11 @@ CParseResult CConfig::parseLine(std::string line, bool dynamic) {
|
|||
return parseVariable(LHS, RHS, dynamic);
|
||||
|
||||
bool found = false;
|
||||
|
||||
for (auto& h : impl->handlers) {
|
||||
// we want to handle potentially nested keywords and ensure
|
||||
// we only call the handler if they are scoped correctly,
|
||||
// unless the keyword is not scoped itself
|
||||
|
||||
const bool UNSCOPED = !h.name.contains(":");
|
||||
const auto HANDLERNAME = !h.name.empty() && h.name.at(0) == ':' ? h.name.substr(1) : h.name;
|
||||
|
||||
if (!h.options.allowFlags && !UNSCOPED) {
|
||||
size_t colon = 0;
|
||||
size_t idx = 0;
|
||||
size_t depth = 0;
|
||||
|
||||
while ((colon = HANDLERNAME.find(':', idx)) != std::string::npos && impl->categories.size() > depth) {
|
||||
auto actual = HANDLERNAME.substr(idx, colon - idx);
|
||||
|
||||
if (actual != impl->categories[depth])
|
||||
break;
|
||||
|
||||
idx = colon + 1;
|
||||
++depth;
|
||||
}
|
||||
|
||||
if (depth != impl->categories.size() || HANDLERNAME.substr(idx) != LHS)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (UNSCOPED && HANDLERNAME != LHS && !h.options.allowFlags)
|
||||
if (!h.options.allowFlags && h.name != LHS)
|
||||
continue;
|
||||
|
||||
if (h.options.allowFlags && (!LHS.starts_with(HANDLERNAME) || LHS.contains(':') /* avoid cases where a category is called the same as a handler */))
|
||||
if (h.options.allowFlags && !LHS.starts_with(h.name))
|
||||
continue;
|
||||
|
||||
ret = h.func(LHS.c_str(), RHS.c_str());
|
||||
|
@ -741,7 +657,7 @@ CParseResult CConfig::parseLine(std::string line, bool dynamic) {
|
|||
}
|
||||
|
||||
line.pop_back();
|
||||
line = trim(line);
|
||||
line = removeBeginEndSpacesTabs(line);
|
||||
impl->categories.push_back(line);
|
||||
}
|
||||
}
|
||||
|
@ -789,35 +705,22 @@ CParseResult CConfig::parse() {
|
|||
CParseResult CConfig::parseRawStream(const std::string& stream) {
|
||||
CParseResult result;
|
||||
|
||||
int rawLineNum = 0;
|
||||
int lineNum = 0;
|
||||
std::string line = "";
|
||||
int linenum = 1;
|
||||
|
||||
std::stringstream str(stream);
|
||||
|
||||
while (true) {
|
||||
const auto line = getNextLine(str, rawLineNum, lineNum);
|
||||
|
||||
if (!line) {
|
||||
switch (line.error()) {
|
||||
case GETNEXTLINEFAILURE_EOF: break;
|
||||
case GETNEXTLINEFAILURE_BACKSLASH:
|
||||
if (!impl->parseError.empty())
|
||||
impl->parseError += "\n";
|
||||
impl->parseError += std::format("Config error: Last line ends with backslash");
|
||||
result.setError(impl->parseError);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
const auto RET = parseLine(line.value());
|
||||
while (std::getline(str, line)) {
|
||||
const auto RET = parseLine(line);
|
||||
|
||||
if (RET.error && (impl->parseError.empty() || impl->configOptions.throwAllErrors)) {
|
||||
if (!impl->parseError.empty())
|
||||
impl->parseError += "\n";
|
||||
impl->parseError += std::format("Config error at line {}: {}", lineNum, RET.errorStdString);
|
||||
impl->parseError += std::format("Config error at line {}: {}", linenum, RET.errorStdString);
|
||||
result.setError(impl->parseError);
|
||||
}
|
||||
|
||||
++linenum;
|
||||
}
|
||||
|
||||
if (!impl->categories.empty()) {
|
||||
|
@ -843,33 +746,21 @@ CParseResult CConfig::parseFile(const char* file) {
|
|||
return result;
|
||||
}
|
||||
|
||||
int rawLineNum = 0;
|
||||
int lineNum = 0;
|
||||
std::string line = "";
|
||||
int linenum = 1;
|
||||
|
||||
while (true) {
|
||||
const auto line = getNextLine(iffile, rawLineNum, lineNum);
|
||||
while (std::getline(iffile, line)) {
|
||||
|
||||
if (!line) {
|
||||
switch (line.error()) {
|
||||
case GETNEXTLINEFAILURE_EOF: break;
|
||||
case GETNEXTLINEFAILURE_BACKSLASH:
|
||||
if (!impl->parseError.empty())
|
||||
impl->parseError += "\n";
|
||||
impl->parseError += std::format("Config error in file {}: Last line ends with backslash", file);
|
||||
result.setError(impl->parseError);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
const auto RET = parseLine(line.value());
|
||||
const auto RET = parseLine(line);
|
||||
|
||||
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);
|
||||
impl->parseError += std::format("Config error in file {} at line {}: {}", file, linenum, RET.errorStdString);
|
||||
result.setError(impl->parseError);
|
||||
}
|
||||
|
||||
++linenum;
|
||||
}
|
||||
|
||||
iffile.close();
|
||||
|
@ -930,7 +821,7 @@ CConfigValue* CConfig::getSpecialConfigValuePtr(const char* category, const char
|
|||
void CConfig::registerHandler(PCONFIGHANDLERFUNC func, const char* name, SHandlerOptions options_) {
|
||||
SHandlerOptions options;
|
||||
std::memcpy(&options, &options_, seekABIStructSize(&options_, 0, sizeof(SHandlerOptions)));
|
||||
impl->handlers.push_back(SHandler{.name = name, .options = options, .func = func});
|
||||
impl->handlers.push_back(SHandler{name, options, func});
|
||||
}
|
||||
|
||||
void CConfig::unregisterHandler(const char* name) {
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <expected>
|
||||
|
||||
struct SHandler {
|
||||
std::string name = "";
|
||||
|
@ -66,11 +64,6 @@ struct SSpecialCategory {
|
|||
size_t anonymousID = 0;
|
||||
};
|
||||
|
||||
enum eGetNextLineFailure : uint8_t {
|
||||
GETNEXTLINEFAILURE_EOF = 0,
|
||||
GETNEXTLINEFAILURE_BACKSLASH,
|
||||
};
|
||||
|
||||
class CConfigImpl {
|
||||
public:
|
||||
std::string path = "";
|
||||
|
@ -95,8 +88,7 @@ class CConfigImpl {
|
|||
|
||||
Hyprlang::SConfigOptions configOptions;
|
||||
|
||||
void parseComment(const std::string& comment);
|
||||
std::expected<float, std::string> parseExpression(const std::string& s);
|
||||
void parseComment(const std::string& comment);
|
||||
|
||||
struct {
|
||||
bool noError = false;
|
||||
|
|
|
@ -14,9 +14,6 @@ $MY_VAR = 1337
|
|||
$MY_VAR_2 = $MY_VAR
|
||||
testVar = $MY_VAR$MY_VAR_2
|
||||
|
||||
$EXPR_VAR = {{MY_VAR + 2}}
|
||||
testExpr = {{EXPR_VAR - 4}}
|
||||
|
||||
testEnv = $SHELL
|
||||
|
||||
source = ./colors.conf
|
||||
|
@ -31,9 +28,6 @@ errorVariable = true
|
|||
|
||||
# hyprlang noerror false
|
||||
|
||||
categoryKeyword = oops, this one shouldn't call the handler, not fun
|
||||
testUseKeyword = yes
|
||||
|
||||
testCategory {
|
||||
testValueInt = 123456
|
||||
testValueHex = 0xF
|
||||
|
@ -42,20 +36,12 @@ testCategory {
|
|||
testColor2 = rgba(0, 0, 0, 1.0)
|
||||
testColor3 = rgba(ffeeff22)
|
||||
|
||||
testIgnoreKeyword = aaa
|
||||
testUseKeyword = no
|
||||
|
||||
nested1 {
|
||||
testValueNest = 1
|
||||
nested2 {
|
||||
testValueNest = 1
|
||||
}
|
||||
categoryKeyword = this one should not either
|
||||
}
|
||||
|
||||
categoryKeyword = we are having fun
|
||||
categoryKeyword = so much fun
|
||||
categoryKeyword = im the fun one at parties
|
||||
}
|
||||
|
||||
$SPECIALVAL1 = 1
|
||||
|
@ -89,15 +75,6 @@ specialAnonymous {
|
|||
value = 3
|
||||
}
|
||||
|
||||
flagsStuff {
|
||||
value = 2
|
||||
}
|
||||
|
||||
multiline = \
|
||||
very \
|
||||
long \
|
||||
command
|
||||
|
||||
testCategory:testValueHex = 0xFFfFaAbB
|
||||
|
||||
$RECURSIVE1 = a
|
||||
|
@ -111,3 +88,4 @@ doABarrelRoll = woohoo, some, params # Funny!
|
|||
flagsabc = test
|
||||
#doSomethingFunny = 1, 2, 3, 4 # Funnier!
|
||||
#testSpaces = abc , def # many spaces, should be trimmed
|
||||
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
# Every number/color in this file is invalid
|
||||
|
||||
invalidHex = 0x1Q
|
||||
emptyHex = 0x
|
||||
hugeHex = 0xFFFFFFFFFFFFFFFF
|
||||
|
||||
invalidInt = 1A
|
||||
emptyInt =
|
||||
|
||||
invalidColor = rgb(ABCDEQ)
|
||||
invalidFirstCharColor = rgb(QABCDE)
|
||||
|
||||
invalidColorAlpha = rgba(9ABCDEFQ)
|
||||
invalidFirstCharColorAlpha = rgba(Q9ABCDEF)
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
# Careful when modifying this file. Line numbers are part of the test.
|
||||
|
||||
multiline = \
|
||||
one \
|
||||
two \
|
||||
three
|
||||
|
||||
# Line numbers reported in errors should match the actual line numbers of the source file
|
||||
# even after multi-line configs. Any errors reported should use the line number of the
|
||||
# first line of any multi-line config.
|
||||
|
||||
this \
|
||||
should \
|
||||
cause \
|
||||
error \
|
||||
on \
|
||||
line \
|
||||
12
|
||||
|
||||
# A config file cannot end with a bashslash because we are expecting another line! Even in a comment! \
|
|
@ -11,7 +11,7 @@ std::string garbage() {
|
|||
|
||||
std::string chars;
|
||||
for (int i = 0; i < len; ++i) {
|
||||
chars += std::to_string((rand() % 254) + 1);
|
||||
chars += rand() % 254 + 1;
|
||||
}
|
||||
|
||||
return chars;
|
||||
|
|
|
@ -23,15 +23,12 @@ namespace Colors {
|
|||
}
|
||||
|
||||
// globals for testing
|
||||
bool barrelRoll = false;
|
||||
std::string flagsFound = "";
|
||||
Hyprlang::CConfig* pConfig = nullptr;
|
||||
std::string currentPath = "";
|
||||
std::string ignoreKeyword = "";
|
||||
std::string useKeyword = "";
|
||||
static std::vector<std::string> categoryKeywordActualValues;
|
||||
bool barrelRoll = false;
|
||||
std::string flagsFound = "";
|
||||
Hyprlang::CConfig* pConfig = nullptr;
|
||||
std::string currentPath = "";
|
||||
|
||||
static Hyprlang::CParseResult handleDoABarrelRoll(const char* COMMAND, const char* VALUE) {
|
||||
static Hyprlang::CParseResult handleDoABarrelRoll(const char* COMMAND, const char* VALUE) {
|
||||
if (std::string(VALUE) == "woohoo, some, params")
|
||||
barrelRoll = true;
|
||||
|
||||
|
@ -47,28 +44,6 @@ static Hyprlang::CParseResult handleFlagsTest(const char* COMMAND, const char* V
|
|||
return result;
|
||||
}
|
||||
|
||||
static Hyprlang::CParseResult handleCategoryKeyword(const char* COMMAND, const char* VALUE) {
|
||||
categoryKeywordActualValues.emplace_back(VALUE);
|
||||
|
||||
return Hyprlang::CParseResult();
|
||||
}
|
||||
|
||||
static Hyprlang::CParseResult handleTestIgnoreKeyword(const char* COMMAND, const char* VALUE) {
|
||||
ignoreKeyword = VALUE;
|
||||
|
||||
return Hyprlang::CParseResult();
|
||||
}
|
||||
|
||||
static Hyprlang::CParseResult handleTestUseKeyword(const char* COMMAND, const char* VALUE) {
|
||||
useKeyword = VALUE;
|
||||
|
||||
return Hyprlang::CParseResult();
|
||||
}
|
||||
|
||||
static Hyprlang::CParseResult handleNoop(const char* COMMAND, const char* VALUE) {
|
||||
return Hyprlang::CParseResult();
|
||||
}
|
||||
|
||||
static Hyprlang::CParseResult handleSource(const char* COMMAND, const char* VALUE) {
|
||||
std::string PATH = std::filesystem::canonical(currentPath + "/" + VALUE);
|
||||
return pConfig->parseFile(PATH.c_str());
|
||||
|
@ -107,52 +82,42 @@ int main(int argc, char** argv, char** envp) {
|
|||
|
||||
// setup config
|
||||
config.addConfigValue("testInt", (Hyprlang::INT)0);
|
||||
config.addConfigValue("testExpr", (Hyprlang::INT)0);
|
||||
config.addConfigValue("testFloat", 0.F);
|
||||
config.addConfigValue("testVec", Hyprlang::SVector2D{.x = 69, .y = 420});
|
||||
config.addConfigValue("testVec", Hyprlang::SVector2D{69, 420});
|
||||
config.addConfigValue("testString", "");
|
||||
config.addConfigValue("testStringColon", "");
|
||||
config.addConfigValue("testEnv", "");
|
||||
config.addConfigValue("testVar", (Hyprlang::INT)0);
|
||||
config.addConfigValue("categoryKeyword", (Hyprlang::STRING) "");
|
||||
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);
|
||||
config.addConfigValue("testCategory:nested1:nested2:testValueNest", (Hyprlang::INT)0);
|
||||
config.addConfigValue("testCategory:nested1:categoryKeyword", (Hyprlang::STRING) "");
|
||||
config.addConfigValue("testDefault", (Hyprlang::INT)123);
|
||||
config.addConfigValue("testCategory:testColor1", (Hyprlang::INT)0);
|
||||
config.addConfigValue("testCategory:testColor2", (Hyprlang::INT)0);
|
||||
config.addConfigValue("testCategory:testColor3", (Hyprlang::INT)0);
|
||||
config.addConfigValue("flagsStuff:value", (Hyprlang::INT)0);
|
||||
config.addConfigValue("myColors:pink", (Hyprlang::INT)0);
|
||||
config.addConfigValue("myColors:green", (Hyprlang::INT)0);
|
||||
config.addConfigValue("myColors:random", (Hyprlang::INT)0);
|
||||
config.addConfigValue("customType", {Hyprlang::CConfigCustomValueType{&handleCustomValueSet, &handleCustomValueDestroy, "def"}});
|
||||
|
||||
config.registerHandler(&handleDoABarrelRoll, "doABarrelRoll", {.allowFlags = false});
|
||||
config.registerHandler(&handleFlagsTest, "flags", {.allowFlags = true});
|
||||
config.registerHandler(&handleSource, "source", {.allowFlags = false});
|
||||
config.registerHandler(&handleTestIgnoreKeyword, "testIgnoreKeyword", {.allowFlags = false});
|
||||
config.registerHandler(&handleTestUseKeyword, ":testUseKeyword", {.allowFlags = false});
|
||||
config.registerHandler(&handleNoop, "testCategory:testUseKeyword", {.allowFlags = false});
|
||||
config.registerHandler(&handleCategoryKeyword, "testCategory:categoryKeyword", {.allowFlags = false});
|
||||
config.registerHandler(&handleDoABarrelRoll, "doABarrelRoll", {false});
|
||||
config.registerHandler(&handleFlagsTest, "flags", {true});
|
||||
config.registerHandler(&handleSource, "source", {false});
|
||||
|
||||
config.addSpecialCategory("special", {.key = "key"});
|
||||
config.addSpecialCategory("special", {"key"});
|
||||
config.addSpecialConfigValue("special", "value", (Hyprlang::INT)0);
|
||||
|
||||
config.addSpecialCategory("specialAnonymous", {.key = nullptr, .ignoreMissing = false, .anonymousKeyBased = true});
|
||||
config.addSpecialCategory("specialAnonymous", {nullptr, false, true});
|
||||
config.addSpecialConfigValue("specialAnonymous", "value", (Hyprlang::INT)0);
|
||||
|
||||
config.addConfigValue("multiline", "");
|
||||
|
||||
config.commence();
|
||||
|
||||
config.addSpecialCategory("specialGeneric:one", {.key = nullptr, .ignoreMissing = true});
|
||||
config.addSpecialCategory("specialGeneric:one", {nullptr, true});
|
||||
config.addSpecialConfigValue("specialGeneric:one", "value", (Hyprlang::INT)0);
|
||||
config.addSpecialCategory("specialGeneric:two", {.key = nullptr, .ignoreMissing = true});
|
||||
config.addSpecialCategory("specialGeneric:two", {nullptr, true});
|
||||
config.addSpecialConfigValue("specialGeneric:two", "value", (Hyprlang::INT)0);
|
||||
|
||||
const Hyprlang::CConfigValue copyTest = {(Hyprlang::INT)1};
|
||||
|
@ -170,7 +135,7 @@ int main(int argc, char** argv, char** envp) {
|
|||
std::cout << " → Testing values\n";
|
||||
EXPECT(std::any_cast<int64_t>(config.getConfigValue("testInt")), 123);
|
||||
EXPECT(std::any_cast<float>(config.getConfigValue("testFloat")), 123.456f);
|
||||
auto EXP = Hyprlang::SVector2D{.x = 69, .y = 420};
|
||||
auto EXP = Hyprlang::SVector2D{69, 420};
|
||||
EXPECT(std::any_cast<Hyprlang::SVector2D>(config.getConfigValue("testVec")), EXP);
|
||||
EXPECT(std::any_cast<const char*>(config.getConfigValue("testString")), std::string{"Hello World! # This is not a comment!"});
|
||||
EXPECT(std::any_cast<const char*>(config.getConfigValue("testStringQuotes")), std::string{"\"Hello World!\""});
|
||||
|
@ -183,24 +148,6 @@ int main(int argc, char** argv, char** envp) {
|
|||
EXPECT(std::any_cast<int64_t>(config.getConfigValue("testCategory:testColor2")), (Hyprlang::INT)0xFF000000);
|
||||
EXPECT(std::any_cast<int64_t>(config.getConfigValue("testCategory:testColor3")), (Hyprlang::INT)0x22ffeeff);
|
||||
EXPECT(std::any_cast<const char*>(config.getConfigValue("testStringColon")), std::string{"ee:ee:ee"});
|
||||
EXPECT(std::any_cast<const char*>(config.getConfigValue("categoryKeyword")), std::string{"oops, this one shouldn't call the handler, not fun"});
|
||||
EXPECT(std::any_cast<const char*>(config.getConfigValue("testCategory:nested1:categoryKeyword")), std::string{"this one should not either"});
|
||||
EXPECT(ignoreKeyword, "aaa");
|
||||
EXPECT(useKeyword, "yes");
|
||||
|
||||
// Test templated wrapper
|
||||
auto T1 = Hyprlang::CSimpleConfigValue<Hyprlang::INT>(&config, "testInt");
|
||||
auto T2 = Hyprlang::CSimpleConfigValue<Hyprlang::FLOAT>(&config, "testFloat");
|
||||
auto T3 = Hyprlang::CSimpleConfigValue<Hyprlang::SVector2D>(&config, "testVec");
|
||||
auto T4 = Hyprlang::CSimpleConfigValue<std::string>(&config, "testString");
|
||||
EXPECT(*T1, 123);
|
||||
EXPECT(*T2, 123.456F);
|
||||
EXPECT(*T3, EXP);
|
||||
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
|
||||
std::cout << " → Testing static values\n";
|
||||
|
@ -214,10 +161,6 @@ int main(int argc, char** argv, char** envp) {
|
|||
EXPECT(barrelRoll, true);
|
||||
EXPECT(flagsFound, std::string{"abc"});
|
||||
|
||||
EXPECT(categoryKeywordActualValues.at(0), "we are having fun");
|
||||
EXPECT(categoryKeywordActualValues.at(1), "so much fun");
|
||||
EXPECT(categoryKeywordActualValues.at(2), "im the fun one at parties");
|
||||
|
||||
// test dynamic
|
||||
std::cout << " → Testing dynamic\n";
|
||||
barrelRoll = false;
|
||||
|
@ -227,8 +170,6 @@ int main(int argc, char** argv, char** envp) {
|
|||
EXPECT(std::any_cast<int64_t>(config.getConfigValue("testCategory:testValueHex")), (Hyprlang::INT)0xAABBCCDD);
|
||||
EXPECT(config.parseDynamic("testStringColon", "1:3:3:7").error, false);
|
||||
EXPECT(std::any_cast<const char*>(config.getConfigValue("testStringColon")), std::string{"1:3:3:7"});
|
||||
EXPECT(config.parseDynamic("flagsStuff:value = 69").error, false);
|
||||
EXPECT(std::any_cast<int64_t>(config.getConfigValue("flagsStuff:value")), (Hyprlang::INT)69);
|
||||
|
||||
// test dynamic special
|
||||
config.addSpecialConfigValue("specialGeneric:one", "boom", (Hyprlang::INT)0);
|
||||
|
@ -248,10 +189,6 @@ int main(int argc, char** argv, char** envp) {
|
|||
EXPECT(config.parseDynamic("$RECURSIVE1 = d").error, false);
|
||||
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
|
||||
std::cout << " → Testing env variables\n";
|
||||
EXPECT(std::any_cast<const char*>(config.getConfigValue("testEnv")), std::string{getenv("SHELL")});
|
||||
|
@ -290,9 +227,6 @@ int main(int argc, char** argv, char** envp) {
|
|||
std::cout << " → Testing custom types\n";
|
||||
EXPECT(*reinterpret_cast<int64_t*>(std::any_cast<void*>(config.getConfigValue("customType"))), (Hyprlang::INT)1);
|
||||
|
||||
// test multiline config
|
||||
EXPECT(std::any_cast<const char*>(config.getConfigValue("multiline")), std::string{"very long command"});
|
||||
|
||||
std::cout << " → Testing error.conf\n";
|
||||
Hyprlang::CConfig errorConfig("./config/error.conf", {.verifyOnly = true, .throwAllErrors = true});
|
||||
|
||||
|
@ -303,35 +237,6 @@ int main(int argc, char** argv, char** envp) {
|
|||
const auto ERRORSTR = std::string{ERRORS.getError()};
|
||||
EXPECT(std::count(ERRORSTR.begin(), ERRORSTR.end(), '\n'), 1);
|
||||
|
||||
std::cout << " → Testing invalid-numbers.conf\n";
|
||||
Hyprlang::CConfig invalidNumbersConfig("./config/invalid-numbers.conf", {.throwAllErrors = true});
|
||||
invalidNumbersConfig.addConfigValue("invalidHex", (Hyprlang::INT)0);
|
||||
invalidNumbersConfig.addConfigValue("emptyHex", (Hyprlang::INT)0);
|
||||
invalidNumbersConfig.addConfigValue("hugeHex", (Hyprlang::INT)0);
|
||||
invalidNumbersConfig.addConfigValue("invalidInt", (Hyprlang::INT)0);
|
||||
invalidNumbersConfig.addConfigValue("emptyInt", (Hyprlang::INT)0);
|
||||
invalidNumbersConfig.addConfigValue("invalidColor", (Hyprlang::INT)0);
|
||||
invalidNumbersConfig.addConfigValue("invalidFirstCharColor", (Hyprlang::INT)0);
|
||||
invalidNumbersConfig.addConfigValue("invalidColorAlpha", (Hyprlang::INT)0);
|
||||
invalidNumbersConfig.addConfigValue("invalidFirstCharColorAlpha", (Hyprlang::INT)0);
|
||||
|
||||
invalidNumbersConfig.commence();
|
||||
const auto ERRORS2 = invalidNumbersConfig.parse();
|
||||
|
||||
EXPECT(ERRORS2.error, true);
|
||||
const auto ERRORSTR2 = std::string{ERRORS2.getError()};
|
||||
EXPECT(std::count(ERRORSTR2.begin(), ERRORSTR2.end(), '\n'), 9 - 1);
|
||||
|
||||
Hyprlang::CConfig multilineErrorConfig("./config/multiline-errors.conf", {.verifyOnly = true, .throwAllErrors = true});
|
||||
multilineErrorConfig.commence();
|
||||
const auto ERRORS3 = multilineErrorConfig.parse();
|
||||
EXPECT(ERRORS3.error, true);
|
||||
const auto ERRORSTR3 = std::string{ERRORS3.getError()};
|
||||
|
||||
// Error on line 12
|
||||
EXPECT(ERRORSTR3.contains("12"), true);
|
||||
// Backslash at end of file
|
||||
EXPECT(ERRORSTR3.contains("backslash"), true);
|
||||
} catch (const char* e) {
|
||||
std::cout << Colors::RED << "Error: " << Colors::RESET << e << "\n";
|
||||
return 1;
|
||||
|
|
Loading…
Reference in a new issue