jsOMS/Math/MathProcessor.js
Dennis Eichhorn 7b75ec58f7
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CI / general_module_workflow_js (push) Has been cancelled
fix permissions
2025-04-02 14:15:07 +00:00

129 lines
3.5 KiB
JavaScript

/**
* Math processor.
*
* @copyright Dennis Eichhorn
* @license OMS License 2.2
* @version 1.0.0
* @since 1.0.0
*/
export class MathProcessor
{
/**
* Evaluate math formula
*
* @param {string} equation Equation
*
* @return {null|number}
*
* @since 1.0.0
*/
static mathEvaluate (equation)
{
const stack = [];
const postfix = MathProcessor.shuntingYard(equation);
const length = postfix.length;
for (let i = 0; i < length; ++i) {
if (!isNaN(parseFloat(postfix[i])) && isFinite(postfix[i])) {
stack.push(postfix[i]);
} else {
const a = MathProcessor.parseValue(stack.pop());
const b = MathProcessor.parseValue(stack.pop());
if (postfix[i] === '+') {
stack.push(a + b);
} else if (postfix[i] === '-') {
stack.push(b - a);
} else if (postfix[i] === '*') {
stack.push(a * b);
} else if (postfix[i] === '/') {
stack.push(b / a);
} else if (postfix[i] === '^') {
stack.push(Math.pow(b, a));
}
}
}
const result = stack.pop();
return !isNaN(parseFloat(result)) && isFinite(result) ? result : null;
};
/**
* Parse value
*
* @param {string} value Value to parse
*
* @return {number}
*
* @since 1.0.0
*/
static parseValue (value)
{
return typeof value === 'string' ? (value.indexOf('.') === -1 ? parseInt(value) : parseFloat(value)) : value;
};
/**
* Perform shunting yard
*
* @param {string} value Value to parse
*
* @return {Array}
*
* @since 1.0.0
*/
static shuntingYard (equation)
{
const stack = [];
const operators = {
'^': { precedence: 4, order: 1 },
'*': { precedence: 3, order: -1 },
'/': { precedence: 3, order: -1 },
'+': { precedence: 2, order: -1 },
'-': { precedence: 2, order: -1 }
};
const output = [];
equation = equation.replace(/\s+/g, '');
equation = equation.split(/([\+\-\*\/\^\(\)])/).filter(function (n) { return n !== ''; });
const length = equation.length;
let token;
for (let i = 0; i < length; ++i) {
token = equation[i];
if (!isNaN(parseFloat(token)) && isFinite(token)) {
output.push(token);
} else if ('^*/+-'.indexOf(token) !== -1) {
const o1 = token;
let o2 = stack[stack.length - 1];
while ('^*/+-'.indexOf(o2) !== -1
&& ((operators[o1].order === -1 && operators[o1].precedence <= operators[o2].precedence)
|| (operators[o1].order === 1 && operators[o1].precedence < operators[o2].precedence))
) {
output.push(stack.pop());
o2 = stack[stack.length - 1];
}
stack.push(o1);
} else if (token === '(') {
stack.push(token);
} else if (token === ')') {
while (stack[stack.length - 1] !== '(') {
output.push(stack.pop());
}
stack.pop();
}
}
while (stack.length > 0) {
output.push(stack.pop());
}
return output;
};
};