forked from lix-project/lix
primops: Move genericClosure to attrset.cc
Change-Id: I0f8ae3dfcc1a4e4ce6e631e13e5b63ac03c1cd74
This commit is contained in:
parent
1e1cc1d741
commit
0bf532b0c1
|
@ -174,99 +174,6 @@ template<typename Callable>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void prim_genericClosure(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
|
||||||
{
|
|
||||||
state.forceAttrs(*args[0], noPos, "while evaluating the first argument passed to builtins.genericClosure");
|
|
||||||
|
|
||||||
/* Get the start set. */
|
|
||||||
Bindings::iterator startSet = getAttr(state, state.sStartSet, args[0]->attrs, "in the attrset passed as argument to builtins.genericClosure");
|
|
||||||
|
|
||||||
state.forceList(*startSet->value, noPos, "while evaluating the 'startSet' attribute passed as argument to builtins.genericClosure");
|
|
||||||
|
|
||||||
ValueList workSet;
|
|
||||||
for (auto elem : startSet->value->listItems())
|
|
||||||
workSet.push_back(elem);
|
|
||||||
|
|
||||||
if (startSet->value->listSize() == 0) {
|
|
||||||
v = *startSet->value;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get the operator. */
|
|
||||||
Bindings::iterator op = getAttr(state, state.sOperator, args[0]->attrs, "in the attrset passed as argument to builtins.genericClosure");
|
|
||||||
state.forceFunction(*op->value, noPos, "while evaluating the 'operator' attribute passed as argument to builtins.genericClosure");
|
|
||||||
|
|
||||||
/* Construct the closure by applying the operator to elements of
|
|
||||||
`workSet', adding the result to `workSet', continuing until
|
|
||||||
no new elements are found. */
|
|
||||||
ValueList res;
|
|
||||||
// `doneKeys' doesn't need to be a GC root, because its values are
|
|
||||||
// reachable from res.
|
|
||||||
auto cmp = CompareValues(state, noPos, "while comparing the `key` attributes of two genericClosure elements");
|
|
||||||
std::set<Value *, decltype(cmp)> doneKeys(cmp);
|
|
||||||
while (!workSet.empty()) {
|
|
||||||
Value * e = *(workSet.begin());
|
|
||||||
workSet.pop_front();
|
|
||||||
|
|
||||||
state.forceAttrs(*e, noPos, "while evaluating one of the elements generated by (or initially passed to) builtins.genericClosure");
|
|
||||||
|
|
||||||
Bindings::iterator key = getAttr(state, state.sKey, e->attrs, "in one of the attrsets generated by (or initially passed to) builtins.genericClosure");
|
|
||||||
state.forceValue(*key->value, noPos);
|
|
||||||
|
|
||||||
if (!doneKeys.insert(key->value).second) continue;
|
|
||||||
res.push_back(e);
|
|
||||||
|
|
||||||
/* Call the `operator' function with `e' as argument. */
|
|
||||||
Value newElements;
|
|
||||||
state.callFunction(*op->value, 1, &e, newElements, noPos);
|
|
||||||
state.forceList(newElements, noPos, "while evaluating the return value of the `operator` passed to builtins.genericClosure");
|
|
||||||
|
|
||||||
/* Add the values returned by the operator to the work set. */
|
|
||||||
for (auto elem : newElements.listItems()) {
|
|
||||||
state.forceValue(*elem, noPos); // "while evaluating one one of the elements returned by the `operator` passed to builtins.genericClosure");
|
|
||||||
workSet.push_back(elem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Create the result list. */
|
|
||||||
state.mkList(v, res.size());
|
|
||||||
unsigned int n = 0;
|
|
||||||
for (auto & i : res)
|
|
||||||
v.listElems()[n++] = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
static RegisterPrimOp primop_genericClosure(PrimOp {
|
|
||||||
.name = "__genericClosure",
|
|
||||||
.args = {"attrset"},
|
|
||||||
.arity = 1,
|
|
||||||
.doc = R"(
|
|
||||||
Take an *attrset* with values named `startSet` and `operator` in order to
|
|
||||||
return a *list of attrsets* by starting with the `startSet` and recursively
|
|
||||||
applying the `operator` function to each `item`. The *attrsets* in the
|
|
||||||
`startSet` and the *attrsets* produced by `operator` must contain a value
|
|
||||||
named `key` which is comparable. The result is produced by calling `operator`
|
|
||||||
for each `item` with a value for `key` that has not been called yet including
|
|
||||||
newly produced `item`s. The function terminates when no new `item`s are
|
|
||||||
produced. The resulting *list of attrsets* contains only *attrsets* with a
|
|
||||||
unique key. For example,
|
|
||||||
|
|
||||||
```
|
|
||||||
builtins.genericClosure {
|
|
||||||
startSet = [ {key = 5;} ];
|
|
||||||
operator = item: [{
|
|
||||||
key = if (item.key / 2 ) * 2 == item.key
|
|
||||||
then item.key / 2
|
|
||||||
else 3 * item.key + 1;
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
```
|
|
||||||
evaluates to
|
|
||||||
```
|
|
||||||
[ { key = 5; } { key = 16; } { key = 8; } { key = 4; } { key = 2; } { key = 1; } ]
|
|
||||||
```
|
|
||||||
)",
|
|
||||||
.fun = prim_genericClosure,
|
|
||||||
});
|
|
||||||
|
|
||||||
static void prim_addErrorContext(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
static void prim_addErrorContext(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
|
|
|
@ -133,6 +133,145 @@ static RegisterPrimOp primop_catAttrs({
|
||||||
.fun = prim_catAttrs,
|
.fun = prim_catAttrs,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* builtins.genericClosure
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void prim_genericClosure(EvalState & state, const PosIdx pos, Value ** args, Value & v)
|
||||||
|
{
|
||||||
|
state.forceAttrs(
|
||||||
|
*args[0], noPos, "while evaluating the first argument passed to builtins.genericClosure"
|
||||||
|
);
|
||||||
|
|
||||||
|
/* Get the start set. */
|
||||||
|
Bindings::iterator startSet = getAttr(
|
||||||
|
state,
|
||||||
|
state.sStartSet,
|
||||||
|
args[0]->attrs,
|
||||||
|
"in the attrset passed as argument to builtins.genericClosure"
|
||||||
|
);
|
||||||
|
|
||||||
|
state.forceList(
|
||||||
|
*startSet->value,
|
||||||
|
noPos,
|
||||||
|
"while evaluating the 'startSet' attribute passed as argument to builtins.genericClosure"
|
||||||
|
);
|
||||||
|
|
||||||
|
ValueList workSet;
|
||||||
|
for (auto elem : startSet->value->listItems()) {
|
||||||
|
workSet.push_back(elem);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startSet->value->listSize() == 0) {
|
||||||
|
v = *startSet->value;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get the operator. */
|
||||||
|
Bindings::iterator op = getAttr(
|
||||||
|
state,
|
||||||
|
state.sOperator,
|
||||||
|
args[0]->attrs,
|
||||||
|
"in the attrset passed as argument to builtins.genericClosure"
|
||||||
|
);
|
||||||
|
state.forceFunction(
|
||||||
|
*op->value,
|
||||||
|
noPos,
|
||||||
|
"while evaluating the 'operator' attribute passed as argument to builtins.genericClosure"
|
||||||
|
);
|
||||||
|
|
||||||
|
/* Construct the closure by applying the operator to elements of
|
||||||
|
`workSet', adding the result to `workSet', continuing until
|
||||||
|
no new elements are found. */
|
||||||
|
ValueList res;
|
||||||
|
// `doneKeys' doesn't need to be a GC root, because its values are
|
||||||
|
// reachable from res.
|
||||||
|
auto cmp = CompareValues(
|
||||||
|
state, noPos, "while comparing the `key` attributes of two genericClosure elements"
|
||||||
|
);
|
||||||
|
std::set<Value *, decltype(cmp)> doneKeys(cmp);
|
||||||
|
while (!workSet.empty()) {
|
||||||
|
Value * e = *(workSet.begin());
|
||||||
|
workSet.pop_front();
|
||||||
|
|
||||||
|
state.forceAttrs(
|
||||||
|
*e,
|
||||||
|
noPos,
|
||||||
|
"while evaluating one of the elements generated by (or initially passed to) "
|
||||||
|
"builtins.genericClosure"
|
||||||
|
);
|
||||||
|
|
||||||
|
Bindings::iterator key = getAttr(
|
||||||
|
state,
|
||||||
|
state.sKey,
|
||||||
|
e->attrs,
|
||||||
|
"in one of the attrsets generated by (or initially passed to) builtins.genericClosure"
|
||||||
|
);
|
||||||
|
state.forceValue(*key->value, noPos);
|
||||||
|
|
||||||
|
if (!doneKeys.insert(key->value).second) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
res.push_back(e);
|
||||||
|
|
||||||
|
/* Call the `operator' function with `e' as argument. */
|
||||||
|
Value newElements;
|
||||||
|
state.callFunction(*op->value, 1, &e, newElements, noPos);
|
||||||
|
state.forceList(
|
||||||
|
newElements,
|
||||||
|
noPos,
|
||||||
|
"while evaluating the return value of the `operator` passed to builtins.genericClosure"
|
||||||
|
);
|
||||||
|
|
||||||
|
/* Add the values returned by the operator to the work set. */
|
||||||
|
for (auto elem : newElements.listItems()) {
|
||||||
|
state.forceValue(*elem, noPos); // "while evaluating one one of the elements returned by
|
||||||
|
// the `operator` passed to builtins.genericClosure");
|
||||||
|
workSet.push_back(elem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create the result list. */
|
||||||
|
state.mkList(v, res.size());
|
||||||
|
unsigned int n = 0;
|
||||||
|
for (auto & i : res) {
|
||||||
|
v.listElems()[n++] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static RegisterPrimOp primop_genericClosure(PrimOp{
|
||||||
|
.name = "__genericClosure",
|
||||||
|
.args = {"attrset"},
|
||||||
|
.arity = 1,
|
||||||
|
.doc = R"(
|
||||||
|
Take an *attrset* with values named `startSet` and `operator` in order to
|
||||||
|
return a *list of attrsets* by starting with the `startSet` and recursively
|
||||||
|
applying the `operator` function to each `item`. The *attrsets* in the
|
||||||
|
`startSet` and the *attrsets* produced by `operator` must contain a value
|
||||||
|
named `key` which is comparable. The result is produced by calling `operator`
|
||||||
|
for each `item` with a value for `key` that has not been called yet including
|
||||||
|
newly produced `item`s. The function terminates when no new `item`s are
|
||||||
|
produced. The resulting *list of attrsets* contains only *attrsets* with a
|
||||||
|
unique key. For example,
|
||||||
|
|
||||||
|
```
|
||||||
|
builtins.genericClosure {
|
||||||
|
startSet = [ {key = 5;} ];
|
||||||
|
operator = item: [{
|
||||||
|
key = if (item.key / 2 ) * 2 == item.key
|
||||||
|
then item.key / 2
|
||||||
|
else 3 * item.key + 1;
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
evaluates to
|
||||||
|
```
|
||||||
|
[ { key = 5; } { key = 16; } { key = 8; } { key = 4; } { key = 2; } { key = 1; } ]
|
||||||
|
```
|
||||||
|
)",
|
||||||
|
.fun = prim_genericClosure,
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* builtins.getAttr
|
* builtins.getAttr
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in a new issue