#A command explaining the math evaluation
1 messages · Page 1 of 1 (latest)
What's your issue with these?
16>>4&6 = (16>>4)&6 = 1&6 = 0
And 16>>(4&6) = 16>>4 = 1
For example, it would be nice, if there was a way to know that parentheses even work. Also the fact that the bot simply ignores messages with '>>' and '&' at the same time, seems sloppy.
Afaik only if they evaluate to 0
Maybe negative values, too, but I didn't try that yet iirc
I experimented in #bot-stuff with /calc and found this binding order:
( )
^
* / %
+ -
<< >>
& | ⊻
Parenthesises obviously have the highest binding power to allow grouping.
Lines with more than one operator mean, that those have the same binding strength and are evaluated left-to-right (which is really weird with the logic stuff, but that's how it is apparently... - as a programmer, I don't like this at all).
Tomorrow I'll use that to make my own calculation function and post it here if all lines up with the calc command
Thanks for giving me the motiviation to finally check this stuff ^^
I was a bit busy, but I'm working on it again now
Had a tiny mistake, but easy fix - now it works
If you know how to run JS code and want to help testing, here's the code:
var digitsOnlyPattern = '^[0-9]+$';
var simpleOperatorPattern = '^[-+*/^%⊻&|()]$';
var compoundOperatorPattern = '^(<<|>>)$';
var operatorCharsPattern = '^[-+*/^%⊻&|<>()]+$';
var numberPattern = '^[0-9.]+$';
function calculate (calcString) {
// tokenise, then evaluate separately
let tokens = [];
let currentToken = '';
for (let i=0; i<calcString.length; i++) {
if (
currentToken.match(simpleOperatorPattern) || currentToken.match(compoundOperatorPattern) ||
currentToken.match(numberPattern) && calcString[i].match(operatorCharsPattern) ||
currentToken.match(operatorCharsPattern) && calcString[i].match(numberPattern)
) {
tokens.push(currentToken);
currentToken = '';
}
currentToken += calcString[i];
}
tokens.push(currentToken);
for (let i=0; i < tokens.length; i++) {
if (tokens[i].match(digitsOnlyPattern)) {
tokens[i] = parseInt(tokens[i]);
} else if (tokens[i].match(numberPattern)) {
tokens[i] = parseFloat(tokens[i]);
}
}
return calculateTokenised(tokens);
}
function calculateTokenised (tokens) {
let lastOpeningIndex;
do {
lastOpeningIndex = -1
for (let i=0; i < tokens.length; i++) {
if (tokens[i] == '(') {
lastOpeningIndex = i;
} else if (tokens[i] == ')') {
if (lastOpeningIndex == -1) {
throw 'Malformed parenthesis';
}
let slice = tokens.splice(lastOpeningIndex, i - lastOpeningIndex + 1);
let partialEval = calculateTokenised(slice.splice(1, slice.length - 2));
tokens.splice(lastOpeningIndex, 0, partialEval);
}
}
} while (lastOpeningIndex != -1);
let operators = [
[
{ 'str': '^', 'f': (a,b) => Math.pow(a, b) }
],
[
{ 'str': '*', 'f': (a,b) => a*b },
{ 'str': '/', 'f': (a,b) => a/b },
{ 'str': '%', 'f': (a,b) => a%b }
],
[
{ 'str': '+', 'f': (a,b) => a+b },
{ 'str': '-', 'f': (a,b) => a-b }
],
[
{ 'str': '<<', 'f': (a,b) => a<<b },
{ 'str': '>>', 'f': (a,b) => a>>b }
],
[
{ 'str': '&', 'f': (a,b) => a&b },
{ 'str': '|', 'f': (a,b) => a|b },
{ 'str': '⊻', 'f': (a,b) => a^b }
]
];
for (let p=0; p<operators.length; p++) {
let ops = operators[p];
let foundOp;
do {
foundOp = false;
for (let t=1; t < tokens.length - 1 && !foundOp; t++) {
for (let op=0; op < ops.length && !foundOp; op++) {
if (tokens[t] == ops[op].str) {
foundOp = true;
let a = tokens[t-1];
let b = tokens[t+1];
if (typeof a != 'number' || typeof b != 'number') {
console.error(tokens);
throw 'Encountered non-number token where number was expected';
}
let partialEval = ops[op].f(a, b);
tokens.splice(t-1, 3, partialEval);
}
}
}
} while (foundOp);
}
if (tokens.length != 1) {
console.error(tokens);
throw 'Evaluation failed';
}
return tokens[0];
}
(I know it's not the cleanest and I would've used TS if that was an option)