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.

115 lines
2.9 KiB
JavaScript

"use strict";
const asExpression = require("as-expression");
const assert = require("assert");
const types = require("@babel/types");
const template = require("@babel/template").default;
const unpackExpression = require("./_unpack-expression");
// NOTE: These are arrow functions because variable references within functions should always refer to the nearest scope; and since we use `this` to handle variable references within recursive attribute sets, we need to ensure that a function definition *does not* create its own `this` context.
let tmplFunctionDefinitionFormalsUniversal = template(`
((%%universal%%) => {
const %%formals%% = %%universal%%;
return %%body%%;
})
`);
// FIXME: Test that this template form actually works with our formals structure
let tmplFunctionDefinitionFormals = template(`
((%%formals%%) => {
return %%body%%;
})
`);
let tmplFunctionDefinitionUniversal = template(`
((%%universal%%) => {
return %%body%%;
})
`);
let tmplFunctionCall = template(`
(%%function%%(%%arg%%))
`);
// NOTE: Duplicated from `attribute-sets` for now
let tmplLazyWrapper = template(`(
$$jsNix$memoize(() => %%expression%%)
)`);
function functionDefinition({ universal, formals, body }) {
let convertedFormals = asExpression(() => {
if (formals != null) {
return types.objectPattern(formals.map((formal) => {
return types.objectProperty(
types.identifier(formal),
types.identifier(formal)
);
}));
} else {
return undefined;
}
});
if (universal != null && formals != null) {
return tmplFunctionDefinitionFormalsUniversal({
body: body,
universal: universal,
formals: convertedFormals
});
} else if (universal != null) {
return tmplFunctionDefinitionUniversal({
body: body,
universal: universal
});
} else {
return tmplFunctionDefinitionFormals({
body: body,
formals: convertedFormals
});
}
}
module.exports = {
name: "functions",
visitors: {
NixFunctionDefinition: (_node, { defer, setContext }) => {
setContext([ "universal", "formals" ], "identifierType", "define");
return defer((node) => {
return unpackExpression(
functionDefinition({
universal: asExpression(() => {
if (node.universal != null) {
assert(node.universal.type === "Identifier");
return node.universal.name;
}
}),
formals: asExpression(() => {
if (node.formals != null) {
assert(node.formals.type === "NixUnpackedAttributes");
return node.formals.formal.map((formal) => {
assert(formal.type === "NixUnpackedAttribute");
assert(formal.name.type === "Identifier");
return formal.name.name;
});
}
}),
body: node.body
})
);
});
},
NixFunctionCall: (_node, { defer }) => {
return defer((node) => {
return unpackExpression(
tmplFunctionCall({
function: node.function,
arg: unpackExpression(tmplLazyWrapper({ expression: node.argument }))
})
);
});
}
}
};