Example #1
0
/*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 = "";
    }
}