"use strict"; const asExpression = require("as-expression"); const assert = require("assert"); const types = require("@babel/types"); const template = require("@babel/template").default; const lazyWrapper = require("./templates/lazy-wrapper"); const callLazyWrapper = require("./templates/call-lazy-wrapper"); const templateExpression = require("./util/template-expression"); let tmplFunctionDefinitionUniversal = templateExpression(` ((%%universal%%) => { return %%body%%; }) `); let tmplFunctionDefinitionWithFormals = templateExpression(`( (%%universal%%) => { %%handleArguments%%; return %%body%%; } )`); let tmplHandleArgument = template(` const %%name%% = $handleArgument(%%nameString%%, %%universal%%, %%defaultArg%%); `); let tmplFunctionCall = templateExpression(` (%%function%%(%%arg%%)) `); function typesUndefined() { // Apparently there's no undefinedLiteral??? return types.unaryExpression("void", types.numericLiteral(0)); } function functionDefinition({ universal, formals, body }) { if (formals != null) { let defaultedUniversal = universal ?? "$tempArg"; return tmplFunctionDefinitionWithFormals({ universal: defaultedUniversal, body: body, handleArguments: formals.map((formal) => { return tmplHandleArgument({ universal: defaultedUniversal, name: types.identifier(formal.name), nameString: types.stringLiteral(formal.name), defaultArg: formal.default ?? typesUndefined() }); }) }); } else { return tmplFunctionDefinitionUniversal({ body: body, universal: universal }); } } module.exports = { name: "functions", visitors: { NixFunctionDefinition: (_node, { defer, setContext }) => { setContext([ "universal", "formals" ], "identifierType", "define"); return defer((node) => { return 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 { name: formal.name.name, // NOTE: This unwrap-and-rewrap dance is necessary to make out-of-order references work, like in eval-okay-scope-4.nix default: (formal.default != null) ? lazyWrapper(callLazyWrapper(formal.default)) : undefined }; }); } }), body: node.body }); }); }, NixFunctionCall: (_node, { defer }) => { return defer((node) => { return tmplFunctionCall({ function: node.function, arg: lazyWrapper(node.argument) }); }); } } };