Some checks failed
Deploy to GitHub Pages / build-and-deploy (push) Has been cancelled
141 lines
4.4 KiB
JavaScript
141 lines
4.4 KiB
JavaScript
// RPN (Reverse Polish Notation) calculator module
|
|
const RPNCalculator = (function() {
|
|
class RPNCalculator {
|
|
constructor() {
|
|
this.clear();
|
|
}
|
|
|
|
// Push a number onto the stack
|
|
push(value) {
|
|
if (typeof value === 'number' || !isNaN(parseFloat(value))) {
|
|
this.stack.push(parseFloat(value));
|
|
}
|
|
}
|
|
|
|
// Pop a number from the stack
|
|
pop() {
|
|
if (this.stack.length > 0) {
|
|
return this.stack.pop();
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
// Perform an operation on stack values
|
|
operate(operator) {
|
|
if (this.stack.length < 2) {
|
|
return 'Error'; // Need at least 2 values for binary operations
|
|
}
|
|
|
|
const b = this.pop();
|
|
const a = this.pop();
|
|
let result;
|
|
|
|
switch (operator) {
|
|
case '+':
|
|
result = a + b;
|
|
break;
|
|
case '-':
|
|
result = a - b;
|
|
break;
|
|
case '*':
|
|
result = a * b;
|
|
break;
|
|
case '/':
|
|
if (b === 0) {
|
|
result = Infinity;
|
|
} else {
|
|
result = a / b;
|
|
}
|
|
break;
|
|
case '^':
|
|
case '**':
|
|
result = Math.pow(a, b);
|
|
break;
|
|
default:
|
|
return 'Error'; // Unknown operator
|
|
}
|
|
|
|
// Check for invalid results
|
|
if (isNaN(result) || !isFinite(result)) {
|
|
this.push('Error');
|
|
return 'Error';
|
|
}
|
|
|
|
this.push(result);
|
|
return result;
|
|
}
|
|
|
|
// Clear the entire stack
|
|
clear() {
|
|
this.stack = [];
|
|
}
|
|
|
|
// Get current stack state
|
|
getStack() {
|
|
return [...this.stack]; // Return a copy to prevent external modification
|
|
}
|
|
|
|
// Execute RPN expression (e.g., "3 4 +")
|
|
evaluate(rpnExpression) {
|
|
try {
|
|
// Split the expression into tokens
|
|
const tokens = rpnExpression.trim().split(/\s+/);
|
|
|
|
this.clear(); // Clear the stack before evaluation
|
|
|
|
for (const token of tokens) {
|
|
if (['+', '-', '*', '/', '^', '**'].includes(token)) {
|
|
// It's an operator
|
|
const result = this.operate(token);
|
|
if (result === 'Error') {
|
|
return 'Error';
|
|
}
|
|
} else {
|
|
// It's a number
|
|
const num = parseFloat(token);
|
|
if (isNaN(num)) {
|
|
return 'Error'; // Invalid token
|
|
}
|
|
this.push(num);
|
|
}
|
|
}
|
|
|
|
// Return the top of the stack if there's a result
|
|
if (this.stack.length === 1) {
|
|
return this.stack[0];
|
|
} else if (this.stack.length === 0) {
|
|
return 0; // Empty stack, return 0
|
|
} else {
|
|
return this.stack[this.stack.length - 1]; // Return top of stack
|
|
}
|
|
} catch (error) {
|
|
return 'Error';
|
|
}
|
|
}
|
|
|
|
// Process a single RPN input token
|
|
processInput(token) {
|
|
if (['+', '-', '*', '/', '^', '**'].includes(token)) {
|
|
// Operator
|
|
return this.operate(token);
|
|
} else if (token === 'ENTER' || token === 'E') {
|
|
// ENTER doesn't do anything in implementation, just pushes numbers that were already processed
|
|
return this.getStack();
|
|
} else if (token === 'C' || token === 'CE') {
|
|
this.clear();
|
|
return 0;
|
|
} else {
|
|
// Number
|
|
const num = parseFloat(token);
|
|
if (!isNaN(num)) {
|
|
this.push(num);
|
|
return num;
|
|
} else {
|
|
return 'Error';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return { RPNCalculator };
|
|
})(); |