Math expression parser in C++

Back to Home

Introduction:

mathparser is a simple c++ program to parse math expressions.

The program is a modified version of math expression parser presented in the book: "C++ The Complete Reference" by H.Schildt.

It supports operators: +-*/^()

It supports math functions: SIN, COS, TAN, ASIN, ACOS, ATAN, SINH, COSH, TANH, ASINH, ACOSH, ATANH, LN, LOG, EXP, SQRT, SQR, ROUND, INT.

It supports variables A to Z.

Sample:

25*3+1.5*(-2^4*log(30)/3)
x=3
y=4
r=sqrt(x^2+y^2)
t=atan(y/x)

About:

mathparser version 1.0 by Hamid Soltani. (gmail: hsoltanim)

Code:

Download mathparser.cpp

/******************************************************************

Introduction:
mathparser is a simple c++ program to parse math expressions.

The program is a modified version of math expression parser 
presented in the book : "C++ The Complete Reference" by H.Schildt.

It supports operators: + - * / ^ ( )

It supports math functions : SIN, COS, TAN, ASIN, ACOS, ATAN, SINH, 
COSH, TANH, ASINH, ACOSH, ATANH, LN, LOG, EXP, SQRT, SQR, ROUND, INT.

It supports variables A to Z.

Sample:
25 * 3 + 1.5*(-2 ^ 4 * log(30) / 3)
x = 3
y = 4
r = sqrt(x ^ 2 + y ^ 2)
t = atan(y / x)

mathparser version 1.0 by Hamid Soltani. (gmail: hsoltanim)
Last modified: Aug. 2016.

*******************************************************************/

#include "stdafx.h"

#include < iostream >
#include < cstdlib >
#include < cctype >
#include < cstring >
#include < math.h > 

#define PI 3.14159265358979323846 

using namespace std;

