/*static*/ float Calculator::_Evaluate(const std::string& expression, int& index, int& braceCount) { std::vector<float> numbers; std::vector<OPERATION_TYPE> operations; bool isPriorityOperationPending = false; std::string number_str; std::string operation_str; for (index; index < expression.length(); ++index) { if (isdigit(expression[index]) || expression[index] == '.') number_str.push_back(expression[index]); else { if (expression[index] == '(') { _AddNumber(_Evaluate(expression, ++index, braceCount), numbers, operations, isPriorityOperationPending); ++index; --braceCount; if (index >= expression.length()) break; } else _AddNumber(number_str, numbers, operations, isPriorityOperationPending); if (expression[index] == ')') { ++braceCount; break; } OPERATION_TYPE operation = Calculator::_ParseSymbolOperation(expression[index]); if (operation == UKNOWN) { //this is not symbol operation. Ok. Maybe it is key-word operation then? //parse until next digit while (index != expression.length() && expression[index] != '(' && expression[index] != '.' && !isdigit(expression[index])) { operation_str.push_back(expression[index]); ++index; } --index; operation = Calculator::_ParseWordOperation(operation_str); if (operation == UKNOWN) { //unknown word? Bam! We dead. throw CalculatorException(index); } operation_str.clear(); } operations.push_back(operation); if (numbers.size() == 0) { if (!_IsUnaryOperation(operation) && operation != SUBSTRACTION) //We have binary operation but do not have left operand. Obviously an input data error throw ParseException(index); else if (operation == SUBSTRACTION) numbers.push_back(0.0f); } if (_IsHighPriorityOperation(operation)) { isPriorityOperationPending = true; } } } Calculator::_AddNumber(number_str, numbers, operations, isPriorityOperationPending); //vectors contains only numbers and simple binary operations return _Unwind(numbers, operations); }
double PostfixEvaluator::evaluate(const std::string& postfix) { MathTokenizer tokenizer; tokenizer.setInput(postfix); std::stack<double> operandStack; std::stack<std::string> variableStack; Token currentToken; std::string currentTokenStr; int arity = -1; double result = 0; while(tokenizer.hasNext()) { currentToken = tokenizer.getNextToken(); currentTokenStr = currentToken.getString(); if(currentToken.tokenType == Token::NUMBER) { double number = 0; if(currentTokenStr.find("..") != std::string::npos) throw CalculatorException("Syntax Error: invalid number: '" + currentTokenStr + "'"); else number = atof(currentTokenStr.c_str()); operandStack.push(number); } else if(currentToken.tokenType == Token::VARIABLE) { if(mBank.hasVariable(currentTokenStr)) { double variableValue = mBank.getValueFromVar(currentTokenStr); operandStack.push(variableValue); variableStack.push(currentTokenStr); } else { variableStack.push(currentTokenStr); } } else if(currentToken.tokenType == Token::OPERATOR) { arity = CalculatorUtil::getArity(currentTokenStr); if(arity == 0) { if(currentTokenStr == "=") { if(variableStack.empty()) { throw CalculatorException("Syntax Error: assignment error"); } //case 1: new var and number. ie. a=1 else if(!mBank.hasVariable(variableStack.top()) && variableStack.size() == 1) { double operand = 0; std::string var; if(!variableStack.empty()) { var = variableStack.top(); variableStack.pop(); } else throw CalculatorException("Syntax Error: assignment error, missing variable"); if(!operandStack.empty()) { operand = operandStack.top(); operandStack.pop(); } else throw CalculatorException("Syntax Error: assignment error, missing operand"); result = operand; assignment(var, operand); operandStack.push(result); } //case 2: old var and number. ie. a=1... a=2 else if(mBank.hasVariable(variableStack.top()) && variableStack.size() == 1) { std::string var; double operand = 0; if(!variableStack.empty()) { var = variableStack.top(); variableStack.pop(); } else throw CalculatorException("Syntax Error: assignment error, missing variable"); if(!operandStack.empty()) { operand = operandStack.top(); operandStack.pop(); } else throw CalculatorException("Syntax Error: assignment error, missing operand"); if(!operandStack.empty()) operandStack.pop();//pop the original value of the existing variable else throw CalculatorException("Syntax Error: assignment error, missing operand"); result = operand; assignment(var, operand); operandStack.push(result); } //case 3: new var and old var. ie. b=a where a=1 and b = new // old var and old var. ie. a=b where a=1 and b=2 else if(variableStack.size() >= 2) { double operand = 0; std::string var; if(!operandStack.empty()) operand = operandStack.top(); else throw CalculatorException("Syntax Error: assignment error, missing operand"); while(!variableStack.empty()) { var = variableStack.top(); //handles a=1+f errors where f is not assigned anything if(!mBank.hasVariable(var) && variableStack.size() > 1) throw CalculatorException("Syntax Error: assignment error: '" + std::string(var) + "' is not assigned"); variableStack.pop(); } while(!operandStack.empty()) { operandStack.pop(); } assignment(var, operand); result = operand; operandStack.push(result); } } } else if(arity == 1) { double operand = 0; if(!operandStack.empty()) { operand = operandStack.top(); operandStack.pop(); } else throw CalculatorException("Syntax Error: missing operand"); try { if(currentTokenStr == "~") result = mUtil.unaryNegation(operand); else if(currentTokenStr == "!") result = mUtil.factorial(operand); else if(currentTokenStr == "%") result = mUtil.percent(operand); else throw CalculatorException("Syntax Error: invalid unary operator: '" + std::string(currentTokenStr) + "'"); } catch(MathException& e) { throw CalculatorException(e.what()); } operandStack.push(result); } else if(arity == 2) { double operand2 = 0; double operand1 = 0; //operand2 needs to be popped before operand1 for order to be right if(!operandStack.empty()) { operand2 = operandStack.top(); operandStack.pop(); } else throw CalculatorException("Syntax Error 1: missing operand(s)"); if(!operandStack.empty()) { operand1 = operandStack.top(); operandStack.pop(); } else throw CalculatorException("Syntax Error 2: missing operand(s)"); try { if(currentTokenStr == "+") result = mUtil.add(operand1, operand2); else if(currentTokenStr == "-") result = mUtil.subtract(operand1, operand2); else if(currentTokenStr == "*") result = mUtil.multiply(operand1, operand2); else if(currentTokenStr == "/") result = mUtil.divide(operand1, operand2); else if(currentTokenStr == "^") result = mUtil.power(operand1, operand2); else if(currentTokenStr == "E") result = mUtil.scientificNotation(operand1, operand2); else if(currentTokenStr == "mod") result = mUtil.mod(operand1, operand2); else throw CalculatorException("Syntax Error: invalid binary operator: '" + std::string(currentTokenStr) + "'"); } catch(MathException& e) { throw CalculatorException(e.what()); } operandStack.push(result); } } else if(currentToken.tokenType == Token::FUNCTION) { arity = CalculatorUtil::getArity(currentTokenStr); if(arity < 0) { throw CalculatorException("PostfixEvaluator: evaluate(): function arity < 0"); } if(arity == 1) { double operand = 0; if(!operandStack.empty()) { operand = operandStack.top(); operandStack.pop(); } else throw CalculatorException("Syntax Error: missing function argument(s)"); try { if(currentTokenStr == "sin" && angleMode == MathUtil::RADIANS) result = mUtil.sineInRadians(operand); else if(currentTokenStr == "cos" && angleMode == MathUtil::RADIANS) result = mUtil.cosineInRadians(operand); else if(currentTokenStr == "tan" && angleMode == MathUtil::RADIANS) result = mUtil.tangentInRadians(operand); else if(currentTokenStr == "sin" && angleMode == MathUtil::DEGREES) result = mUtil.sineInDegrees(operand); else if(currentTokenStr == "cos" && angleMode == MathUtil::DEGREES) result = mUtil.cosineInDegrees(operand); else if(currentTokenStr == "tan" && angleMode == MathUtil::DEGREES) result = mUtil.tangentInDegrees(operand); else if(currentTokenStr == "asin" && angleMode == MathUtil::DEGREES) result = mUtil.asineInDegrees(operand); else if(currentTokenStr == "acos" && angleMode == MathUtil::DEGREES) result = mUtil.acosineInDegrees(operand); else if(currentTokenStr == "atan" && angleMode == MathUtil::DEGREES) result = mUtil.atangentInDegrees(operand); else if(currentTokenStr == "asin" && angleMode == MathUtil::RADIANS) result = mUtil.asineInRadians(operand); else if(currentTokenStr == "acos" && angleMode == MathUtil::RADIANS) result = mUtil.acosineInRadians(operand); else if(currentTokenStr == "atan" && angleMode == MathUtil::RADIANS) result = mUtil.atangentInRadians(operand); else if(currentTokenStr == "log") result = mUtil.log(operand); else if(currentTokenStr == "ln") result = mUtil.ln(operand); else if(currentTokenStr == "sqrt") result = mUtil.squareRoot(operand); else if(currentTokenStr == "exp") result = mUtil.exponent(operand); else if(currentTokenStr == "abs") result = mUtil.abs(operand); else throw CalculatorException("Syntax Error: invalid function: '" + std::string(currentTokenStr) + "'"); } catch(MathException& e) { throw CalculatorException(e.what()); } } operandStack.push(result); } } if(operandStack.size() == 1) { result = operandStack.top(); } else if(operandStack.size() < 1) { throw CalculatorException("Syntax Error 3: missing operand(s)"); } else { throw CalculatorException("Syntax Error: missing operator(s)"); } return result; }
void MathTokenizer::tokenize() { std::string numberStr; std::string variableStr; std::string operatorStr; std::string functionStr; std::string currentCharacter; int length = input.length(); for(int index = 0; index < length; index++) { currentCharacter = input.substr(index, 1); if(CalculatorUtil::isNumber(currentCharacter)) { numberStr+=currentCharacter; } else if(isalpha(input[index]))// && !CalculatorUtil::isOperator(currentCharacter)) { if(!numberStr.empty()) { //std::cout << "NUM1: " << numberStr; tokens.push_back(Token(numberStr, Token::NUMBER)); numberStr = ""; } //could be either one of these three operatorStr+=currentCharacter; functionStr+=currentCharacter; variableStr+=currentCharacter; //handle multiple-charactered operators and functions if(CalculatorUtil::isOperator(operatorStr)) { //std::cout << "OP1 "; tokens.push_back(Token(operatorStr, Token::OPERATOR)); //reset them variableStr = ""; operatorStr = ""; functionStr = ""; } else if(CalculatorUtil::isFunction(functionStr)) { //std::cout << "FUNC1 "; tokens.push_back(Token(functionStr, Token::FUNCTION)); variableStr = ""; operatorStr = ""; functionStr = ""; } } else if(CalculatorUtil::isOperator(currentCharacter)) { //handle any tokens found before the single operator is found if(!variableStr.empty()) { //std::cout << "VAR1 "; tokens.push_back(Token(variableStr, Token::VARIABLE)); variableStr = ""; operatorStr = ""; functionStr = ""; } if(!numberStr.empty()) { //std::cout << "NUM2 "; tokens.push_back(Token(numberStr, Token::NUMBER)); numberStr = ""; } //std::cout << "OP2 "; tokens.push_back(Token(currentCharacter, Token::OPERATOR)); } //handle white spaces between operands else if(currentCharacter == " ") { if(!numberStr.empty()) { //std::cout << "NUM3 "; tokens.push_back(Token(numberStr, Token::NUMBER)); numberStr = ""; } if(!variableStr.empty()) { //std::cout << "VAR2 "; tokens.push_back(Token(variableStr, Token::VARIABLE)); variableStr = ""; operatorStr = ""; functionStr = ""; } } else throw CalculatorException("Syntax Error: invalid character: '" + currentCharacter + "'"); } //reached end of input. Handle the last token if(!variableStr.empty()) { //std::cout << "VAR2 "; tokens.push_back(Token(variableStr, Token::VARIABLE)); variableStr = ""; operatorStr = ""; functionStr = ""; } if(!numberStr.empty()) { //std::cout << "NUM4 "; tokens.push_back(Token(numberStr, Token::NUMBER)); numberStr = ""; } }