You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

80 lines
2.4 KiB
JavaScript

"use strict";
const splitFilter = require("split-filter");
const assert = require("assert");
const NoChange = require("../astformer/actions/no-change");
const nixTypes = require("./util/nix-types");
module.exports = {
name: "desugar-inherits",
visitors: {
NixAttributeSet: (node) => {
let [ inherits, regularBindings ] = splitFilter(node.bind, (binding) => binding.type === "NixInherit");
if (inherits.length === 0) {
return NoChange;
} else {
let tempCounter = 0;
let inherits_ = inherits.map((inherit) => {
return {
tempName: `$temp$${tempCounter++}`,
sourceExpression: inherit.expression,
names: inherit.attrs.attr.map((attribute) => {
assert(attribute.type === "NixAttributeIdentifier");
return attribute.name;
})
};
});
let letBindings = inherits_.map((inherit) => {
return nixTypes.NixBinding(
[ nixTypes.NixAttributeIdentifier(inherit.tempName) ],
inherit.sourceExpression
);
});
let inheritedAttributeBindings = inherits_.flatMap((inherit) => {
return inherit.names.map((name) => {
return nixTypes.NixBinding(
[ nixTypes.NixAttributeIdentifier(name) ],
nixTypes.NixAttributeSelection(
nixTypes.NixIdentifier(inherit.tempName),
[ nixTypes.NixAttributeIdentifier(name) ]
)
);
});
});
if (node.recursive === false) {
// When not recursive, we need to ensure that the temporary variables are defined a scope *higher* than the attribute set that actually makes use of their properties, because we cannot access those variables from the attribute bindings otherwise.
return nixTypes.NixLetIn(
letBindings,
{
... node,
bind: [
... regularBindings,
... inheritedAttributeBindings
]
}
);
} else {
// ... but when recursive, we *must* specify the temporary variables as part of the recursive attribute set itself, because otherwise we don't have the ability to bidirectionally reference between fixed attributes and inherited ones; see, for example, eval-okay-scope-7.nix.
// FIXME: Mark the temporary variables as 'internal' so that they don't get returned in the final object; this would make the generated code more readable.
return {
... node,
bind: [
... regularBindings,
... letBindings,
... inheritedAttributeBindings
]
};
}
}
}
}
};