"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 ] }; } } } } };