#include "formula.h" #include subExceptionDef( ParseException ); Formula::Formula() { hVars["pi"] = M_PI; hVars["e"] = M_E; hFunc["sin"] = FuncSin(); } Formula::~Formula() { } double Formula::run( char *sFormula ) { for(;;) { uint8_t tNum = nextToken( &sFormula ); if( tNum == symEOS ) break; else if( tNum == symSubtract ) { tNum = nextToken( &sFormula ); if( tNum != symNumber ) throw ParseException("Unary minus must be followed by a number, " "variable, function, or parenthesis."); sValue.top() = -sValue.top(); } else if( tNum == symOpenParen ) { sOper.push( tNum ); continue; } oppart: uint8_t tOpr = nextToken( &sFormula ); if( tOpr == symEOS ) { //printf("EOS "); reduce(); return sValue.top(); break; } if( !sOper.empty() && getPrec( sOper.top() ) > getPrec( tOpr ) ) { reduce(); } if( tOpr != symCloseParen ) { sOper.push( tOpr ); } else { reduce( true ); goto oppart; } } return sValue.top(); } void Formula::reduce( bool bCloseParen ) { while( !sOper.empty() ) { uint8_t nOpr = sOper.top(); if( nOpr == symOpenParen ) { //printf("Found ( stopping reduction.\n"); if( bCloseParen == true ) sOper.pop(); return; } sOper.pop(); double dTop = sValue.top(); sValue.pop(); switch( nOpr ) { case symAdd: //printf("%f + %f = %f\n", sValue.top(), dTop, sValue.top()+dTop ); sValue.top() += dTop; break; case symSubtract: //printf("%f - %f = %f\n", sValue.top(), dTop, sValue.top()-dTop ); sValue.top() -= dTop; break; case symMultiply: //printf("%f * %f = %f\n", sValue.top(), dTop, sValue.top()*dTop ); sValue.top() *= dTop; break; case symDivide: //printf("%f / %f = %f\n", sValue.top(), dTop, sValue.top()/dTop ); sValue.top() /= dTop; break; case symExponent: //printf("%f ^ %f = %f\n", sValue.top(), dTop, pow(sValue.top(),dTop) ); sValue.top() = pow( sValue.top(), dTop ); break; case symModulus: //printf("%f %% %f = %f\n", sValue.top(), dTop, fmod(sValue.top(),dTop) ); sValue.top() = fmod( sValue.top(), dTop ); break; } } if( bCloseParen == true ) { throw ParseException("Close-paren found without matching open-paren."); } } uint8_t Formula::getPrec( uint8_t nOper ) { switch( nOper ) { case symNumber: case symVariable: case symOpenParen: case symCloseParen: return 0; case symAdd: case symSubtract: return 1; case symMultiply: case symDivide: case symModulus: return 2; case symExponent: return 3; default: return 0; } } uint8_t Formula::nextToken( char **sBuf ) { for(;;) { char cbuf = **sBuf; ++(*sBuf); switch( cbuf ) { case '+': return symAdd; case '-': return symSubtract; case '*': return symMultiply; case '/': return symDivide; case '^': return symExponent; case '%': return symModulus; case '(': return symOpenParen; case ')': return symCloseParen; case ' ': case '\t': case '\n': case '\r': break; case '\0': return symEOS; default: if( cbuf == '.' || (cbuf >= '0' && cbuf <= '9') ) { char num[50]={cbuf}; int nPos = 1; bool bDot = false; for(;;) { cbuf = **sBuf; if( cbuf == '.' ) { if( bDot == false ) bDot = true; else throw ParseException( "Numbers cannot have more than one " ". in them." ); } if( cbuf == '.' || (cbuf >= '0' && cbuf <= '9') ) { num[nPos++] = cbuf; } else { num[nPos] = '\0'; sValue.push( strtod( num, NULL ) ); return symNumber; } ++(*sBuf); } } else if( (cbuf >= 'a' && cbuf <= 'z') || (cbuf >= 'A' && cbuf <= 'Z') || (cbuf == '_') ) { char tok[50]={cbuf}; int nPos = 1; for(;;) { cbuf = **sBuf; if( (cbuf >= 'a' && cbuf <= 'z') || (cbuf >= 'A' && cbuf <= 'Z') || (cbuf >= '0' && cbuf <= '9') || cbuf == '_' || cbuf == '.' || cbuf == ':' ) { tok[nPos++] = cbuf; } else { tok[nPos] = '\0'; //printf("Checking variable \"%s\"\n", tok ); try { sValue.push( hVars[tok] ); return symNumber; } catch( HashException &e ) { throw ParseException( "No variable named \"%s\" exists.", tok ); } } ++(*sBuf); } } break; } } }