eval() function from scratch in JavaScript
If you want to solve arithmetic expressions in JavaScript, we have an eval()
function for that but to understand the logic let's implement our function which solves arithmetic expressions in the exact hierarchy that needs to be solved from brackets to multiplication and division, to addition and subtraction.
Steps
Step 1-Remove unnecessary spaces from the input
Step 2-Implement the algorithm on expression to compute output
Implementation
Step 1-Removing unnecessary spaces from the input
function evaluate(expression) {
//Remove space using the replace function
expression = expression.replace(/\s/g, '');
//Expression is converted into array of characters using Array.from
//Example:1+2 will be ['1','+','2']
return calculate(Array.from(expression), 0);
}
We will receive the input in the form of a string. That string will be stored in the expression
variable. All the unnecessary spaces will be removed from the expression
variable.
Spaces are removed by applying the replace function on the expression. Here, text between / /
represents a regular expression pattern which is \s
. \s
represent all whitespace characters including spaces, tabs and line breaks. g
stands for global here, which means it will replace all occurrences of the pattern of the string; without g
it will replace only the first occurrence.
So, when expression.replace(/\s/g, '');
code executes, it searches for all whitespace characters in the expression
string and replaces them with an empty string, effectively removing all spaces and other whitespace characters from the original string. The updated string is then assigned back to the expression
variable, overwriting the original value with the modified string.
function evaluate()
will return the output from calculate()
which takes two parameters(one is the array
of characters made from the expression
string called str
,0
is the index denoting the position of where to start the execution.)
Step 2-Implement the algorithm on expression to compute output.
To solve this, we will move step by step. For now, we will take the expression which has addition and subtraction in it.
Addition and Subtraction
Let's suppose we have an expression expression=1+2-3
function calculate(str, index) {
var stack=[];
var sign='+';
var number=0;
//Traversing the array of characters
for(let i=index;i<str.length;i++){
var current=str[i];
if(current>='0' && current<='9')
number=number*10+(current-'0');
//This condition will work if current contains special characters or is at last position of the string
if(!(current>='0' && current<='9') || i===str.length-1){
//Switch case is used to push value of number variable in stack
switch(sign){
case '+':
stack.push(number);
break;
case '-':
stack.push(number*-1);
break;
}
sign=current;
number=0;
}
}
let ans = 0;
while (stack.length > 0) {
ans += stack.pop();
}
return ans;
}
Here, we will have a calculate function that will receive the array of characters and index(starting zero) from the evaluate()
function above.
We will have three variables inside the calculate()
function.
stack[]=An array that will store all the numbers of the expression.
sign=A character variable which will be used to store '+' and '-' characters.These characters will determine the positivity and negativity of numbers and will play a huge role in pushing numbers into the stack.
number =It stores single or multi-digit operands of the expression.
Working:
The loop will start with the first character in the current
variable. current will be set to current='1'
after accessing the str
First if condition in the for loop will execute and 1 will be stored in number
variable. In the line,number=number*10+(current-'0');
,current-'0'
is used so that we can convert the character into it's numeric value.
Second if condition will not work as current is neither a special character nor is i equal to the last position of the string.
2nd time the loop works, current
will be updated to '+' and the second if condition will come into play which will push the value that is in number
variable into the stack. Remember: value will be pushed which is in the number variable from the previous iteration and according to the sign '+' and not according to the current
variable sign*.* Switch case will push a positive number into the stack if the sign is '+' or else a negative number by multiplying it by -1. sign
will be set to current after the switch case is completed and number
will be set to 0 for the next operand in the expression.
When we reach the last operand or say 3, we will be able to enter the second if condition as i===str.length-1
is true which will help us push -3 into stack. -3 will be pushed as the sign
was set to '-' and number
was set to 3.
The stack variable will look like this after for loop is completed.stack[]=[1,2,-3]
.Once we are out of the loop, while loop will pop every element one by one out of the stack and add it to ans
variable which will give us the desired output which is 0.
Now, let's tackle multiplication and division operators in our expression.
Multiplication and Division
Suppose we have an expression=(1*2+3/2)
which will result in 3.5. Now we need to solve the multiplication and division parts first and then add both answers. So to do this we just have to add two more logic in our switch case.
let pre=-1;
switch(sign){
case '+':
stack.push(number);
break;
case '-':
stack.push(number*-1);
break;
case '*':
pre=stack.pop();
stack.push(pre*number);
break;
case '/':
pre=stack.pop();
stack.push(pre/number);
break;
}
In the case of multiplication ('*')
and division ('/')
, we will pop the last element(then stored in pre
variable) from the stack, multiply it by the value in the number variable and push it into the stack. In this way, our multiplication and division will always be solved first. The final stack in this case would be stack[]=[2,1.5]
. These two will result in 3.5 after for loop is finished.
Dealing with parentheses
Now the only way to deal with parentheses or brackets is to break the problem into subparts. Suppose, an expression = (3*(5/2))
.Earlier we called the calculate()
function on the whole expression when we were not given brackets but now we can call the calculate()
function whenever or wherever we see the brackets while traversing the array
.
To simplify this: calculate(3*(calculate(5/2))
function calculate(str, index) {
var stack=[];
var sign='+';
var number=0;
for(let i=index;i<str.length;i++){
var current=str[i];
if(current>='0' && current<='9')
number=number*10+(current-'0');
if(!(current>='0' && current<='9') || i===str.length-1){
if(current=='('){
number=calculate(str,i+1);
let opening_parentheses=1;
let closing_parantheses=0;
//This loop is for counting the parantheses and shift the i pointer to the right position
for(let j=i+1;j<str.length;j++){
if(str[j]===')')
{
closing_parantheses++;
if(opening_parentheses===closing_parantheses){
i=j;
break;
}
}
else if(str[j]=='('){
opening_parentheses++;
}
}
}
let pre=-1;
switch(sign){
case '+':
stack.push(number);
break;
case '-':
stack.push(number*-1);
break;
case '*':
pre=stack.pop();
stack.push(pre*number);
break;
case '/':
pre=stack.pop();
stack.push(pre/number);
break;
}
sign=current;
number=0;
if(current===')')
break;
}
}
let ans = 0;
while (stack.length > 0) {
ans += stack.pop();
}
return ans;
}
This will result in recursion and the part of the expression which needs to be solved first (5/2)
will get solved and get returned. The inner calculate
function will return 2.5, which will be stored in the number
variable of the outer stack.
Counting of brackets will be done with the help of the inner for loop which starts from j=i+1
to keep track of no. of opening and closing parantheses and to modify the iteration because we do not want to execute the same code twice that has been just solved and returned from the inner calculate()
function. That's why i=3
was shifted to i=6
in the code below.
calculate(3*(5/2),0){
//before i=2,outer_stack and sign will reach the value given below.
outer_stack[]=[3]
sign='*';
//when i=2;
number=calculate(3*(5/2),3){
sign='/'
inner_stack=[2.5]
sign=current which is (')')
//Loop will break when ')' will be encountered in the current variable and 2.5 will be returned from inner calculate.
}
}
//Once we get 2.5 in the number variable from inner calculate function ; outer function will work as follows
calculate(3*(5/2),0){
//i=2;
outer_stack[]=[3]
sign='*'
number=2.5
//Now counting of brackets will start from i+1 which is 3.
//we will get closing bracket at j=6. and i will be set to j=6 which will be last iteration
//switch case will work and our resultant stack would be outer_stack[]=[7.5]
//after this 7.5 will be added to ans and returned.
}
The whole code will be executed according to the explanation given in the comments above.
That was all the JavaScript logic behind solving arithmetic expressions.
We can now write some HTML code to receive input from the user.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Calculator</title>
<style>
body{
text-align: center;
}
</style>
</head>
<body>
<h1>Calculator</h1>
<p>This calculator is used to evaluate <b>arithmetic expressions</b> which contains Brackets () , '+' , '-' , '*' , '/' operators</p>
<p>Input rules:</p>
<p>Division of any number by zero will result in Infinity so it should be avoided</p>
<p>Two operators together will result in wrong answer.Avoid this type of expression(1*-2).Write this instead(1*(-2)).</p>
<p>No. of opening brackets should always be equal to no. of closing brackets.((1+2)+3 is invalid expression</p>
<p>Avoid alphabets in the expression</p>
<h3>Sample valid expression:10+2*(6-(4+1)/2)+7</h3>
<input type="text" id="box" placeholder="Enter the valid expression" size="50%">
<input type="button" value="Evaluate" onclick="calculateExpression()">
<div>
<h3>The result is:</h3>
<h3 id="result"></h3>
</div>
</body>
<script>
//Javascript Code
</script>
</html>
Explore more:
Link: https://monis07.github.io/Calculator_/
GitHub Link: https://github.com/monis07/Calculator_
Subscribe to my newsletter
Read articles from Monis Azeem directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Monis Azeem
Monis Azeem
I am currently building browzerai - An AI assistant for browser. (browzerai.com). Supported by Microsoft for Startups Love to build applications using Next.js, Typescript, Express.js, MongoDB B.Tech in Information Technology from CGC Landran. monisazeem@gmail.com https://www.github.com/monis07