Have you ever wanted to build a calculator that can do more than just simple addition and subtraction? Just imagine—what if you could solve complex mathematical equations in text format, such as "8 / 4 + 2"
In this tutorial, we'll build a math evaluator in JavaScript from scratch. We won’t use any external libraries or even eval(). I also created the Exprify - math evaluator library, and I'll show you how to use my library.
You will learn how computers solve mathematical expressions step by step. This is not just about building a calculator- It’s about understanding the core ideas behind parsing and evaluating expressions. These concepts have many calculator applications.
🔗 You can find the math evaluation code examples on GitHub.
By the end, you will learn:
- How to breakdown a math expression into parts
- How to understand the order of math operations
- How to calculate the final result
Before you start, you need to have some basic understanding of JavaScript. Don't worry if you're just starting to learn JavaScript, we'll take you step by step.
Table of Contents
- Math Evaluation Engine: Basic Concept
- Why Should eval() Be Avoided?
- How to create a Math Evaluation?
- Next Step
- Closing Thoughts
Math Evaluation Engine: Basic Concept
The math evaluation engine is a programming method, that works by calculating a mathematical expression from plain text. JavaScript cannot directly solve equations like "2 + 3 * 4" (string), so the math evaluation engine is parsing, structuring, and evaluating them.
Text → Breakdown → Structure → Calculate
First, the math evaluation engine breaks the string into smaller parts—such as numbers or operators. Next, the engine analyzes the structure or syntax of the expression. Finally, the engine performs the calculations step-by-step to solve the final answer.
Math Evaluation Libraries in JavaScript
If you don’t want to build a math evaluation from scratch, there are several ready-made libraries in JavaScript like math.js, expr-eval, or nerdamer.
Here is an example of Exprify—a math evaluation library.
<script src="https://unpkg.com/exprify"></script>
<script>
const expr = new Exprify();
console.log(expr.evaluate("(100 + 15) * 2")); //230
console.log(expr.evaluate("9 / 3 + 2i")); //3 + 2i
console.log(expr.evaluate("3 inch to cm")); //7.62 cm
</script>
This library is made by me, and I will show you how to make such a library.
Why Should eval() Be Avoided?
Although eval() function can evaluate mathematical expressions, but it's not used in applications because of many problems. The biggest problem with eval() is that it doesn't just math calculations—it can also execute any JavaScript code. For example:-
eval("2 + 3 * 4"); //14
eval("alert('code')"); //JavaScript
This means a user can run harmful code inside your application, which is dangerous.
Another issue is performance. Since eval() parses and runs code while executing, it's slower than structured methods. These methods break expressions into tokens and evaluate them step by step.
For these reasons, developers prefer to create a custom math evaluation engine, which allows safe execution.
How to create a Math Evaluation?
Now we come to the main part—what method can be used to create a Math Evaluation Engine? This cannot be answered in one word, because it depends on your project.
If you just want to create a simple calculator, you can create math evaluation using very simple method.
Tokenization → infixToPostfix → Evaluation
But, if you want to build a PRO-level math engine—one that functions like a programming language or is capable of advanced matrix operations—then you must follow a structured method.
Tokenization → AST (Abstract Syntax Tree) → Evaluation
Here, we'll create Math Evaluation by following a simple method.
Tokenization: From Text to Tokens
Tokenization is the first step. This step is a mathematical expression, which is first divided into smaller parts from the text (string) size. These are our tokens.
When the user enters an expression, such as "2 + (3 * 4)", the computer sees it as just plain text. Computer doesn't understand what is a number, what is an operator, or how to calculate. Tokenization solves this problem.
Using the simple method, the expression transforms as follows:
[2, "+", "(", 3, "*", 4, ")"]
Here, each part can be a number, an operator (+, -, *, /), or parentheses.
function tokenize(expression) {
return expression
.match(/\d+|\+|\-|\*|\/|\(|\)/g)
.map(token => (isNaN(token) ? token : Number(token)));
}
// Example
const result = tokenize("2 + (3 * 4)");
console.log(result); // [2, "+", "(", 3, "*", 4, ")"]
Breakdown this way makes the next steps much easier. But if you want to create more advanced Math Evaluation, then tokenizing like this will be difficult. For example- You can tokenize a complex math question like "(x + 12) * 1" to transforms as follows:
[
{"type": "Parenthesis","value": "(","pos": 0},
{"type": "Identifier","name": "x","pos": 2},
{"type": "Operator","value": "+","pos": 3},
{"type": "Number","value": 12, "pos": 7},
{"type": "Parenthesis","value": ")","pos": 7},
{"type": "Operator","value": "*","pos": 9},
{"type": "Number","value": 1,"pos": 11}
]
Tokenizing in this JSON structure makes it easy to build an AST (Abstract Syntax Tree). But now we don't need to build an AST for basic math evaluation.
Infix to Postfix Conversion
This process may seem a little complicated at first, but no problem. The way we usually write math is called Infix notation. For example: 2 + 3 * 4
Here the operators (such as +, *) are placed between two numbers. Human easily understand that multiplication (*) must be done first, then addition (+). But the computer doesn't understand which one comes first and later.
The solution to this problem is to use Postfix notation (Reverse Polish Notation). The same expression written in Postfix is: 2 3 4 * +
Here the operator is at the end, so there is no confusion. The computer can calculate directly from left to right. Infix expressions have to handle operator precedence (such as * before, + after) and parentheses (()), which complicates evaluation. But Postfix doesn't need parentheses.
function infixToPostfix(tokens) {
const output = [];
const stack = [];
const operators = {
'^': { precedence: 3, associativity: 'right' },
'*': { precedence: 2, associativity: 'left' },
'/': { precedence: 2, associativity: 'left' },
'%': { precedence: 2, associativity: 'left' },
'+': { precedence: 1, associativity: 'left' },
'-': { precedence: 1, associativity: 'left' }
};
tokens.forEach(token => {
if (typeof token === "number") {
output.push(token);
} else if (token === "(") {
stack.push(token);
} else if (token === ")") {
while (stack.length &&
stack[stack.length - 1] !== "(") {
output.push(stack.pop());
}
stack.pop();
} else {
while (stack.length &&
stack[stack.length - 1] !== "(" &&
(operators[stack[stack.length - 1]].precedence >
operators[token].precedence ||
(operators[stack[stack.length - 1]].precedence
=== operators[token].precedence
&& operators[token].associativity === 'left')
)){
output.push(stack.pop());
}
stack.push(token);
}
});
while (stack.length) {
output.push(stack.pop());
}
return output;
}
// Example
const tokens = [2, "+", "(", 3, "*", 4, ")"];
console.log(infixToPostfix(tokens)); //[2, 3, 4, '*', '+']
A stack-based algorithm (like Shunting Yard Algorithm) is used to convert from Infix to Postfix. In this process we use output list and operator stack. The number is added directly to the output and the operators go on the stack.
Postfix Evaluation
Now we have come to the last step of creating a Math Evaluation Engine, Postfix Expression Evaluation. After converting from Infix to Postfix, our job now is to calculate that Postfix expression.
Here we use the LIFO method (Last In First Out), meaning the latest data comes out first. The postfix expression is read from left to right and each token is processed.
function evaluatePostfix(postfix) {
const stack = [];
postfix.forEach(token => {
if (typeof token === "number") {
stack.push(token);
} else {
const b = stack.pop();
const 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(a % b); break;
case '^': stack.push(Math.pow(a, b)); break;
}
}
});
return stack[0];
}
// Example
const postfix = [2, 3, 4, '*', '+'];
console.log(evaluatePostfix(postfix)); //14
When a number is found, it's pushed onto the stack. If an operator is found, the last two numbers are popped from the stack and calculated. The result is then pushed back onto the stack.
In this way, the expression is processed step by step. In the end, there is only one value left on the stack, and that is the final answer.
Next Step
Now that you can build a basic math evaluation engine, it's time to move on to more advanced features. At this point, your project starts to look like a min programming language. The steps you need to take to do this are:
1. Replace Postfix with ASTInstead of converting expressions into postfix notation, you should build an AST (Abstract Syntax Tree). AST makes it easy to add features like functions, variables, and complex math.
2. Add Function SupportNext, extend your engine to support in-built functions like:
sin(0)
max(2, 5)
sqrt(16)
3. Support Variables
Allow users to write expressions like:
x + 2
y * 5
You can also add more options such as Array or Matrix Operations.
Closing Thoughts
Building a Math Evaluation Engine from scratch in JavaScript is more than just creating a calculator. It is a deep dive into how programming languages work.
Throughout this journey.
You began with simple ideas like breakdown expressions into tokens. Then you moved on to converting infix expressions into postfix notation. Finally, you evaluated them using a LIFO method.
These steps may seem small on their own, but together they build the foundation of parsing systems.
Thank you for reading this patiently. If you have any problems, you can let me know through the comments. I am Nirmal Paul, for more coding related stuff, you can visit my YouTube channel.
Now it's your turn, Happy Postfixing!
0 Comments