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.

101 lines
2.9 KiB
JavaScript

"use strict";
const assert = require("assert");
const types = require("@babel/types");
const template = require("@babel/template").default;
const unpackExpression = require("./_unpack-expression");
const NoChange = require("../astformer/actions/no-change");
// FIXME: Add expression parens!
let tmplCallLazy = template(`
%%wrapper%%()
`);
let tmplLazyWrapper = template(`(
() => %%expression%%
)`);
let tmplLazyWrapperRecursive = template(`(
function() { return %%expression%%; }
)`);
let tmplExtend = template(`(
$$jsNix$extend(this ?? {}, %%object%%)
)`);
function lazyEvaluationWrapper(args) {
let { recursive, ... rest } = args;
let wrapper = (recursive)
? tmplLazyWrapperRecursive
: tmplLazyWrapper;
return unpackExpression(wrapper(rest));
}
function isDynamicBinding(binding) {
return binding.attrpath.attr[0].type !== "NixAttributeIdentifier";
}
module.exports = {
name: "attribute-sets",
visitors: {
Program: (_node, { setContext }) => {
setContext(null, "attributeSets_inRecursiveSet", false);
return NoChange;
},
NixAttributeSelection: (_node, { defer }) => {
// TODO: Optimize dynamic attribute access by translating builtins.getAttr to dynamic property access at compile time? This requires scope analysis!
return defer((node) => {
assert(node.attrpath.type === "NixAttributePath");
return node.attrpath.attr.reduce((last, identifier) => {
assert(identifier.type === "NixAttributeIdentifier");
return unpackExpression(tmplCallLazy({
wrapper: types.memberExpression(last, types.identifier(identifier.name))
}));
}, node.expression);
});
},
NixAttributeSet: (node, { defer, setContext, getContext }) => {
if (node.recursive) {
setContext(null, "attributeSets_inRecursiveSet", true);
}
return defer((node) => {
let isRecursive = node.recursive;
let hasDynamicBindings = node.bind.some((binding) => isDynamicBinding(binding));
if (hasDynamicBindings) {
throw new Error(`UNIMPLEMENTED: Dynamic bindings are not supported yet`);
} else {
let object = types.objectExpression(node.bind.map((binding) => {
assert(binding.attrpath.attr.length === 1); // Nested attributes should have been desugared by this point
let name = binding.attrpath.attr[0].name;
let expression = binding.expression;
return types.objectProperty(
types.stringLiteral(name),
lazyEvaluationWrapper({ recursive: isRecursive, expression: expression })
);
}));
// FIXME/MARKER: This is currently broken!
console.log({context: getContext("attributeSets_inRecursiveSet")});
// Only do prototypal inheritance for `this` if we're dealing with nested recursive attribute sets
if (isRecursive && getContext("attributeSets_inRecursiveSet") === true) {
return unpackExpression(tmplExtend({
object: object
}));
} else {
return object;
}
}
});
},
}
};