enum types { DELIMITER = 1, VARIABLE, NUMBER, FUNCTION };
const int NUMVARS = 26;
class parser {
	char *exp_ptr; // points to the expression
	char token[256]; // holds current token
	char tok_type; // holds token's type
	double vars[NUMVARS]; // holds variable's values
	void eval_exp1(double &result);
	void eval_exp2(double &result);
	void eval_exp3(double &result);
	void eval_exp4(double &result);
	void eval_exp5(double &result);
	void eval_exp6(double &result);
	void get_token();
public:
	parser();
	double eval_exp(char *exp);
	char errormsg[64];
};
// Parser constructor.
parser::parser()
{
	int i;
	exp_ptr = NULL;
	for (i = 0; i < NUMVARS; i++)
		vars[i] = 0.0;
	errormsg[0] = '\0';
}
// Parser entry point.
double parser::eval_exp(char *exp)
{
	errormsg[0] = '\0';
	double result;
	exp_ptr = exp;
	get_token();
	if (!*token) 
	{
		strcpy(errormsg, "No Expression Present"); // no expression present
		return (double)0;
	}
	eval_exp1(result);
	if (*token) // last token must be null
		strcpy(errormsg, "Syntax Error");
	return result;
}
// Process an assignment.
void parser::eval_exp1(double &result)
{
	int slot;
	char temp_token[80];
	if (tok_type == VARIABLE) 
	{
		// save old token
		char *t_ptr = exp_ptr;
		strcpy(temp_token, token);
		// compute the index of the variable
		slot = *token - 'A';
		get_token();
		if (*token != '=') 
		{
			exp_ptr = t_ptr; // return current token
			strcpy(token, temp_token); // restore old token
			tok_type = VARIABLE;
		}
		else {
			get_token(); // get next part of exp
			eval_exp2(result);
			vars[slot] = result;
			return;
		}
	}
	eval_exp2(result);
}
// Add or subtract two terms.
void parser::eval_exp2(double &result)
{
	register char op;
	double temp;
	eval_exp3(result);
	while ((op = *token) == '+' || op == '-')
	{
		get_token();
		eval_exp3(temp);
		switch (op) 
		{
		case '-':
			result = result - temp;
			break;
		case '+':
			result = result + temp;
			break;
		}
	}
}
// Multiply or divide two factors.
void parser::eval_exp3(double &result)
{
	register char op;
	double temp;
	eval_exp4(result);
	while ((op = *token) == '*' || op == '/') 
	{
		get_token();
		eval_exp4(temp);
		switch (op) 
		{
		case '*':
			result = result * temp;
			break;
		case '/':
			result = result / temp;
			break;
		}
	}
}
// Process an exponent.
void parser::eval_exp4(double &result)
{
	double temp;
	eval_exp5(result);
	while (*token == '^')
	{
		get_token();
		eval_exp5(temp);
		result = pow(result, temp);
	}
}
// Evaluate a unary + or -.
void parser::eval_exp5(double &result)
{
	register char op;
	op = 0;
	if ((tok_type == DELIMITER) && *token == '+' || *token == '-')
	{
		op = *token;
		get_token();
	}
	eval_exp6(result);
	if (op == '-')
		result = -result;
}
// Process a function, a parenthesized expression, a value or a variable
void parser::eval_exp6(double &result)
{
	bool isfunc = (tok_type == FUNCTION);
	char temp_token[80];
	if (isfunc)
	{
		strcpy(temp_token, token);
		get_token();
	} 
	if ((*token == '(')) 
	{
		get_token();
		eval_exp2(result);
		if (*token != ')')
			strcpy(errormsg, "Unbalanced Parentheses");
		if (isfunc)
		{
			if (!strcmp(temp_token, "SIN"))
				result = sin(PI / 180 * result);
			else if (!strcmp(temp_token, "COS"))
				result = cos(PI / 180 * result);
			else if (!strcmp(temp_token, "TAN"))
				result = tan(PI / 180 * result);
			else if (!strcmp(temp_token, "ASIN"))
				result = 180 / PI*asin(result);
			else if (!strcmp(temp_token, "ACOS"))
				result = 180 / PI*acos(result);
			else if (!strcmp(temp_token, "ATAN"))
				result = 180 / PI*atan(result);
			else if (!strcmp(temp_token, "SINH"))
				result = sinh(result);
			else if (!strcmp(temp_token, "COSH"))
				result = cosh(result);
			else if (!strcmp(temp_token, "TANH"))
				result = tanh(result);
			else if (!strcmp(temp_token, "ASINH"))
				result = asinh(result);
			else if (!strcmp(temp_token, "ACOSH"))
				result = acosh(result);
			else if (!strcmp(temp_token, "ATANH"))
				result = atanh(result);
			else if (!strcmp(temp_token, "LN"))
				result = log(result);
			else if (!strcmp(temp_token, "LOG"))
				result = log10(result);
			else if (!strcmp(temp_token, "EXP"))
				result = exp(result);
			else if (!strcmp(temp_token, "SQRT"))
				result = sqrt(result);
			else if (!strcmp(temp_token, "SQR"))
				result = result*result;
			else if (!strcmp(temp_token, "ROUND"))
				result = round(result);
			else if (!strcmp(temp_token, "INT"))
				result = floor(result);
			else
				strcpy(errormsg, "Unknown Function");
		}
		get_token();
	}
	else 
		switch (tok_type)
		{
		case VARIABLE:
			result = vars[*token - 'A'];
			get_token();
			return;
		case NUMBER:
			result = atof(token);
			get_token();
			return;
		default:
			strcpy(errormsg, "Syntax Error");
		}
}
// Obtain the next token.
void parser::get_token()
{
	register char *temp;
	tok_type = 0;
	temp = token;
	*temp = '\0';
	if (!*exp_ptr)  // at end of expression
		return;
	while (isspace(*exp_ptr))  // skip over white space
		++exp_ptr; 
	if (strchr("+-*/%^=()", *exp_ptr)) 
	{
		tok_type = DELIMITER;
		*temp++ = *exp_ptr++;  // advance to next char
	}
	else if (isalpha(*exp_ptr)) 
	{
		while (!strchr(" +-/*%^=()\t\r", *exp_ptr) && (*exp_ptr))
			*temp++ = toupper(*exp_ptr++);
		while (isspace(*exp_ptr))  // skip over white space
			++exp_ptr;
		tok_type = (*exp_ptr == '(') ? FUNCTION : VARIABLE;
	}
	else if (isdigit(*exp_ptr) || *exp_ptr == '.')
	{
		while (!strchr(" +-/*%^=()\t\r", *exp_ptr) && (*exp_ptr))
			*temp++ = toupper(*exp_ptr++);
		tok_type = NUMBER;
	}
	*temp = '\0';
	if ((tok_type == VARIABLE) && (token[1]))
		strcpy(errormsg, "Only first letter of variables is considered");
}

int main()
{
	char expstr[256];
	parser ob;
	cout << "Math expression parser. Enter a blank line to stop.\n\n";
	do
	{
		cout << "Enter expression: ";
		cin.getline(expstr, 255);
		double ans = ob.eval_exp(expstr);
		if (*ob.errormsg)
			cout << "Error: " << ob.errormsg << "\n\n";
		else
			cout << "Answer: " << ans << "\n\n";
	} while (*expstr);
	return 0;
}