52 lines
1.6 KiB
C++
52 lines
1.6 KiB
C++
#include <nix/expr/primops.hh>
|
|
#include <nix/expr/eval.hh>
|
|
#include <nix/expr/eval-inline.hh>
|
|
#include <nix/expr/nixexpr.hh>
|
|
#include <nix/expr/attr-set.hh>
|
|
|
|
using namespace nix;
|
|
|
|
static void prim_scope(EvalState & state, const PosIdx pos, Value ** args, Value & v)
|
|
{
|
|
std::vector<Symbol> path;
|
|
|
|
state.forceStringNoCtx(*args[0], pos, "'scope' path argument");
|
|
auto s = std::string(args[0]->string_view());
|
|
if (s.empty())
|
|
state.error<TypeError>("'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,
|
|
});
|