From 0e5218513ec92ee751e7a0dc854082106b77473c Mon Sep 17 00:00:00 2001 From: atagen Date: Wed, 25 Feb 2026 12:18:18 +1100 Subject: [PATCH] init --- CMakeLists.txt | 11 +++++++++++ flake.lock | 27 ++++++++++++++++++++++++++ flake.nix | 38 ++++++++++++++++++++++++++++++++++++ scope.cc | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 128 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 scope.cc diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..3a9dd81 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.9) +project(nix-scope-plugin) + +find_package(PkgConfig REQUIRED) +pkg_check_modules(NIX_EXPR REQUIRED IMPORTED_TARGET nix-expr) + +add_library(nix-scope-plugin MODULE scope.cc) +target_link_libraries(nix-scope-plugin PkgConfig::NIX_EXPR) +set_target_properties(nix-scope-plugin PROPERTIES PREFIX "lib") + +install(TARGETS nix-scope-plugin DESTINATION lib/nix/plugins) diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..450d227 --- /dev/null +++ b/flake.lock @@ -0,0 +1,27 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1771923393, + "narHash": "sha256-Fy0+UXELv9hOE8WjYhJt8fMDLYTU2Dqn3cX4BwoGBos=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "ea7f1f06811ce7fcc81d6c6fd4213150c23edcf2", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..a98302f --- /dev/null +++ b/flake.nix @@ -0,0 +1,38 @@ +{ + description = "Nix plugin providing a `scope` builtin for constructing nested attrsets from dotted paths"; + + inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + + outputs = { self, nixpkgs }: + let + supportedSystems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ]; + forAllSystems = nixpkgs.lib.genAttrs supportedSystems; + in + { + packages = forAllSystems (system: + let + pkgs = nixpkgs.legacyPackages.${system}; + nix = pkgs.nix; + in + { + default = pkgs.stdenv.mkDerivation { + pname = "nix-scope-plugin"; + version = "0.1.0"; + src = self; + nativeBuildInputs = [ pkgs.cmake pkgs.pkg-config ]; + buildInputs = [ nix nix.dev pkgs.boost pkgs.nlohmann_json ]; + }; + }); + + devShells = forAllSystems (system: + let + pkgs = nixpkgs.legacyPackages.${system}; + nix = pkgs.nix; + in + { + default = pkgs.mkShell { + packages = [ pkgs.cmake pkgs.pkg-config nix nix.dev pkgs.boost pkgs.nlohmann_json ]; + }; + }); + }; +} diff --git a/scope.cc b/scope.cc new file mode 100644 index 0000000..8709a26 --- /dev/null +++ b/scope.cc @@ -0,0 +1,52 @@ +#include +#include +#include +#include +#include + +using namespace nix; + +static void prim_scope(EvalState & state, const PosIdx pos, Value ** args, Value & v) +{ + std::vector path; + + state.forceStringNoCtx(*args[0], pos, "'scope' path argument"); + auto s = std::string(args[0]->string_view()); + if (s.empty()) + state.error("'scope' requires a non-empty path") + .atPos(pos) + .debugThrow(); + size_t start = 0; + while (start < s.size()) { + auto dot = s.find('.', start); + if (dot == std::string::npos) { + path.push_back(state.symbols.create(s.substr(start))); + break; + } + path.push_back(state.symbols.create(s.substr(start, dot - start))); + start = dot + 1; + } + + // Build nested attrset from inside out + // Given path [a, b, c] and value v: { a = { b = { c = v; }; }; } + Value * inner = args[1]; + for (auto it = path.rbegin(); it != path.rend(); ++it) { + auto attrs = state.buildBindings(1); + attrs.insert(*it, inner); + Value * wrapper = state.allocValue(); + wrapper->mkAttrs(attrs.finish()); + inner = wrapper; + } + v = *inner; +} + +static RegisterPrimOp rp({ + .name = "scope", + .args = {"path", "value"}, + .doc = R"( + Construct a nested attribute set from a dotted string path and a value. + + Example: `scope "a.b.c" 42` evaluates to `{ a = { b = { c = 42; }; }; }`. + )", + .fun = prim_scope, +});