"use strict"; const measureTime = require("./astformer/util/measure-time"); const transpile = require("./transpile"); module.exports = function evaluate(nixCode) { let transpiled = transpile(nixCode); const api = { builtins: {}, $memoize: function (func) { let isCalled = false; let storedResult; return function (arg) { if (isCalled === false) { storedResult = func(arg); isCalled = true; } return storedResult; }; }, $handleArgument: function (name, _arg, defaultValue) { // We need to evaluate the lazy wrapper for `arg`, as it is passed in as an attribute set; and since that is a value, it will have been wrapped. const arg = _arg(); // FIXME: Improve this check, check for a (translated) attrset specifically if (typeof arg === "object") { // NOTE: We do *not* evaluate the actual attributes, nor the default value; them merely being present is enough for our case. const value = arg[name]; if (value !== undefined) { return value; } else if (defaultValue !== undefined) { return defaultValue; } else { throw new Error(`Missing required argument '${name}'`); } } else { // FIXME: Improve error throw new Error(`Must pass an attribute set to function that expects one`); } }, $assertUniqueKeys: function (keys) { let seen = new Set(); for (let key of keys) { if (seen.has(key)) { throw new Error(`Attempted to define duplicate attribute '${key}'`); } else { seen.add(key); } } } }; // TODO: Switch to Node `vm` API instead, and check whether there's a polyfill for it for non-Node environments, build a custom one if not const context = { module: {}, exports: {} }; context.module.exports = exports; new Function("module", transpiled)(context.module); // Warm-up for hot VM performance testing // for (let i = 0; i < 10000; i++) { // context.module.exports(api); // } let result = measureTime(() => context.module.exports(api)); if (process.env.DEBUG_NIX) { console.log("-- EVALUATION RESULT:"); console.log(result); } return result; };