Пример #1
0
        /// <summary>
        /// Evaluate expression with Dijkstra's Shunting Yard Algorithm
        /// </summary>
        /// <returns>result of calculations</returns>
        public double?Calculate()
        {
            if (_input == null)
            {
                throw new ArgumentException();
            }

            Stack <Op> operatorStack = new Stack <Op>();
            Queue <Op> outputQueue   = new Queue <Op>();

            // Let's split the input string into a token list
            List <String> tokenList = Regex.Split(_input, TokenSplitRegex).Select(t => t.Trim()).Where(t => !String.IsNullOrEmpty(t)).ToList();

            for (int tokenNum = 0; tokenNum < tokenList.Count(); ++tokenNum)
            {
                double?   tmpValue;
                String    token     = tokenList[tokenNum];
                TokenType tokenType = GetTokenType(token, out tmpValue);

                // Handle this token and insert into the correct queue or stack
                switch (tokenType)
                {
                case TokenType.Value:
                    if (tmpValue.HasValue)
                    {
                        outputQueue.Enqueue(new Operand(tmpValue.Value));
                    }
                    else
                    {
                        throw new ArgumentException("Unknown operand " + token);
                    }
                    break;

                case TokenType.Operator:
                    Operator newOperator = GetOperator(token, (tokenNum == 0 || tokenList[tokenNum - 1] == "("));
                    if (operatorStack.Count > 0)
                    {
                        Op topOperator = operatorStack.Peek();
                        if (topOperator is Operator)
                        {
                            if (newOperator.Precedence <= ((Operator)topOperator).Precedence)
                            {
                                outputQueue.Enqueue(operatorStack.Pop());
                            }
                        }
                    }
                    operatorStack.Push(newOperator);
                    break;

                case TokenType.LeftParenthesis:
                    operatorStack.Push(new LeftParenthesis());
                    break;

                case TokenType.RightParenthesis:
                    // Handle all operators in the stack (i.e. move them to the outputQueue)
                    // until we find the LeftParenthesis
                    while (!(operatorStack.Peek() is LeftParenthesis))
                    {
                        outputQueue.Enqueue(operatorStack.Pop());
                    }
                    operatorStack.Pop();
                    break;

                case TokenType.Function:
                    if ((tokenList.Count >= tokenNum + 1) && (tokenList[tokenNum + 1] == "("))
                    {
                        Function.FunctionTypes type = Function.GetFunctionType(token);
                        if (type == Function.FunctionTypes.UNKNOWN)
                        {
                            throw new ArgumentException("Unknown function " + token);
                        }
                        operatorStack.Push(new Function(type));
                    }
                    break;
                }

                // If we don't find any token between a value and parenthesis, automatically
                // add a multiply sign
                if (tokenType == TokenType.Value || tokenType == TokenType.RightParenthesis)
                {
                    if (tokenNum < tokenList.Count() - 1)
                    {
                        String    nextToken     = tokenList[tokenNum + 1];
                        TokenType nextTokenType = GetTokenType(nextToken, out tmpValue);
                        if (nextTokenType != TokenType.Operator && nextTokenType != TokenType.RightParenthesis)
                        {
                            tokenList.Insert(tokenNum + 1, "*");
                        }
                    }
                }
            }

            // Move all operators into the outputqueue
            while (operatorStack.Count > 0)
            {
                Op operand = operatorStack.Pop();
                if (operand is LeftParenthesis || operand is RightParenthesis)
                {
                    throw new ArgumentException("Mismatched parentheses");
                }

                outputQueue.Enqueue(operand);
            }

            // Now we have the expression in reverse polish notation and it's easy to calculate
            // Step through the outputQueue and calculate the result
            Stack <Operand> outputStack = new Stack <Operand>();

            while (outputQueue.Count > 0)
            {
                Op currentOp = outputQueue.Dequeue();

                if (currentOp is Operand)
                {
                    outputStack.Push((Operand)currentOp);
                }
                else if (currentOp is Operator)
                {
                    Operator currentOperator = (Operator)currentOp;
                    currentOperator.Execute(outputStack, this.Mode);
                }
            }

            // If we haven't got only one answer, the formula is invalid, return that.
            if (outputStack.Count != 1)
            {
                throw new ArgumentException("Invalid formula");
            }

            // Pop and return the result
            return(outputStack.Pop().Value);
        }