# Math expression evaluator

It is my second attempt to make a math expression evaluator.

To evaluate an expression, I created a class that works like this:

-it removes all the blank spaces

-if brackets are present, it processes them by creating another evaluator with the expression inside those brackets

-it gets all the numbers and does first all the exponentiations, then the multiplications/divisions and finally sums all the numbers

For example, let's say I have the expression $$3*(3+4)^2+3*(5+6)$$

-replaces (3+4) and (5+6) with 7 and 11 $$3*7^2+3*11$$

-does 7^2 $$3*49+3*11$$

-does the multiplications $$147+33$$

-sums all the numbers $$180$$

If you know if there's something wrong or to optimize I'd really appreciate it

MathSolver.h

#include <vector>
#include <string>
#include <algorithm>
#include <sstream>
#include <iostream>
#include <map>
#include <functional>

void changeCharacters(std::string chars, std::string toChange, std::string &str)
{
int i = -1;
std::for_each(str.begin(), str.end(), [chars, toChange, &i, &str](char c)
{
i++;
for (auto &j : chars)
{
if (c == j)
{
str.erase(str.begin() + i);
str.insert(i, toChange);
break;
}
}
});
}

class MathSolver
{
private:
std::string exp;
std::vector<double> nums;
double result;
std::function<bool(char)> isPlusMinus = [](char c) {return c == '+' || c == '-'; };
std::function<bool(char)> isMultDiv = [](char c) {return c == '*' || c == '/'; };

public:
MathSolver(std::string exp)
{
exp.erase(std::remove(exp.begin(), exp.end(), ' '), exp.end());
if (exp.find_first_of("+-") != 0)
exp.insert(0, "+");
for (int i = 0; i < exp.length(); i++)
{
if (isPlusMinus(exp[i]))
exp.insert(i + 1, "1*");
}
changeCharacters("[{", "(", exp);
changeCharacters("]}", ")", exp);
changeCharacters(":", "/", exp);
this->exp = exp;
this->processBrackets();
this->parse();
}
void countBracks(std::vector< std::pair<int, int>> &bracks)
{
int parOC = 0;
for (int i = 0; i < exp.length(); i++)
{
if (exp[i] == '(')
{
if (parOC == 0)
bracks.push_back(std::make_pair(i, 0));
parOC++;
}
else if (exp[i] == ')')
{
parOC--;
if (parOC == 0)
bracks[bracks.size() - 1].second = i;
}
}
}
void processBrackets()
{
std::vector< std::pair<int, int>> bracks;
countBracks(bracks);
int count = bracks.size();
for (int i = 0; i < count; i++)
{
std::pair<int, int> j = bracks[0];
MathSolver solve(exp.substr(j.first + 1, j.second - 1 - j.first));
double res = solve.getResult();
std::stringstream ss;
ss << res;
exp.erase(exp.begin() + j.first, exp.begin() + j.second + 1);
exp.insert(j.first, ss.str());

bracks.clear();
countBracks(bracks);
}
}
void parse()
{
std::function<void(double&, std::istringstream&)> searchPow = [](double &num, std::istringstream &iss)
{
if (iss.peek() == '^')
{
char tmp2;
double tmp3;
iss >> tmp2 >> tmp3;
num = pow(num, tmp3);

}
};
double num;
char tmp;
std::istringstream iss(exp);
while ((int)iss.tellg() != EOF)
{
if (isPlusMinus(iss.peek()) && isdigit(exp[(int)iss.tellg() + 1]))
{
iss >> num;
searchPow(num, iss);
nums.push_back(num);
}
else if (isMultDiv(iss.peek()) && (isdigit(exp[(int)iss.tellg() + 1]) || isdigit(exp[(int)iss.tellg() + 2])))
{
iss >> tmp >> num;
searchPow(num, iss);
nums.push_back(num);

if (tmp == '/')
nums[nums.size() - 1] = 1 / nums[nums.size() - 1];
nums[nums.size() - 1] *= nums[nums.size() - 2];
nums[nums.size() - 2] = 0;
}
}
nums.erase(remove(nums.begin(), nums.end(), 0), nums.end());

for (auto i : nums)
result += i;
}
double getResult()
{
return result;
}
};
• There is an entire field in computer science dedicated to this, i. e. to formally describe the syntax of expressions or entire languages and to implement its parsing, execution and/or compilation. The usual approach is define the syntax, implement a tokenizer (splits string into the numbers and operators) and implement the parsing (checks syntax and immediately evaluate expression). The easiest to understand parser is the recursive descent parser.
– Codo
Nov 25, 2016 at 12:43
• You can also use the shunting yard algorithm, which is specifically designed for such math expressions. It might be easier to build a fully fledged parser :) Nov 25, 2016 at 17:42

1. use std::isspace for whitespace detection. In that case you can do

str.erase(std::remove_if(str.begin(), str.end(), std::isspace), str.end());

2. Don't use unnecessary abbreviations: bracks vs brackets is not really a gain at all.

3. You should use a std::stack for bracket checking. So whenever you encounter an opening bracket you put it on the stack and check the next one.

4. You might want to get a separate function for extraction of the inner bracket content such like this:

std:string innerBracketString(const std::string& expr,
size_t& startPos,
size_t& endPos) {
startPos = expr.find_first_of("([{", endPos)+1;
endPos   = expr.find_first_of(")]}", startPos);
return expr.substr(startPos, endPos - startPos);
}