Jofthomas's picture
Jofthomas HF staff
Upload 4781 files
5c2ed06 verified
{
"version": 3,
"sources": ["../../../server/chat-plugins/calculator.ts"],
"sourcesContent": ["import { Utils } from '../../lib';\n\ntype Operator = '^' | 'negative' | '%' | '/' | '*' | '+' | '-' | '(';\ninterface Operators {\n\tprecedence: number;\n\tassociativity: \"Left\" | \"Right\";\n}\n\nconst OPERATORS: { [k in Operator]: Operators } = {\n\t\"^\": {\n\t\tprecedence: 5,\n\t\tassociativity: \"Right\",\n\t},\n\t\"negative\": {\n\t\tprecedence: 4,\n\t\tassociativity: \"Right\",\n\t},\n\t\"%\": {\n\t\tprecedence: 3,\n\t\tassociativity: \"Left\",\n\t},\n\t\"/\": {\n\t\tprecedence: 3,\n\t\tassociativity: \"Left\",\n\t},\n\t\"*\": {\n\t\tprecedence: 3,\n\t\tassociativity: \"Left\",\n\t},\n\t\"+\": {\n\t\tprecedence: 2,\n\t\tassociativity: \"Left\",\n\t},\n\t\"-\": {\n\t\tprecedence: 2,\n\t\tassociativity: \"Left\",\n\t},\n\t\"(\": {\n\t\tprecedence: 1,\n\t\tassociativity: \"Right\",\n\t},\n};\n\nconst BASE_PREFIXES: { [base: number]: string } = {\n\t2: \"0b\",\n\t8: \"0o\",\n\t10: \"\",\n\t16: \"0x\",\n};\n\nfunction parseMathematicalExpression(infix: string) {\n\t// Shunting-yard Algorithm -- https://en.wikipedia.org/wiki/Shunting-yard_algorithm\n\tconst outputQueue: string[] = [];\n\tconst operatorStack: Operator[] = [];\n\tinfix = infix.replace(/\\s+/g, \"\");\n\tconst infixArray = infix.split(/([+\\-*/%^()])/).filter(token => token);\n\tlet isExprExpected = true;\n\tfor (const token of infixArray) {\n\t\tif (isExprExpected && \"+-\".includes(token)) {\n\t\t\tif (token === '-') operatorStack.push('negative');\n\t\t} else if (\"^%*/+-\".includes(token)) {\n\t\t\tif (isExprExpected) throw new SyntaxError(`Got \"${token}\" where an expression should be`);\n\t\t\tconst op = OPERATORS[token as Operator];\n\t\t\tlet prevToken = operatorStack[operatorStack.length - 1] || '(';\n\t\t\tlet prevOp = OPERATORS[prevToken];\n\t\t\twhile (op.associativity === \"Left\" ? op.precedence <= prevOp.precedence : op.precedence < prevOp.precedence) {\n\t\t\t\toutputQueue.push(operatorStack.pop()!);\n\t\t\t\tprevToken = operatorStack[operatorStack.length - 1] || '(';\n\t\t\t\tprevOp = OPERATORS[prevToken];\n\t\t\t}\n\t\t\toperatorStack.push(token as Operator);\n\t\t\tisExprExpected = true;\n\t\t} else if (token === \"(\") {\n\t\t\tif (!isExprExpected) throw new SyntaxError(`Got \"(\" where an operator should be`);\n\t\t\toperatorStack.push(token as Operator);\n\t\t\tisExprExpected = true;\n\t\t} else if (token === \")\") {\n\t\t\tif (isExprExpected) throw new SyntaxError(`Got \")\" where an expression should be`);\n\t\t\twhile (operatorStack.length && operatorStack[operatorStack.length - 1] !== \"(\") {\n\t\t\t\toutputQueue.push(operatorStack.pop()!);\n\t\t\t}\n\t\t\toperatorStack.pop();\n\t\t\tisExprExpected = false;\n\t\t} else {\n\t\t\tif (!isExprExpected) throw new SyntaxError(`Got \"${token}\" where an operator should be`);\n\t\t\toutputQueue.push(token);\n\t\t\tisExprExpected = false;\n\t\t}\n\t}\n\tif (isExprExpected) throw new SyntaxError(`Input ended where an expression should be`);\n\twhile (operatorStack.length > 0) {\n\t\tconst token = operatorStack.pop()!;\n\t\tif (token === '(') continue;\n\t\toutputQueue.push(token);\n\t}\n\treturn outputQueue;\n}\n\nfunction solveRPN(rpn: string[]): [number, number] {\n\tlet base = 10;\n\tconst resultStack: number[] = [];\n\tfor (let token of rpn) {\n\t\tif (token === 'negative') {\n\t\t\tif (!resultStack.length) throw new SyntaxError(`Unknown syntax error`);\n\t\t\tresultStack.push(-resultStack.pop()!);\n\t\t} else if (!\"^%*/+-\".includes(token)) {\n\t\t\tif (token.endsWith('h')) {\n\t\t\t\t// Convert h suffix for hexadecimal to 0x prefix\n\t\t\t\ttoken = `0x${token.slice(0, -1)}`;\n\t\t\t} else if (token.endsWith('o')) {\n\t\t\t\t// Convert o suffix for octal to 0o prefix\n\t\t\t\ttoken = `0o${token.slice(0, -1)}`;\n\t\t\t} else if (token.endsWith('b')) {\n\t\t\t\t// Convert b suffix for binary to 0b prefix\n\t\t\t\ttoken = `0b${token.slice(0, -1)}`;\n\t\t\t}\n\t\t\tif (token.startsWith('0x')) base = 16;\n\t\t\tif (token.startsWith('0b')) base = 2;\n\t\t\tif (token.startsWith('0o')) base = 8;\n\t\t\tlet num = Number(token);\n\t\t\tif (isNaN(num) && token.toUpperCase() in Math) {\n\t\t\t\t// @ts-expect-error Math consts should be safe\n\t\t\t\tnum = Math[token.toUpperCase()];\n\t\t\t}\n\t\t\tif (isNaN(num) && token !== 'NaN') {\n\t\t\t\tthrow new SyntaxError(`Unrecognized token ${token}`);\n\t\t\t}\n\t\t\tresultStack.push(num);\n\t\t} else {\n\t\t\tif (resultStack.length < 2) throw new SyntaxError(`Unknown syntax error`);\n\t\t\tconst a = resultStack.pop()!;\n\t\t\tconst b = resultStack.pop()!;\n\t\t\tswitch (token) {\n\t\t\tcase \"+\":\n\t\t\t\tresultStack.push(a + b);\n\t\t\t\tbreak;\n\t\t\tcase \"-\":\n\t\t\t\tresultStack.push(b - a);\n\t\t\t\tbreak;\n\t\t\tcase \"*\":\n\t\t\t\tresultStack.push(a * b);\n\t\t\t\tbreak;\n\t\t\tcase \"/\":\n\t\t\t\tresultStack.push(b / a);\n\t\t\t\tbreak;\n\t\t\tcase \"%\":\n\t\t\t\tresultStack.push(b % a);\n\t\t\t\tbreak;\n\t\t\tcase \"^\":\n\t\t\t\tresultStack.push(b ** a);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\tif (resultStack.length !== 1) throw new SyntaxError(`Unknown syntax error`);\n\treturn [resultStack.pop()!, base];\n}\n\nexport const commands: Chat.ChatCommands = {\n\tmath: \"calculate\",\n\tcalculate(target, room, user) {\n\t\tif (!target) return this.parse('/help calculate');\n\n\t\tlet base = 0;\n\t\tconst baseMatchResult = (/\\b(?:in|to)\\s+([a-zA-Z]+)\\b/).exec(target);\n\t\tif (baseMatchResult) {\n\t\t\tswitch (toID(baseMatchResult[1])) {\n\t\t\tcase 'decimal': case 'dec': base = 10; break;\n\t\t\tcase 'hexadecimal': case 'hex': base = 16; break;\n\t\t\tcase 'octal': case 'oct': base = 8; break;\n\t\t\tcase 'binary': case 'bin': base = 2; break;\n\t\t\tdefault:\n\t\t\t\treturn this.errorReply(`Unrecognized base \"${baseMatchResult[1]}\". Valid options are binary or bin, octal or oct, decimal or dec, and hexadecimal or hex.`);\n\t\t\t}\n\t\t}\n\t\tconst expression = target.replace(/\\b(in|to)\\s+([a-zA-Z]+)\\b/g, '').trim();\n\n\t\tif (!this.runBroadcast()) return;\n\t\ttry {\n\t\t\tconst [result, inferredBase] = solveRPN(parseMathematicalExpression(expression));\n\t\t\tif (!base) base = inferredBase;\n\t\t\tlet baseResult = '';\n\t\t\tif (Number.isFinite(result) && base !== 10) {\n\t\t\t\tbaseResult = `${BASE_PREFIXES[base]}${result.toString(base).toUpperCase()}`;\n\t\t\t\tif (baseResult === expression) baseResult = '';\n\t\t\t}\n\t\t\tlet resultStr = '';\n\t\t\tconst resultTruncated = parseFloat(result.toPrecision(15));\n\t\t\tlet resultDisplay = resultTruncated.toString();\n\t\t\tif (resultTruncated > 10 ** 15) {\n\t\t\t\tresultDisplay = resultTruncated.toExponential();\n\t\t\t}\n\t\t\tif (baseResult) {\n\t\t\t\tresultStr = `<strong>${baseResult}</strong> = ${resultDisplay}`;\n\t\t\t} else {\n\t\t\t\tresultStr = `<strong>${resultDisplay}</strong>`;\n\t\t\t}\n\t\t\tthis.sendReplyBox(`${expression}<br />= ${resultStr}`);\n\t\t} catch (e: any) {\n\t\t\tthis.sendReplyBox(\n\t\t\t\tUtils.html`${expression}<br />= <span class=\"message-error\"><strong>Invalid input:</strong> ${e.message}</span>`\n\t\t\t);\n\t\t}\n\t},\n\tcalculatehelp: [\n\t\t`/calculate [arithmetic question] - Calculates an arithmetical question. Supports PEMDAS (Parenthesis, Exponents, Multiplication, Division, Addition and Subtraction), pi and e.`,\n\t\t`/calculate [arithmetic question] in [base] - Returns the result in a specific base. [base] can be bin, oct, dec or hex.`,\n\t],\n};\n"],
"mappings": "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",
"names": []
}