Add support for variables (pulling from previous commands in same message)
This commit is contained in:
		
							parent
							
								
									d142b35522
								
							
						
					
					
						commit
						b7e58f56a5
					
				| 
						 | 
					@ -41,7 +41,7 @@ export const runCmd = (fullCmd: string, modifiers: RollModifiers): SolvedRoll =>
 | 
				
			||||||
    assertPrePostBalance(sepCmds);
 | 
					    assertPrePostBalance(sepCmds);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Send the split roll into the command tokenizer to get raw response data
 | 
					    // Send the split roll into the command tokenizer to get raw response data
 | 
				
			||||||
    const [tempReturnData, tempCountDetails] = tokenizeCmd(sepCmds, modifiers, true);
 | 
					    const [tempReturnData, tempCountDetails] = tokenizeCmd(sepCmds, modifiers, true, []);
 | 
				
			||||||
    loggingEnabled && log(LT.LOG, `Return data is back ${JSON.stringify(tempReturnData)}`);
 | 
					    loggingEnabled && log(LT.LOG, `Return data is back ${JSON.stringify(tempReturnData)}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Remove any floating spaces from fullCmd
 | 
					    // Remove any floating spaces from fullCmd
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -15,7 +15,7 @@ import { loggingEnabled } from 'artigen/utils/logFlag.ts';
 | 
				
			||||||
import { getMatchingInternalIdx, getMatchingPostfixIdx } from 'artigen/utils/parenBalance.ts';
 | 
					import { getMatchingInternalIdx, getMatchingPostfixIdx } from 'artigen/utils/parenBalance.ts';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// tokenizeCmd expects a string[] of items that are either config.prefix/config.postfix or some text that contains math and/or dice rolls
 | 
					// tokenizeCmd expects a string[] of items that are either config.prefix/config.postfix or some text that contains math and/or dice rolls
 | 
				
			||||||
export const tokenizeCmd = (cmd: string[], modifiers: RollModifiers, topLevel: boolean): [ReturnData[], CountDetails[]] => {
 | 
					export const tokenizeCmd = (cmd: string[], modifiers: RollModifiers, topLevel: boolean, previousResults: number[]): [ReturnData[], CountDetails[]] => {
 | 
				
			||||||
  loggingEnabled && log(LT.LOG, `Tokenizing command ${JSON.stringify(cmd)}`);
 | 
					  loggingEnabled && log(LT.LOG, `Tokenizing command ${JSON.stringify(cmd)}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const returnData: ReturnData[] = [];
 | 
					  const returnData: ReturnData[] = [];
 | 
				
			||||||
| 
						 | 
					@ -28,8 +28,15 @@ export const tokenizeCmd = (cmd: string[], modifiers: RollModifiers, topLevel: b
 | 
				
			||||||
    const openIdx = cmd.indexOf(config.prefix);
 | 
					    const openIdx = cmd.indexOf(config.prefix);
 | 
				
			||||||
    const closeIdx = getMatchingPostfixIdx(cmd, openIdx);
 | 
					    const closeIdx = getMatchingPostfixIdx(cmd, openIdx);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    loggingEnabled && log(LT.LOG, `Setting previous results: topLevel:${topLevel} ${topLevel ? returnData.map((rd) => rd.rollTotal) : previousResults}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Handle any nested commands
 | 
					    // Handle any nested commands
 | 
				
			||||||
    const [tempData, tempCounts] = tokenizeCmd(cmd.slice(openIdx + 1, closeIdx), modifiers, false);
 | 
					    const [tempData, tempCounts] = tokenizeCmd(
 | 
				
			||||||
 | 
					      cmd.slice(openIdx + 1, closeIdx),
 | 
				
			||||||
 | 
					      modifiers,
 | 
				
			||||||
 | 
					      false,
 | 
				
			||||||
 | 
					      topLevel ? returnData.map((rd) => rd.rollTotal) : previousResults,
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
    const data = tempData[0];
 | 
					    const data = tempData[0];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (topLevel) {
 | 
					    if (topLevel) {
 | 
				
			||||||
| 
						 | 
					@ -60,7 +67,7 @@ export const tokenizeCmd = (cmd: string[], modifiers: RollModifiers, topLevel: b
 | 
				
			||||||
    loggingEnabled && log(LT.LOG, `Tokenizing math ${JSON.stringify(cmd)}`);
 | 
					    loggingEnabled && log(LT.LOG, `Tokenizing math ${JSON.stringify(cmd)}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Solve the math and rolls for this cmd
 | 
					    // Solve the math and rolls for this cmd
 | 
				
			||||||
    const [tempData, tempCounts] = tokenizeMath(cmd.join(''), modifiers);
 | 
					    const [tempData, tempCounts] = tokenizeMath(cmd.join(''), modifiers, previousResults);
 | 
				
			||||||
    const data = tempData[0];
 | 
					    const data = tempData[0];
 | 
				
			||||||
    loggingEnabled && log(LT.LOG, `Solved math is back ${JSON.stringify(data)} | ${JSON.stringify(returnData)}`);
 | 
					    loggingEnabled && log(LT.LOG, `Solved math is back ${JSON.stringify(data)} | ${JSON.stringify(returnData)}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -70,6 +70,21 @@ export const mathSolver = (conf: MathConf[], wrapDetails = false): SolvedStep =>
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Look for any implicit multiplication that may have been missed
 | 
				
			||||||
 | 
					  // Start at index 1 as there will never be implicit multiplication before the first element
 | 
				
			||||||
 | 
					  for (let i = 1; i < conf.length; i++) {
 | 
				
			||||||
 | 
					    loopCountCheck();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const prevConfAsStr = <string> conf[i - 1];
 | 
				
			||||||
 | 
					    const curConfAsStr = <string> conf[i];
 | 
				
			||||||
 | 
					    if (!signs.includes(curConfAsStr) && !signs.includes(prevConfAsStr)) {
 | 
				
			||||||
 | 
					      // Both previous and current conf are operators, slip in the "*"
 | 
				
			||||||
 | 
					      conf.splice(i, 0, '*');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // At this point, conf should be [num, op, num, op, num, op, num, etc]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Evaluate all EMDAS by looping thru each tier of operators (exponential is the highest tier, addition/subtraction the lowest)
 | 
					  // Evaluate all EMDAS by looping thru each tier of operators (exponential is the highest tier, addition/subtraction the lowest)
 | 
				
			||||||
  const allCurOps = [['^'], ['*', '/', '%'], ['+', '-']];
 | 
					  const allCurOps = [['^'], ['*', '/', '%'], ['+', '-']];
 | 
				
			||||||
  allCurOps.forEach((curOps) => {
 | 
					  allCurOps.forEach((curOps) => {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,17 +18,17 @@ import { assertParenBalance } from 'artigen/utils/parenBalance.ts';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const operators = ['(', ')', '^', '*', '/', '%', '+', '-'];
 | 
					const operators = ['(', ')', '^', '*', '/', '%', '+', '-'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const tokenizeMath = (cmd: string, modifiers: RollModifiers): [ReturnData[], CountDetails[]] => {
 | 
					export const tokenizeMath = (cmd: string, modifiers: RollModifiers, previousResults: number[]): [ReturnData[], CountDetails[]] => {
 | 
				
			||||||
  const countDetails: CountDetails[] = [];
 | 
					  const countDetails: CountDetails[] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  loggingEnabled && log(LT.LOG, `Parsing roll ${cmd}`);
 | 
					  loggingEnabled && log(LT.LOG, `Parsing roll ${cmd} | ${JSON.stringify(modifiers)} | ${JSON.stringify(previousResults)}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Remove all spaces from the operation config and split it by any operator (keeping the operator in mathConf for fullSolver to do math on)
 | 
					  // Remove all spaces from the operation config and split it by any operator (keeping the operator in mathConf for fullSolver to do math on)
 | 
				
			||||||
  const mathConf: MathConf[] = cmd
 | 
					  const mathConf: MathConf[] = cmd
 | 
				
			||||||
    .replace(cmdSplitRegex, '')
 | 
					    .replace(cmdSplitRegex, '')
 | 
				
			||||||
    .replace(internalWrapRegex, '')
 | 
					    .replace(internalWrapRegex, '')
 | 
				
			||||||
    .replace(/ /g, '')
 | 
					    .replace(/ /g, '')
 | 
				
			||||||
    .split(/([-+()*/^]|(?<![d%])%)/g)
 | 
					    .split(/([-+()*/^]|(?<![d%])%)|(x\d+(\.\d*)?)/g)
 | 
				
			||||||
    .filter((x) => x);
 | 
					    .filter((x) => x);
 | 
				
			||||||
  loggingEnabled && log(LT.LOG, `Split roll into mathConf ${JSON.stringify(mathConf)}`);
 | 
					  loggingEnabled && log(LT.LOG, `Split roll into mathConf ${JSON.stringify(mathConf)}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -41,16 +41,16 @@ export const tokenizeMath = (cmd: string, modifiers: RollModifiers): [ReturnData
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    loggingEnabled && log(LT.LOG, `Parsing roll ${JSON.stringify(cmd)} | Evaluating rolls into math-able items ${JSON.stringify(mathConf[i])}`);
 | 
					    loggingEnabled && log(LT.LOG, `Parsing roll ${JSON.stringify(cmd)} | Evaluating rolls into math-able items ${JSON.stringify(mathConf[i])}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const strMathConfI = mathConf[i].toString();
 | 
					    const curMathConfStr = mathConf[i].toString();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (strMathConfI.length === 0) {
 | 
					    if (curMathConfStr.length === 0) {
 | 
				
			||||||
      // If its an empty string, get it out of here
 | 
					      // If its an empty string, get it out of here
 | 
				
			||||||
      mathConf.splice(i, 1);
 | 
					      mathConf.splice(i, 1);
 | 
				
			||||||
      i--;
 | 
					      i--;
 | 
				
			||||||
    } else if (mathConf[i] == parseFloat(strMathConfI)) {
 | 
					    } else if (mathConf[i] == parseFloat(curMathConfStr)) {
 | 
				
			||||||
      // If its a number, parse the number out
 | 
					      // If its a number, parse the number out
 | 
				
			||||||
      mathConf[i] = parseFloat(strMathConfI);
 | 
					      mathConf[i] = parseFloat(curMathConfStr);
 | 
				
			||||||
    } else if (strMathConfI.toLowerCase() === 'e') {
 | 
					    } else if (curMathConfStr.toLowerCase() === 'e') {
 | 
				
			||||||
      // If the operand is the constant e, create a SolvedStep for it
 | 
					      // If the operand is the constant e, create a SolvedStep for it
 | 
				
			||||||
      mathConf[i] = {
 | 
					      mathConf[i] = {
 | 
				
			||||||
        total: Math.E,
 | 
					        total: Math.E,
 | 
				
			||||||
| 
						 | 
					@ -58,21 +58,21 @@ export const tokenizeMath = (cmd: string, modifiers: RollModifiers): [ReturnData
 | 
				
			||||||
        containsCrit: false,
 | 
					        containsCrit: false,
 | 
				
			||||||
        containsFail: false,
 | 
					        containsFail: false,
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
    } else if (strMathConfI.toLowerCase() === 'fart' || strMathConfI.toLowerCase() === '💩') {
 | 
					    } else if (curMathConfStr.toLowerCase() === 'fart' || curMathConfStr.toLowerCase() === '💩') {
 | 
				
			||||||
      mathConf[i] = {
 | 
					      mathConf[i] = {
 | 
				
			||||||
        total: 7,
 | 
					        total: 7,
 | 
				
			||||||
        details: '💩',
 | 
					        details: '💩',
 | 
				
			||||||
        containsCrit: false,
 | 
					        containsCrit: false,
 | 
				
			||||||
        containsFail: false,
 | 
					        containsFail: false,
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
    } else if (strMathConfI.toLowerCase() === 'sex') {
 | 
					    } else if (curMathConfStr.toLowerCase() === 'sex') {
 | 
				
			||||||
      mathConf[i] = {
 | 
					      mathConf[i] = {
 | 
				
			||||||
        total: 69,
 | 
					        total: 69,
 | 
				
			||||||
        details: '( ͡° ͜ʖ ͡°)',
 | 
					        details: '( ͡° ͜ʖ ͡°)',
 | 
				
			||||||
        containsCrit: false,
 | 
					        containsCrit: false,
 | 
				
			||||||
        containsFail: false,
 | 
					        containsFail: false,
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
    } else if (strMathConfI.toLowerCase() === 'inf' || strMathConfI.toLowerCase() === 'infinity' || strMathConfI.toLowerCase() === '∞') {
 | 
					    } else if (curMathConfStr.toLowerCase() === 'inf' || curMathConfStr.toLowerCase() === 'infinity' || curMathConfStr.toLowerCase() === '∞') {
 | 
				
			||||||
      // If the operand is the constant Infinity, create a SolvedStep for it
 | 
					      // If the operand is the constant Infinity, create a SolvedStep for it
 | 
				
			||||||
      mathConf[i] = {
 | 
					      mathConf[i] = {
 | 
				
			||||||
        total: Infinity,
 | 
					        total: Infinity,
 | 
				
			||||||
| 
						 | 
					@ -80,7 +80,7 @@ export const tokenizeMath = (cmd: string, modifiers: RollModifiers): [ReturnData
 | 
				
			||||||
        containsCrit: false,
 | 
					        containsCrit: false,
 | 
				
			||||||
        containsFail: false,
 | 
					        containsFail: false,
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
    } else if (strMathConfI.toLowerCase() === 'pi' || strMathConfI.toLowerCase() === '𝜋') {
 | 
					    } else if (curMathConfStr.toLowerCase() === 'pi' || curMathConfStr.toLowerCase() === '𝜋') {
 | 
				
			||||||
      // If the operand is the constant pi, create a SolvedStep for it
 | 
					      // If the operand is the constant pi, create a SolvedStep for it
 | 
				
			||||||
      mathConf[i] = {
 | 
					      mathConf[i] = {
 | 
				
			||||||
        total: Math.PI,
 | 
					        total: Math.PI,
 | 
				
			||||||
| 
						 | 
					@ -88,7 +88,7 @@ export const tokenizeMath = (cmd: string, modifiers: RollModifiers): [ReturnData
 | 
				
			||||||
        containsCrit: false,
 | 
					        containsCrit: false,
 | 
				
			||||||
        containsFail: false,
 | 
					        containsFail: false,
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
    } else if (strMathConfI.toLowerCase() === 'pie') {
 | 
					    } else if (curMathConfStr.toLowerCase() === 'pie') {
 | 
				
			||||||
      // If the operand is pie, pi*e, create a SolvedStep for e and pi (and the multiplication symbol between them)
 | 
					      // If the operand is pie, pi*e, create a SolvedStep for e and pi (and the multiplication symbol between them)
 | 
				
			||||||
      mathConf[i] = {
 | 
					      mathConf[i] = {
 | 
				
			||||||
        total: Math.PI,
 | 
					        total: Math.PI,
 | 
				
			||||||
| 
						 | 
					@ -110,16 +110,31 @@ export const tokenizeMath = (cmd: string, modifiers: RollModifiers): [ReturnData
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
      i += 2;
 | 
					      i += 2;
 | 
				
			||||||
    } else if (!legalMathOperators.includes(strMathConfI) && legalMathOperators.some((mathOp) => strMathConfI.endsWith(mathOp))) {
 | 
					    } else if (!legalMathOperators.includes(curMathConfStr) && legalMathOperators.some((mathOp) => curMathConfStr.endsWith(mathOp))) {
 | 
				
			||||||
      // Identify when someone does something weird like 4floor(2.5) and split 4 and floor
 | 
					      // Identify when someone does something weird like 4floor(2.5) and split 4 and floor
 | 
				
			||||||
      const matchedMathOp = legalMathOperators.filter((mathOp) => strMathConfI.endsWith(mathOp))[0];
 | 
					      const matchedMathOp = legalMathOperators.filter((mathOp) => curMathConfStr.endsWith(mathOp))[0];
 | 
				
			||||||
      mathConf[i] = parseFloat(strMathConfI.replace(matchedMathOp, ''));
 | 
					      mathConf[i] = parseFloat(curMathConfStr.replace(matchedMathOp, ''));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      mathConf.splice(i + 1, 0, ...['*', matchedMathOp]);
 | 
					      mathConf.splice(i + 1, 0, ...['*', matchedMathOp]);
 | 
				
			||||||
      i += 2;
 | 
					      i += 2;
 | 
				
			||||||
    } else if (![...operators, ...legalMathOperators].includes(strMathConfI)) {
 | 
					    } else if (/(x\d+(\.\d*)?)/.test(curMathConfStr)) {
 | 
				
			||||||
 | 
					      // Identify when someone is using a variable from previous commands
 | 
				
			||||||
 | 
					      if (curMathConfStr.includes('.')) {
 | 
				
			||||||
 | 
					        // Verify someone did not enter x1.1 as a variable
 | 
				
			||||||
 | 
					        throw new Error(`IllegalVariable_${curMathConfStr}`);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const varIdx = parseInt(curMathConfStr.replaceAll('x', ''));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // Get the index from the variable and attempt to use it to query the previousResults
 | 
				
			||||||
 | 
					      if (previousResults.length > varIdx) {
 | 
				
			||||||
 | 
					        mathConf[i] = parseFloat(previousResults[varIdx].toString());
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        throw new Error(`IllegalVariable_${curMathConfStr}`);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } else if (![...operators, ...legalMathOperators].includes(curMathConfStr)) {
 | 
				
			||||||
      // If nothing else has handled it by now, try it as a roll
 | 
					      // If nothing else has handled it by now, try it as a roll
 | 
				
			||||||
      const formattedRoll = generateFormattedRoll(strMathConfI, modifiers);
 | 
					      const formattedRoll = generateFormattedRoll(curMathConfStr, modifiers);
 | 
				
			||||||
      mathConf[i] = formattedRoll.solvedStep;
 | 
					      mathConf[i] = formattedRoll.solvedStep;
 | 
				
			||||||
      countDetails.push(formattedRoll.countDetails);
 | 
					      countDetails.push(formattedRoll.countDetails);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -104,6 +104,9 @@ export const translateError = (solverError: Error): [string, string] => {
 | 
				
			||||||
    case 'UndefinedStep':
 | 
					    case 'UndefinedStep':
 | 
				
			||||||
      errorMsg = 'Error: Roll became undefined, one or more operands are not a roll or a number, check input';
 | 
					      errorMsg = 'Error: Roll became undefined, one or more operands are not a roll or a number, check input';
 | 
				
			||||||
      break;
 | 
					      break;
 | 
				
			||||||
 | 
					    case 'IllegalVariable':
 | 
				
			||||||
 | 
					      errorMsg = `Error: \`${errorDetails}\` is not a valid variable`;
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
    default:
 | 
					    default:
 | 
				
			||||||
      log(LT.ERROR, `Unhandled Parser Error: ${errorName}, ${errorDetails}`);
 | 
					      log(LT.ERROR, `Unhandled Parser Error: ${errorName}, ${errorDetails}`);
 | 
				
			||||||
      errorMsg = `Unhandled Error: ${solverError.message}\nCheck input and try again, if issue persists, please use \`${config.prefix}report\` to alert the devs of the issue`;
 | 
					      errorMsg = `Unhandled Error: ${solverError.message}\nCheck input and try again, if issue persists, please use \`${config.prefix}report\` to alert the devs of the issue`;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue