Skip to content

Instantly share code, notes, and snippets.

@burgil
Last active April 18, 2024 02:51
Show Gist options
  • Save burgil/d64f86bdc1b9a7cfa37c828c0e6dfd1c to your computer and use it in GitHub Desktop.
Save burgil/d64f86bdc1b9a7cfa37c828c0e6dfd1c to your computer and use it in GitHub Desktop.
#Fixed math, the universe and everything in between ! Serverless compatible Math-Only JavaScript EVAL ! Introducing math eval - A math only eval that can actually calculate numbers unlike python and javascript lol, and does not require any library
const evalMath = (str) => {
const operatorToFunction = {
"+": (a, b) => a + b,
"-": (a, b) => a - b,
"*": (a, b) => a * b,
"/": (a, b) => a / b
};
const operationStr = str.replace(/\s/g, '');
const numbers = operationStr.split(/[-+*/]/).map(Number);
const operators = operationStr.split(/\d+/).filter(Boolean).filter(operator => operator !== '.');
for (let i = 0; i < operators.length; i++) {
const operator = operators[i];
const nextNumber = numbers[i + 1];
if (operator === '*' || operator === '/') {
numbers[i] = operatorToFunction[operator](numbers[i], nextNumber);
numbers.splice(i + 1, 1);
operators.splice(i, 1);
i--;
}
}
let result = numbers[0];
for (let i = 0; i < operators.length; i++) {
const operator = operators[i];
const nextNumber = numbers[i + 1];
result = operatorToFunction[operator](result, nextNumber);
}
const numberString = result.toFixed(15);
const decimalIndex = numberString.indexOf('.');
let trimmedNumberString = numberString;
if (decimalIndex !== -1) {
let i = numberString.length - 1;
while (numberString[i] === '0' && i > decimalIndex) {
i--;
}
trimmedNumberString = numberString.substring(0, i + 1);
if (trimmedNumberString.charAt(trimmedNumberString.length - 1) === '.') trimmedNumberString = trimmedNumberString.slice(0, -1);
}
const finalResult = parseFloat(trimmedNumberString);
// test: (compares itself to eval)
const DEBUG = true;
if (DEBUG) {
const PERFORM_TEST = false; // <<<<< enable this
if (!PERFORM_TEST) {
console.log(str, '=', finalResult)
} else {
const ONLY_SHOW_INVALID = false;
try {
const evalResult = eval(str);
const numberString2 = evalResult.toFixed(15);
const decimalIndex2 = numberString2.indexOf('.');
let trimmedNumberString2 = numberString2;
if (decimalIndex2 !== -1) {
let i2 = numberString2.length - 1;
while (numberString2[i2] === '0' && i2 > decimalIndex2) {
i2--;
}
trimmedNumberString2 = numberString2.substring(0, i2 + 1);
if (trimmedNumberString2.charAt(trimmedNumberString2.length - 1) === '.') trimmedNumberString2 = trimmedNumberString2.slice(0, -1);
}
const finalEvalResult = parseFloat(trimmedNumberString2);
const isValid = finalResult === finalEvalResult;
if (ONLY_SHOW_INVALID) {
if (!isValid) console.log(str, '=', finalResult, 'Invalid: ' + finalEvalResult)
} else {
console.log(str, '=', finalResult, "Is Valid?", isValid, isValid ? 'Valid' : 'Invalid: ' + finalEvalResult)
}
} catch(e) {
console.warn("Eval Error:", str, e.message);
}
}
}
// you can remove the test (which uses eval to verify itself) ^^
return finalResult;
};
let example;
example = evalMath('1 + 1');
example = evalMath('4 - 1');
example = evalMath('2 * 5');
example = evalMath('16 / 4');
example = evalMath('1 + 1 + 2');
example = evalMath('4 - 1 - 2');
example = evalMath('15 * 1000 * 2 / 2 * 2');
example = evalMath('1 + 15 * 1000 * 2 / 2 * 2');
example = evalMath('15 * 1000 * 2 / 2 * 2 + 1');
example = evalMath('16 / 4 / 2');
example = evalMath('5 * 5');
example = evalMath('10 / 2');
example = evalMath('3 + 5 * 2');
example = evalMath('3 * 5 + 2');
example = evalMath('10 - 3 * 2 + 5');
example = evalMath('10 - 3 * 2');
example = evalMath('3 * 2 + 5');
example = evalMath('2 * 3 + 5 * 4');
// example = evalMath('2 * (3 + 5)'); // Error
// example = evalMath('a + a'); // Error
// example = evalMath('b'); // Error
example = evalMath('0');
example = evalMath('20');
example = evalMath('-10 + 10');
example = evalMath('-10 + 12');
example = evalMath('10 - 15');
// example = evalMath('1-'); // Error
example = evalMath('-1');
example = evalMath('10 / 3');
example = evalMath('1+1+1*2+1+1+1');
example = evalMath('2.5 * 5');
example = evalMath('1.5 + 1.5');
example = evalMath('4.5 - 1.2');
example = evalMath('2.3 * 2.5');
example = evalMath('16.8 / 4.2');
example = evalMath('1.1 + 1.2 + 2.3');
example = evalMath('4.5 - 1.2 - 2.1');
example = evalMath('15.25 * 100.5 * 2.2 / 2.5 * 2.5');
example = evalMath('1.1 + 15.5 * 100.25 * 2.2 / 2.1 * 2.1');
example = evalMath('15.5 * 1000.2 * 2.1 / 2.5 * 2.5 + 1.5');
example = evalMath('16.5 / 4.5 / 2.5');
example = evalMath('5.5 * 5.5');
example = evalMath('10.5 / 2.5');
example = evalMath('3.5 + 5.5 * 2.5');
example = evalMath('3.2 * 5.5 + 2.5');
example = evalMath('10.5 - 3.2 * 2.5 + 5.5');
example = evalMath('10.5 - 3.2 * 2.5');
example = evalMath('3.5 * 2.5 + 5.5');
example = evalMath('2.1 * 3.5 + 5.5 * 4.5');
example = evalMath('2.5 * 5.2');
example = evalMath('0.1');
example = evalMath('20.2');
example = evalMath('-10.5 + 10.5');
example = evalMath('-10.5 + 12.2');
example = evalMath('10.5 - 15.5');
// example = evalMath('1.1-'); // Error
example = evalMath('-1.1');
example = evalMath('10.5 / 3.5');
example = evalMath('1.2+1.3+1.5*2.5+1.7+1.8+1.9');
"Finished"
@burgil
Copy link
Author

burgil commented Apr 18, 2024

Important notes:

  1. If you define operatorToFunction outside the scope of executeOperation sometimes you will get can not access operatorToFunction before initialization when testing on the devTools

  2. Unless the . is filtered, the operators read it as an operator

  3. Unless the result is fixed to 15 decimal points, 3.3-2.1 does not equal 1.2.

  4. To prevent the extra zeros after the decimal caused by fixing to 15 decimal points the third loop is needed.

  5. To prevent cases where a . is all that remain after removing all zeros the extra slice is needed at the end

  6. To support multiple operations the ... is needed

@burgil
Copy link
Author

burgil commented Apr 18, 2024

Best use case:

Best used when setting up build-time math-only evals, for example rate limits, and NOT for user input

Fixes this eval scenario for example:

const RATE_LIMITS = {
    "IP": {
        // perSecond: 1,
        // perMinute: 1,
        // perHour: 2,
        // perDay: 6,
        // perWeek: 8,
        // perMonth: 10,
        // perYear: 100,
        custom: {
            "15 * 1000": 1, // allowed up to 1 request per 15 seconds
            "30 * 1000": 1, // allowed up to 1 request per 30 seconds
            "15000": 1, // allowed up to 1 request per 15 seconds
            "2 minutes": 1, // allowed up to 1 request per 2 minutes
            "1 minute": 1, // allowed up to 1 request per 1 minutes
            "1 second": 1, // 
            "0.5 years": 1, // 
            "0.5 seconds": 1, // 
            "1 hour and 30 minutes": 1, // 
            "90 minutes": 1, // 
        }
    },
    "Email": {
        perSecond: 1,
        perMinute: 1,
        perHour: 2,
        perDay: 3,
        perWeek: 4,
        perMonth: 5,
        perYear: 50,
    }
};

for (const limit in RATE_LIMITS) {
    if (RATE_LIMITS[limit].custom) {
        for (const customLimit in RATE_LIMITS[limit].custom) {
            if (customLimit.includes('+') || customLimit.includes('*') || customLimit.includes('-') || customLimit.includes('/')) {
                const newLimit = eval(customLimit); // simply replace with evalMath
                console.log(newLimit, customLimit);
                const limitCount = RATE_LIMITS[limit].custom[customLimit];
                delete RATE_LIMITS[limit].custom[customLimit];
                if (RATE_LIMITS[limit].custom[newLimit]) {
                    console.warn("Time Limit Already Exist:", newLimit, customLimit, limit);
                } else {
                    RATE_LIMITS[limit].custom[newLimit] = limitCount;
                }
            }
        }
    }
}
console.log(RATE_LIMITS); // This simply replaced the 15 * 1000 with 15000 for example

@burgil
Copy link
Author

burgil commented Apr 18, 2024

Python trying to calculate numbers: 🤣

image

JavaScript trying to calculate numbers: 🤯

image

Calculator:

image

My Solution:

Test and find out

@burgil
Copy link
Author

burgil commented Apr 18, 2024

Spoiler:
image

@burgil
Copy link
Author

burgil commented Apr 18, 2024

To solve the python one just use:
format(3.3-2.1, '.1f')
image

or

from decimal import Decimal
result = Decimal('3.3') - Decimal('2.1')
print(result)

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment