const fs = require("fs") const kWantMaxSupportedElements = 100 const kMaxPairedElements = 10 const kMacroParamLimit = 127 function forN(n, cb) { for (var i = 0; i < n; i++) { cb(i) } } function genArray(n) { return [...Array(n).keys()] } function formatNJoin(n, start, join) { return start + genArray(n).join(join) } function formatNJoinReverseSuffix(n, start, join, suffix) { return start + genArray(n).reverse().join(suffix + join) + suffix } function formatNParamPattern(n, join) { return formatNJoin(n, join, ", " + join) } function formatNParamPatternReverseSuffix(n, join, suffix) { return formatNJoinReverseSuffix(n, join, ", " + join, suffix) } var textBuffer = "" textBuffer += `/** * X-Macro alternative. Defines AU_FOR_EACH[_THAT/_FIRST][_N](expandable, ...args*N) * Autogenerated by forEach.js; do not modify" */ #define _ZAUFE_FE_EXPAND(exp) exp #define _ZAUFE_FE_ERROR(exp) ERROR. CHECK PARAMETER COUNT. DID YOU FORGET A COMMA? ` forN(kMaxPairedElements, function(nElements) { if (!nElements) return var suffix = nElements != 1 ? "_" + (nElements): "" var nElementsMulNCapped = Math.min(kMacroParamLimit - 3, kWantMaxSupportedElements * nElements) var ignoreList = {} forN(nElementsMulNCapped, function(i) { if (i == 0) { textBuffer += `#define _ZAUFE_FE_0${suffix}(expandable) \n` ignoreList[`_ZAUFE_FE_0_FIRST${suffix}`] = true ignoreList[`_ZAUFE_FE_0_THAT${suffix}`] = true return } var X = formatNParamPattern(nElements, "X") var iM1 = i - 1 var iM1Translated = i - nElements if (i == nElements - 1) { // Edge case: the trailing macro does not do anything // We could hack in an _LAST callback here, should a need for it arise textBuffer += `#define _ZAUFE_FE_${i}${suffix}(expandable)\n` } else if ((i == 1) && (nElements != 1)) { // Edge case: FOR_EACH_N(EXPANDABLE, ), where N has to be defined/has an element count of > 1, this is technically illegal // However, we can just ignore empty element expansions in those instances // We can simply use the first 'macro has 2 arguments' slot to hook these (EXPANDABLE, ) conditions // AU_FOR_EACH CANNOT INTRINSICALLY TELL THE DIFFERENCE BETWEEN AN EMTPY VALUE AND ARG 0 textBuffer += `#define _ZAUFE_FE_${i}${suffix}(expandable, empty)\n` } else if (i % nElements == 0) { if (i == nElements) { // Edge case: the last element does not accept any parameters textBuffer += `#define _ZAUFE_FE_${i}${suffix}(expandable, ${X}) expandable(${X})\n` } else { textBuffer += `#define _ZAUFE_FE_${i}${suffix}(expandable, ${X}, ...) expandable(${X}) _ZAUFE_FE_EXPAND(_ZAUFE_FE_${iM1Translated}${suffix}(expandable, __VA_ARGS__))\n` } } else { ignoreList[`_ZAUFE_FE_${i}${suffix}`] = true } var I2 = i + 1 // Similar to above if (i < nElements) { ignoreList[`_ZAUFE_FE_${i}_THAT${suffix}`] = true ignoreList[`_ZAUFE_FE_${I2}_THAT${suffix}`] = true } else if (i == nElements) { ignoreList[`_ZAUFE_FE_${i}_THAT${suffix}`] = true textBuffer += `#define _ZAUFE_FE_${I2}_THAT${suffix}(expandable, ctx, ${X}) expandable(ctx, ${X})\n` } else if ((i % nElements) == 0) { var offByOne = iM1Translated + 1 if (i == nElements) { // Edge case: the last element does not accept any parameters ignoreList[`_ZAUFE_FE_${I2}_THAT${suffix}`] = true // ? dont question it textBuffer += `#define _ZAUFE_FE_${I2}_THAT${suffix}(expandable, ctx, ${X}) expandable(ctx, ${X})\n` } else { textBuffer += `#define _ZAUFE_FE_${I2}_THAT${suffix}(expandable, ctx, ${X}, ...) expandable(ctx, ${X}) _ZAUFE_FE_EXPAND(_ZAUFE_FE_${offByOne}_THAT${suffix}(expandable, ctx, __VA_ARGS__))\n` } } else { ignoreList[`_ZAUFE_FE_${I2}_THAT${suffix}`] = true } ignoreList[`_ZAUFE_FE_${suffix}_THAT${suffix}`] = true if (I2 == nElements) { textBuffer += `#define _ZAUFE_FE_${I2}_FIRST${suffix}(first, second, ...) \n` textBuffer += `#define _ZAUFE_FE_${i}_FIRST${suffix}(first, second) \n` } else if (i == nElements) { textBuffer += `#define _ZAUFE_FE_${I2}_FIRST${suffix}(first, second, ${X}) first(${X}) _ZAUFE_FE_EXPAND(_ZAUFE_FE_${iM1Translated}${suffix}(second))\n` } else if ((i % (nElements) == 0)) { if (i == nElements) { ignoreList[`_ZAUFE_FE_${I2}_FIRST${suffix}`] = true // ? dont question it textBuffer += `#define _ZAUFE_FE_${I2}_FIRST${suffix}(first, second, ${X}) first(${X}) _ZAUFE_FE_EXPAND(_ZAUFE_FE_${iM1Translated}${suffix}(second))\n` } else { textBuffer += `#define _ZAUFE_FE_${I2}_FIRST${suffix}(first, second, ${X}, ...) first(${X}) _ZAUFE_FE_EXPAND(_ZAUFE_FE_${iM1Translated}${suffix}(second, __VA_ARGS__))\n` } } else { ignoreList[`_ZAUFE_FE_${I2}_FIRST${suffix}`] = true } }) // Defines a macro that effectively choses a macro given an array of macros based on the number of input arguments var getMacroParams = formatNParamPattern(nElementsMulNCapped, "_") textBuffer += `#define _ZAUFE_GET_MACRO${suffix}(${getMacroParams}, NAME,...) NAME\n` // Stringify the parameter portion of the prototype var params = formatNParamPatternReverseSuffix(nElementsMulNCapped, "_ZAUFE_FE_", suffix) var params2 = formatNParamPatternReverseSuffix(nElementsMulNCapped, "_ZAUFE_FE_", "_FIRST" + suffix) var params3 = formatNParamPatternReverseSuffix(nElementsMulNCapped, "_ZAUFE_FE_", "_THAT" + suffix) // Replace missing macro paramaters with the _ZAUFE_FE_ERROR macro params = params .split(", ").map((str) => { return ignoreList[str] ? "_ZAUFE_FE_ERROR" : str}).join(", ") params2 = params2.split(", ").map((str) => { return ignoreList[str] ? "_ZAUFE_FE_ERROR" : str}).join(", ") params3 = params3.split(", ").map((str) => { return ignoreList[str] ? "_ZAUFE_FE_ERROR" : str}).join(", ") textBuffer += `#define AU_FOR_EACH${suffix}(action, ...) _ZAUFE_FE_EXPAND(_ZAUFE_GET_MACRO${suffix}(_whydoweneedthis, __VA_ARGS__, ${params})(action,__VA_ARGS__))\n` textBuffer += `#define AU_FOR_EACH_FIRST${suffix}(action, ...) _ZAUFE_FE_EXPAND(_ZAUFE_GET_MACRO${suffix}(_whydoweneedthis, __VA_ARGS__, ${params2})(action,__VA_ARGS__))\n` textBuffer += `#define AU_FOR_EACH_THAT${suffix}(action, ...) _ZAUFE_FE_EXPAND(_ZAUFE_GET_MACRO${suffix}(_whydoweneedthis, __VA_ARGS__, ${params3})(action,__VA_ARGS__))\n` textBuffer += "\n\n" }) fs.writeFileSync("./Include/AuroraForEach.hpp", textBuffer)