diff --git a/samples/attrset-nested-both.nix b/samples/attrset-nested-both.nix new file mode 100644 index 0000000..1f5074b --- /dev/null +++ b/samples/attrset-nested-both.nix @@ -0,0 +1 @@ +rec { a = 1; b = rec { c = d.foo; }; d = { foo = a; }; }.b.c diff --git a/src/transformers/attribute-sets.js b/src/transformers/attribute-sets.js index 54bdf2e..30741ac 100644 --- a/src/transformers/attribute-sets.js +++ b/src/transformers/attribute-sets.js @@ -5,6 +5,7 @@ const types = require("@babel/types"); const template = require("@babel/template").default; const unpackExpression = require("./_unpack-expression"); const NoChange = require("../astformer/actions/no-change"); +const printAst = require("../print-ast"); // FIXME: Add expression parens! let tmplCallLazy = template(` @@ -23,9 +24,23 @@ let tmplExtend = template(`( $$jsNix$extend(this ?? {}, %%object%%) )`); +let tmplScopeWrapper = template(`( + (() => { + %%bindings%%; + + return %%object%%; + })() +)`); + +// FIXME: Verify that this always works, and that we don't need `var` for hoisting! +let tmplRecursiveBinding = template(` + const %%name%% = %%expression%%; +`); + function lazyEvaluationWrapper(args) { let { recursive, ... rest } = args; + // printAst(rest.expression); let wrapper = (recursive) ? tmplLazyWrapperRecursive : tmplLazyWrapper; @@ -68,23 +83,41 @@ module.exports = { let isRecursive = node.recursive; let hasDynamicBindings = node.bind.some((binding) => isDynamicBinding(binding)); + let bindings = node.bind.map((binding) => { + assert(binding.attrpath.attr.length === 1); // Nested attributes should have been desugared by this point + + return { + name: binding.attrpath.attr[0].name, + expression: lazyEvaluationWrapper({ recursive: isRecursive, expression: binding.expression }) + }; + }); + if (hasDynamicBindings) { throw new Error(`UNIMPLEMENTED: Dynamic bindings are not supported yet`); + } else if (isRecursive) { + return unpackExpression(tmplScopeWrapper({ + bindings: bindings.map(({ name, expression }) => { + return tmplRecursiveBinding({ + name: name, + expression: expression + }); + }), + object: types.objectExpression(bindings.map(({ name }) => { + return types.objectProperty( + types.stringLiteral(name), + types.identifier(name) + ); + })) + })); } 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; - + let object = types.objectExpression(bindings.map(({ name, expression }) => { return types.objectProperty( types.stringLiteral(name), - lazyEvaluationWrapper({ recursive: isRecursive, expression: expression }) + // TODO: Remove the isRecursive distinction here once heap-of-vars works + 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({ diff --git a/src/transformers/functions.js b/src/transformers/functions.js index b47a732..32ecbb5 100644 --- a/src/transformers/functions.js +++ b/src/transformers/functions.js @@ -32,6 +32,11 @@ let tmplFunctionCall = template(` (%%function%%(%%arg%%)) `); +// NOTE: Duplicated from `attribute-sets` for now +let tmplLazyWrapper = template(`( + () => %%expression%% +)`); + function functionDefinition({ universal, formals, body }) { let convertedFormals = asExpression(() => { if (formals != null) { @@ -101,7 +106,7 @@ module.exports = { return unpackExpression( tmplFunctionCall({ function: node.function, - arg: node.argument + arg: unpackExpression(tmplLazyWrapper({ expression: node.argument })) }) ); }); diff --git a/src/transformers/index.js b/src/transformers/index.js index 445e2f2..1764dc5 100644 --- a/src/transformers/index.js +++ b/src/transformers/index.js @@ -47,7 +47,7 @@ let tmplObjectDynamicEntry = template(` `); let tmplIdentifierReference = template(`( - (this?.%%name%% ?? %%name%%)() + %%name%%() )`); function generateDynamicObject(bindings, recursive) {