Build a Math Evaluation Engine in JavaScript from Scratch

math evaluation engine

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

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 AST
ast vs postfix

Instead 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 Support

Next, 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!

Post a Comment

0 Comments