UNPKG

@haystacks/async

Version:

A framework to build any number or any kind of native application or automation solution.

370 lines (356 loc) 23.7 kB
/** * @file commandArrayParsing.js * @module commandArrayParsing * @description Contains all system defined business rules for parsing arrays specific to commands. * @requires module:ruleParsing * @requires module:configurator * @requires module:loggers * @requires {@link https://www.npmjs.com/package/@haystacks/constants|@haystacks/constants} * @requires {@link https://www.npmjs.com/package/path|path} * @author Seth Hollingsead * @date 2022/04/26 * @copyright Copyright © 2022-… by Seth Hollingsead. All rights reserved */ // Internal imports import ruleParsing from '../ruleParsing.js'; import configurator from '../../../executrix/configurator.js'; import loggers from '../../../executrix/loggers.js'; // External imports import hayConst from '@haystacks/constants'; import path from 'path'; const {bas, biz, cfg, msg, sys, wrd} = hayConst; const baseFileName = path.basename(import.meta.url, path.extname(import.meta.url)); // framework.businessRules.rules.arrayParsing.commandArrayParsing. const namespacePrefix = wrd.cframework + bas.cDot + sys.cbusinessRules + bas.cDot + wrd.crules + bas.cDot + wrd.carray + wrd.cParsing + bas.cDot + baseFileName + bas.cDot; /** * @function solveLehmerCode * @description Used the inputData as an addressable Lehmer Code to find all possible combinations of array elements. * @param {array<integer>} inputData The Lehmer code addressable index array we will use to permutate over all possible combinations. * @param {array<array<string>>} inputMetaData The nested array that contains all instances of strings that should be used when generating permutations. * @return {string} The delimited list of possible combinations generated by solving the Lehmer Code. * @author Seth Hollingsead * @date 2022/01/20 * @NOTE: https://en.wikipedia.org/wiki/Lehmer_code */ async function solveLehmerCode(inputData, inputMetaData) { let functionName = solveLehmerCode.name; await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function); await loggers.consoleLog(namespacePrefix + functionName, msg.cinputDataIs + JSON.stringify(inputData)); await loggers.consoleLog(namespacePrefix + functionName, msg.cinputMetaDataIs + JSON.stringify(inputMetaData)); let returnData = ''; if (inputData) { // [["Wondr","Wundr","Wndr","Wonder"],["Wman","Wmn","Womn","Woman"],["Amzing","Amzng","Amazing"]] // [3,3,2] // // { // "wonder": "wondr,wundr,wndr", // "Woman": "wman,wmn,womn", // "Amazing": "amzing,amzng" // } let lengthOfInputData = inputData.length; let expandedLehmerCodeArray = []; let lehmerCodeArray = await Array.from(await Array(lengthOfInputData), () => 0); let expandedArray = await recursiveArrayExpansion([0, lehmerCodeArray], inputData); // expandedArray is: await loggers.consoleLog(namespacePrefix + functionName, msg.cexpandedArrayIs + JSON.stringify(expandedArray)); expandedLehmerCodeArray = await ruleParsing.processRulesInternal([expandedArray, ''], [biz.carrayDeepClone]); // expandedLehmerCodeArray is: await loggers.consoleLog(namespacePrefix + functionName, msg.cexpandedLehmerCodeArrayIs + JSON.stringify(expandedLehmerCodeArray)); // Now we just iterate over each array in expandedLehmerCodeArray and call: getLehmerCodeValue for (let i = 0; i < expandedLehmerCodeArray.length - 1; i++) { let lehmerCodeStringValue = await getLehmerCodeValue(expandedLehmerCodeArray[i], inputMetaData); if (i === 0) { returnData = returnData + lehmerCodeStringValue; } else { returnData = returnData + bas.cComa + lehmerCodeStringValue; } } // End-for (let i = 0; i < expandedLehmerCodeArray.length - 1; i++) } // End-if (inputData) await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + returnData); await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function); return returnData; } /** * @function recursiveArrayExpansion * @description Recursively expands all possible combinations of an input array given an index of expansion and returns the list of arrays. * @param {array<integer,array<integer>>} inputData The index of expansion and the array to be expanded as an array object. * @param {array<integer>} inputMetaData The Lehmer Codex that should be used to set the limit of expansion based on the index of expansion. * @return {array<array<integer>>} The final list of arrays after the array expansion has completed successfully. * @author Seth Hollingsead * @date 2022/01/20 */ async function recursiveArrayExpansion(inputData, inputMetaData) { let functionName = recursiveArrayExpansion.name; await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function); await loggers.consoleLog(namespacePrefix + functionName, msg.cinputDataIs + JSON.stringify(inputData)); await loggers.consoleLog(namespacePrefix + functionName, msg.cinputMetaDataIs + JSON.stringify(inputMetaData)); let returnData = []; let inputDataIsArray = await ruleParsing.processRulesInternal([inputData, ''], [biz.cisArray]); let inputMetaDataIsArray = await ruleParsing.processRulesInternal([inputMetaData, ''], [biz.cisArray]); if (inputData && inputMetaData && inputDataIsArray === true && inputMetaDataIsArray === true && inputData.length > 0 && inputMetaData.length > 0) { // Setup & parse the inputData & inputMetaData into a format we can use to actually do recursive array expansion. let indexOfExpansion = inputData[0]; let arrayToBeExpanded = inputData[1]; let limitOfExpansion = inputMetaData[indexOfExpansion]; // indexOfExpansion is: await loggers.consoleLog(namespacePrefix + functionName, msg.cindexOfExpansionIs + indexOfExpansion); // arrayToBeExpanded is: await loggers.consoleLog(namespacePrefix + functionName, msg.carrayToBeExpandedIs + JSON.stringify(arrayToBeExpanded)); // limitOfExpansion is: await loggers.consoleLog(namespacePrefix + functionName, msg.climitOfExpansionIs + limitOfExpansion); let masterTempReturnData = []; // When we are all done we will set the returnData back to the list of arays in this array. // [["Wondr","Wundr","Wndr","Wonder"],["Wman","Wmn","Womn","Woman"],["Amzing","Amzng","Amazing"]] // [3,3,2] // // { // "wonder": "wondr,wundr,wndr", // "Woman": "wman,wmn,womn", // "Amazing": "amzing,amzng" // } // First level array expansion. for (let i = 0; i <= limitOfExpansion; i++) { let lehmerCodeArray1 = await ruleParsing.processRulesInternal([arrayToBeExpanded, ''], [biz.carrayDeepClone]); // returnData is: await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + JSON.stringify(returnData)); lehmerCodeArray1[indexOfExpansion] = i; if (await ruleParsing.processRulesInternal([[returnData, lehmerCodeArray1], ''], [biz.cdoesArrayContainValue]) === false) { // pushing LehmerCodeArray1 to returnData value: await loggers.consoleLog(namespacePrefix + functionName, msg.cpushingLehmerCodeArray1ToReturnDataValue + JSON.stringify(lehmerCodeArray1)); returnData.push(lehmerCodeArray1); // returnData after push is: await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataAfterPushIs + JSON.stringify(returnData)); } // End-if (await ruleParsing.processRulesInternal([[returnData, lehmerCodeArray1], ''], [biz.cdoesArrayContainValue]) === false) } // End-for (let i = 0; i <= limitOfExpansion; i++) // returnData after level 1 is: await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataAfterLevel1Is + JSON.stringify(returnData)); // Second level array expansion, this is where we call recursively. // We need to determine if the index of expansion is equal to the length of the arrayToBeExpanded. // If it is then we have reached our recursive expansion limit. // If NOT then we need to recursively expand some more on each of the arrays that are now in the returnData array. // arrayToBeExpanded.length is: await loggers.consoleLog(namespacePrefix + functionName, msg.carrayToBeExpandedDotLengthIs + arrayToBeExpanded.length); if (indexOfExpansion < arrayToBeExpanded.length - 1) { // We need to remove arrays from the returnData and recursively call the recursiveArrayExpansion with each array we remove. // The data we get back from each recursive call should be pushed back to masterTempReturnData array. // returnData.length is: await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataDotLengthIs + returnData.length); // Make sure we clone the array we will be removing array elements from, // because otherwise we would be looping over the same array we are removing elements from, // which would mean that we would never visit all of the elements. // https://stackoverflow.com/questions/54081930/why-array-foreach-array-pop-would-not-empty-the-array let returnDataTemp = await ruleParsing.processRulesInternal([returnData, ''], [biz.carrayDeepClone]); // returnDataTemp is: await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataTempIs + JSON.stringify(returnDataTemp)); // for (let registryPlugin in pluginRegistryList) { // await returnDataTemp.forEach(async function() { for (let arrayElement in returnDataTemp) { // arrayElement is: await loggers.consoleLog(namespacePrefix + functionName, msg.carrayElementIs + JSON.stringify(arrayElement)); // returnData BEFORE POP is: await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataBeforePopIs + JSON.stringify(returnData)); let lehmerCodeArray2 = await ruleParsing.processRulesInternal([await returnData.pop(), ''], [biz.carrayDeepClone]); // returnData AFTER POP is: await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataAfterPopIs + JSON.stringify(returnData)); // masterTempReturnData BEFORE recursive call is: await loggers.consoleLog(namespacePrefix + functionName, msg.cmasterTempReturnDataBeforeRecursiveCallIs + JSON.stringify(masterTempReturnData)); let expandedArray = await recursiveArrayExpansion([indexOfExpansion + 1, lehmerCodeArray2], inputMetaData); let tempReturnData1 = await ruleParsing.processRulesInternal([expandedArray, ''], [biz.carrayDeepClone]); // tempReturnData1 is: await loggers.consoleLog(namespacePrefix + functionName, msg.ctempReturnData1Is + JSON.stringify(await tempReturnData1)); // tempReturnData1.length is: await loggers.consoleLog(namespacePrefix + functionName, msg.ctempReturnData1DotLengthIs + tempReturnData1.length); for (let k = 0; k <= tempReturnData1.length - 1; k++) { // BEGIN k-th iteration: await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_kthIteration + k); if (await ruleParsing.processRulesInternal([[masterTempReturnData, tempReturnData1[k]], ''], [biz.cdoesArrayContainValue]) === false) { // pushing tempReturnData1[k] value: await loggers.consoleLog(namespacePrefix + functionName, msg.cpushingTempReturnData1Kvalue + JSON.stringify(await tempReturnData1[k])); await masterTempReturnData.push(await ruleParsing.processRulesInternal([tempReturnData1[k], ''], [biz.carrayDeepClone])); } // End-if (await ruleParsing.processRulesInternal([[masterTempReturnData, tempReturnData1[k]], ''], [biz.cdoesArrayContainValue]) === false) // END k-th iteration: await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_kthIteration + k); } // End-for (let k = 0; k <= tempReturnData1.length - 1; k++) // masterTempReturnData AFTER recursive call is: await loggers.consoleLog(namespacePrefix + functionName, msg.cmasterTempReturnDataAfterRecursiveCallIs + JSON.stringify(masterTempReturnData)); } // End-for-each (returnDataTemp.forEach(function()) returnData = await ruleParsing.processRulesInternal([masterTempReturnData, ''], [biz.carrayDeepClone]); } // End-if (indexOfExpansion < arrayToBeExpanded.length - 1) } // End-if (inputData && inputMetaData && inputDataIsArray === true && inputMetaDataIsArray === true && inputData.length > 0 && inputMetaData.length > 0) await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + JSON.stringify(await returnData)); await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function); return await returnData; } /** * @function getLehmerCodeValue * @description Takes a Lehmer code array and an array of arrays and uses the Lehmer Code array to look up the corresponding values in the array of arrays. * @param {array<integer>} inputData The Lehmer code array with indices for values we should get & return. * @param {array<array<string>>} inputMetaData The nested array of arrays with the values we should get and combine then return as a single string. * @return {string} The joined string from each of the array element strings at the Lehmer code indices. * @author Seth Hollingsead * @date 2022/01/20 */ async function getLehmerCodeValue(inputData, inputMetaData) { let functionName = getLehmerCodeValue.name; await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function); await loggers.consoleLog(namespacePrefix + functionName, msg.cinputDataIs + JSON.stringify(inputData)); await loggers.consoleLog(namespacePrefix + functionName, msg.cinputMetaDataIs + JSON.stringify(inputMetaData)); let returnData = ''; if (inputData) { let lengthOfInputData = inputData.length; for (let i = 0; i < lengthOfInputData; i++) { // BEGIN i-th iteration: await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_ithIteration + i); let lookupIndex = inputData[i]; // lookupIndex is: await loggers.consoleLog(namespacePrefix + functionName, msg.clookupIndexIs + lookupIndex); let lookupValue = inputMetaData[i][lookupIndex]; // lookupValue is: await loggers.consoleLog(namespacePrefix + functionName, msg.clookupValueIs + lookupValue); returnData = returnData + lookupValue; // returnData is: await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + returnData); // END i-th iteration: await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_ithIteration + i); } // End-for (let i = 0; i < lengthOfInputData; i++) } // End-if (inputData) await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + returnData); await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function); return returnData; } /** * @function generateCommandAliases * @description Generates all possible combinations of command aliases given a set of command words and command word abbreviations. * @param {object} inputData An object containing all of the meta-data needed for command words and * command word abbreviations needed to generate every possible combination of command aliases. * @param {string} inputMetaData Not used for this business rule. * @return {string} A coma-separated list of every possible combination of command aliases. * @author Seth Hollingsead * @date 2022/01/21 */ async function generateCommandAliases(inputData, inputMetaData) { let functionName = generateCommandAliases.name; await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function); await loggers.consoleLog(namespacePrefix + functionName, msg.cinputDataIs + JSON.stringify(inputData)); await loggers.consoleLog(namespacePrefix + functionName, msg.cinputMetaDataIs + inputMetaData); let returnData = false; if (inputData) { // {"wonder":"wondr,wundr,wndr","Woman":"wman,wmn,womn","Amazing":"amzing,amzng"} // // { // "wonder": "wondr,wundr,wndr", // "Woman": "wman,wmn,womn", // "Amazing": "amzing,amzng" // } let primaryCommandDelimiter = await configurator.getConfigurationSetting(wrd.csystem, cfg.cprimaryCommandDelimiter); await loggers.consoleLog(namespacePrefix + functionName, msg.cprimaryCommandDelimiterIs + primaryCommandDelimiter); let secondaryCommandDelimiter = await configurator.getConfigurationSetting(wrd.csystem, cfg.csecondaryCommandDelimiter); await loggers.consoleLog(namespacePrefix + functionName, msg.csecondaryCommandDelimiterIs + secondaryCommandDelimiter); let tertiaryCommandDelimiter = await configurator.getConfigurationSetting(wrd.csystem, cfg.ctertiaryCommandDelimiter); await loggers.consoleLog(namespacePrefix + functionName, msg.ctertiaryCommandDelimiterIs + tertiaryCommandDelimiter); let commandDelimiter = ''; let commandWordsKeys1 = Object.keys(inputData); let commandWordAliasesArray = []; let masterCommandWordAliasesArray = [commandWordsKeys1.length - 1]; let masterArrayIndex = [commandWordsKeys1.length - 1]; for (let i = 0; i < commandWordsKeys1.length; i++) { // commandWordsKeys1.forEach((key1) => { let key1 = commandWordsKeys1[i]; let commandWordAliases = inputData[key1]; if (commandWordAliases.includes(primaryCommandDelimiter)) { commandDelimiter = primaryCommandDelimiter; } else if (commandWordAliases.includes(secondaryCommandDelimiter)) { commandDelimiter = secondaryCommandDelimiter; } else if (commandWordAliases.includes(tertiaryCommandDelimiter)) { commandDelimiter = tertiaryCommandDelimiter; } commandWordAliases = commandWordAliases + commandDelimiter + key1; // commandWordAliases BEFORE CHANGE is: await loggers.consoleLog(namespacePrefix + functionName, msg.ccommandWordAliasesBeforeChangeIs + commandWordAliases); commandWordAliasesArray = commandWordAliases.split(commandDelimiter); masterArrayIndex[i] = commandWordAliasesArray.length - 1; for (let j = 0; j < commandWordAliasesArray.length; j++) { let commandAliasWord = commandWordAliasesArray[j]; if (await ruleParsing.processRulesInternal([commandAliasWord, ''], [biz.cisFirstCharacterLowerCase]) === true) { let firstLetterOfCommandAliasWord = commandAliasWord.charAt(0).toUpperCase(); commandAliasWord = await ruleParsing.processRulesInternal([[commandAliasWord, 0], firstLetterOfCommandAliasWord], [biz.creplaceCharacterAtIndexOfString]); commandWordAliasesArray[j] = commandAliasWord; // Saved the changes back to array. } // End-if (ruleParsing.processRulesInternal([commandAliasWord, ''], [biz.cisFirstCharacterLowerCase]) === true) } // End-for (let j = 0; j < commandWordAliasesArray.length; j++) // commandWordAliasesArray AFTER CHANGE is: await loggers.consoleLog(namespacePrefix + functionName, msg.ccommandWordAliasesAfterChangeIs + JSON.stringify(commandWordAliasesArray)); masterCommandWordAliasesArray[i] = commandWordAliasesArray; // Try to build an array of arrays (2D) } // End-for (let i = 0; i < commandWordsKeys1.length; i++) // masterCommandWordAliasesArray is: await loggers.consoleLog(namespacePrefix + functionName, msg.cmasterCommandWordAlisesArrayIs + JSON.stringify(masterCommandWordAliasesArray)); // masterArrayIndex is: await loggers.consoleLog(namespacePrefix + functionName, msg.cmasterArrayIndexIs + JSON.stringify(masterArrayIndex)); // NOTES: Console output is: // masterCommandWordAliasesArray is: [["Wondr","Wundr","Wndr","Wonder"],["Wman","Wmn","Womn","Woman"],["Amzing","Amzng","Amazing"]] // masterArrayIndex is: [4,4,3] // // We should be able to have 2 nested for-loops, and we will declare a counter array initialized to [0,0,0] to match the masterArrayIndex above. // The counter array tells us which combination of words we should get. // We can simply push those combination of words as a string on a stack we will make for this business rule. // Then iterate the last array element as long as it's not greater than the number in the master array index and do the same things over again. // When the array index for the last element in the array reaches the masterArrayIndex for the same array index then we increment the second from the last array counter. // and start over again with the last element in the array counter. // This way we should be able to iterate over the entire 2D array and get every combination without having to create x number of nested for-loops. // Essentially we will be having 2-nested for-loops looping over the counter array. The top level loop will be looping over masterArrayIndex.length, // and the second loop will be iterating over the integers in the counter array. // The counter array will tell the algorthim which combination of words to put together and push on the stack. // // NOTE: The algorthim described above is called: Lehmer code // https://en.wikipedia.org/wiki/Lehmer_code returnData = await solveLehmerCode(masterArrayIndex, masterCommandWordAliasesArray); // Command Aliases are: await console.log(msg.cCommandAliasesAre + returnData); } // End-if (inputData) await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + returnData); await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function); return returnData; } /** * @function aggregateCommandArguments * @description Combines all of the input arguments into a single command line to be executed by the command parser. * @param {array<string>} inputData An array of strings that represents the command and command parameters to execute. * @param {string} inputMetaData Not used for this business rule. * @return {string} A sinle string command line of code that should be sent to the command parser. * @author Seth Hollingsead * @date 2022/01/21 */ async function aggregateCommandArguments(inputData, inputMetaData) { let functionName = aggregateCommandArguments.name; await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_Function); await loggers.consoleLog(namespacePrefix + functionName, msg.cinputDataIs + JSON.stringify(inputData)); await loggers.consoleLog(namespacePrefix + functionName, msg.cinputMetaDataIs + inputMetaData); let returnData = ''; if (inputData) { if (inputData.length > 3) { for (let i = 2; i < inputData.length; i++) { // BEGIN i-th iteration: await loggers.consoleLog(namespacePrefix + functionName, msg.cBEGIN_ithIteration + i); if (i === 2) { returnData = await ruleParsing.processRulesInternal([inputData[i], '' ], [biz.ccleanCommandInput]); } else { returnData = returnData + bas.cSpace + await ruleParsing.processRulesInternal([inputData[i], ''], [biz.ccleanCommandInput]); } // returnData is: await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + returnData); // END i-th iteration: await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_ithIteration + i); } // End-for (let i = 2; i < inputData.length; i++) } else { // else-clause if (inputData.length > 3) returnData = await ruleParsing.processRulesInternal([inputData[2], ''], [biz.ccleanCommandInput]); } } // End-if (inputData) await loggers.consoleLog(namespacePrefix + functionName, msg.creturnDataIs + returnData); await loggers.consoleLog(namespacePrefix + functionName, msg.cEND_Function); return returnData; } export default { solveLehmerCode, recursiveArrayExpansion, getLehmerCodeValue, generateCommandAliases, aggregateCommandArguments };