//invoke sample
char szBuf[1024];
m_edtInput.GetWindowText(szBuf, 1023);
double db = EvaluateExpression(szBuf);
CString strResult;
strResult.Format("%f", db);
m_edtOutput.SetWindowText(strResult);
//main function
double EvaluateExpression(LPCTSTR lpszExpression)
{
vector<CString> tokens = TokenizeExpr(lpszExpression);
for(int idxToken = 0; idxToken < tokens.size(); idxToken++)
TRACE("%s,", tokens[idxToken]);
TRACE("/n");
deque<CString> calcStack;
for (int idxToken = 0; idxToken < tokens.size(); idxToken++)
{
TRACE("/n================= Token = %s/n", tokens[idxToken]);
for(int idx = calcStack.size()-1; idx >= 0; idx--)
TRACE("%s,", calcStack[idx]);
TRACE("/n");
if ("(" == tokens[idxToken] ||
"+" == tokens[idxToken] ||
"-" == tokens[idxToken] ||
"*" == tokens[idxToken] ||
"/" == tokens[idxToken])
{
calcStack.push_front(tokens[idxToken]);
}
else if (")" == tokens[idxToken])
{
//reduce "("
CString num = calcStack.front();
calcStack.pop_front(); //pop number
calcStack.pop_front(); //pop (
IncomingNumber(calcStack, num);
}
else //numbers
{
IncomingNumber(calcStack, tokens[idxToken]);
}
for(int idx = calcStack.size()-1; idx >= 0; idx--)
TRACE("%s,", calcStack[idx]);
TRACE("/n");
}
return atof(calcStack.front());
}
vector<CString> TokenizeExpr(LPCTSTR lpszExpression)
{
vector<CString> org_tokens;
org_tokens.reserve(100); //increate speed
CString expr(lpszExpression);
expr.Replace(" ", ""); //remove space
const char *szNonDigits = "+-*/()";
LPCTSTR pData = expr.GetBuffer();
org_tokens.push_back("(");
while(*pData)
{
//if non digits, push directly
if (strchr(szNonDigits, *pData))
{
org_tokens.push_back(CString(*pData));
pData++;
}
else
{
LPCTSTR p1 = pData;
while(*p1 && !strchr(szNonDigits, *p1)) p1++;
//extrace number
char szNumber[64];
int len = p1 - pData;
strncpy(szNumber, pData, len);
szNumber[len] = 0;
org_tokens.push_back(szNumber);
pData = p1;
}
}
org_tokens.push_back(")");
expr.ReleaseBuffer();
//return org_tokens;
/* http://en.wikipedia.org/wiki/Operator-precedence_parser
Alternatives to Dijkstra's Algorithm (shunting yard algorithm)
There are alternative ways to apply operator precedence rules which do not involve the Shunting Yard algorithm.
One is to build a tree of the original expression, then apply tree rewrite rules to it.
Such trees do not necessarily need to be implemented using data structures conventionally used for trees. Instead, tokens can be stored in flat structures, such as tables, by simultaneously building a priority list which states what elements to process in which order. As an example, such an approach is used in the Mathematical Formula Decomposer [1].
Another is the algorithm used in the early FORTRAN I compiler, which is to first fully parenthesise the expression by a trivial macro replacement — inserting a number of parentheses around each operator, such that they lead to the correct precedence when parsed with a 'flat' grammar. (A hack which takes longer to explain properly than to write code for - see below!)
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[]){
int i;
printf("((((");
for(i=1;i!=argc;i++){
if( strcmp(argv[i], "^")==0) printf(")^(");
else if(strcmp(argv[i], "*")==0) printf("))*((");
else if(strcmp(argv[i], "/")==0) printf("))/((");
else if(strcmp(argv[i], "+")==0) printf(")))+(((");
else if(strcmp(argv[i], "-")==0) printf(")))-(((");
else printf("%s", argv[i]);
}
printf("))))/n");
return 0;
*/
//handle * and / precedence: add "(" and ")"
deque<CString> d1;
vector<CString>::iterator iter = org_tokens.begin();
d1.push_back("(");
d1.push_back("(");
d1.push_back("(");
while(iter != org_tokens.end())
{
if ("*" == *iter || "/" == *iter)
{
d1.push_back(")");
d1.push_back(*iter); // * or /
d1.push_back("(");
}
else if ("+" == *iter || "-" == *iter)
{
d1.push_back(")");
d1.push_back(")");
d1.push_back(*iter); // + or -
d1.push_back("(");
d1.push_back("(");
}
else
d1.push_back(*iter);
if (iter != org_tokens.end())
iter++;
}
d1.push_back(")");
d1.push_back(")");
d1.push_back(")");
return vector<CString>(d1.begin(), d1.end());
}
void IncomingNumber(deque<CString> &calcStack, CString strElement)
{
if (calcStack.size() <= 0 || calcStack.front() == "(")
{
calcStack.push_front(strElement);
return;
}
double newVal = atof(strElement);
CString top = calcStack.front();
calcStack.pop_front();
double oldVal = atof(calcStack.front());
calcStack.pop_front();
double ret = 0;
if (top == "+") ret = oldVal + newVal;
else if (top == "-") ret = oldVal - newVal;
else if (top == "*") ret = oldVal * newVal;
else if (top == "/") ret = oldVal / newVal;
else
{
AfxMessageBox("Unexpected symbol: " + top);
return;
}
CString strRet;
strRet.Format("%f", ret);
//recursive call
IncomingNumber(calcStack, strRet);
}