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)
|
||||
{
|
||||
|
|
|
@ -133,6 +133,145 @@ static RegisterPrimOp primop_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
|
||||
*/
|
||||
|
|
Loading…
Reference in a new issue