Building a calculator in JavaScript is the classic beginner project. Building a good calculator that handles operator precedence negative numbers and edge cases correctly is surprisingly difficult.
The operator precedence problem
The simplest calculator implementation processes operations left to right:
2 + 3 * 4 = 20 (wrong)
The correct answer is 14 because multiplication has higher precedence than addition. A proper calculator must implement operator precedence, which requires either the Shunting-Yard algorithm or recursive descent parsing.
function evaluate(expression) {
// Tokenize
const tokens = expression.match(/(\d+\.?\d*|[+\-*/^()])/g);
// Shunting-Yard algorithm
const output = [];
const operators = [];
const precedence = { '+': 1, '-': 1, '*': 2, '/': 2, '^': 3 };
for (const token of tokens) {
if (!isNaN(token)) {
output.push(parseFloat(token));
} else if (token === '(') {
operators.push(token);
} else if (token === ')') {
while (operators[operators.length - 1] !== '(') {
output.push(operators.pop());
}
operators.pop();
} else {
while (operators.length &&
precedence[operators[operators.length - 1]] >= precedence[token]) {
output.push(operators.pop());
}
operators.push(token);
}
}
while (operators.length) output.push(operators.pop());
// Evaluate RPN
const stack = [];
for (const token of output) {
if (typeof token === 'number') {
stack.push(token);
} else {
const b = stack.pop(), a = stack.pop();
switch (token) {
case '+': stack.push(a + b); break;
case '-': stack.push(a - b); break;
case '*': stack.push(a * b); break;
case '/': stack.push(a / b); break;
case '^': stack.push(Math.pow(a, b)); break;
}
}
}
return stack[0];
}
The negative number problem
Is -3 a negative number or the unary minus operator applied to 3? Your parser needs to distinguish between:
- Unary minus:
-3 + 5(negative three plus five) - Binary minus:
8 - 3(eight minus three) - Implicit multiplication:
2(-3)(two times negative three)
The rule: a minus sign is unary if it appears at the start of the expression, after an operator, or after an opening parenthesis.
The display problem
Calculators need to show partial expressions as the user types. This means maintaining two representations: the visual expression ("2 + 3 ×") and the computable expression ("2 + 3 *").
For a full-featured calculator that handles precedence, parentheses, and scientific functions correctly, I built one at zovo.one/free-tools/online-calculator. It implements proper operator precedence and supports scientific functions.
I'm Michael Lip. I build free developer tools at zovo.one. 500+ tools, all private, all free.
Top comments (0)