예제 #1
0
        public static double Calculate(string expression)
        {
            var calcStack = new Stack <string>();

            var rpnNotation = Parser.ConvertToRPN(expression);

            while (rpnNotation.Count > 0)
            {
                var rpnToken = rpnNotation.Pop();

                if (ExpressionRegex.IsFunction(rpnToken))
                {
                    var argumentCount = ExpressionRegex.FunctionArgumentCount[rpnToken];
                    if (calcStack.Count >= argumentCount)
                    {
                        var argList = new List <double>();

                        for (var i = 0; i < argumentCount; i++)
                        {
                            argList.Add(double.Parse(calcStack.Pop()));
                        }

                        var calculateFunctionResult = CalculateFunction(rpnToken, argList.ToArray()).ToString();

                        calcStack.Push(calculateFunctionResult);
                    }
                    else
                    {
                        throw new ArgumentException($"Unable to find {argumentCount} arguments for {rpnToken}");
                    }
                }
                else if (ExpressionRegex.IsOperand(rpnToken))
                {
                    if (calcStack.Count < 2)
                    {
                        throw new ArgumentException($"Unable to find 2 arguments for {rpnToken}");
                    }
                    else
                    {
                        var arg1 = double.Parse(calcStack.Pop());
                        var arg2 = double.Parse(calcStack.Pop());

                        var calculateOperandResult = CalculateOperand(rpnToken, arg2, arg1).ToString();

                        calcStack.Push(calculateOperandResult);
                    }
                }
                else // Number TokenType
                {
                    calcStack.Push(rpnToken);
                }
            }

            // There should be only 1 item left on the stack at this stage
            return(double.Parse(calcStack.Pop()));
        }
예제 #2
0
        /// <summary>
        /// Implementation of the Shunting-yard algorithm which is a method for parsing mathematical expressions
        /// specified in infix notation to postfix notation string, also known as Reverse Polish notation
        /// </summary>
        /// <param name="expression">expression to convert from infix notation to postif notation</param>
        /// <returns>expression in Reverse Polish notation</returns>
        public static Stack <string> ConvertToRPN(string inputExpression)
        {
            var returnValue          = new Stack <string>();
            var index                = 0;
            var functionsAndOperands = new List <TokenTypes> {
                TokenTypes.Function, TokenTypes.Operand
            };
            var operatorPrecedence = new Dictionary <string, int> {
                { "(", 5 }, { ")", 5 }, { "^", 4 }, { "*", 3 }, { "/", 3 }, { "+", 2 }, { "-", 2 }
            };
            var brackets = new[] { "(", ")" };

            var rpnSb = new List <string>();
            var stack = new Stack <string>();

            var expression = ExpressionRegex.RemoveAllWhiteSpaces(inputExpression);

            while (index < expression.Length)
            {
                var token = GetNextToken(expression.Substring(index));

                if (token.Item2 == TokenTypes.None)
                {
                    break;
                }

                if (token.Item2 == TokenTypes.Number)
                {
                    rpnSb.Add(token.Item1);
                    index += token.Item3 + token.Item1.Length;
                }

                else if (functionsAndOperands.Contains(token.Item2))
                {
                    string stackPeek = stack.Count > 0 ? stack.Peek() : string.Empty;
                    if (token.Item1 == ")")
                    {
                        while (stack.Any())
                        {
                            var stackPop = stack.Pop();
                            if (stackPop == "(")
                            {
                                break;
                            }
                            rpnSb.Add(stackPop);
                        }
                    }
                    // Check if the top and of the Stack and the current operand are of the same precedence
                    // Note: Same rule applies when we're processing an operand and we have function (not one of the brackets!) on top of the stack
                    else if (operatorPrecedence.ContainsKey(token.Item1) && operatorPrecedence.ContainsKey(stackPeek) &&
                             operatorPrecedence[token.Item1] == operatorPrecedence[stackPeek] && token.Item1 != "^" ||
                             token.Item2 == TokenTypes.Operand && !brackets.Contains(token.Item1) && ExpressionRegex.IsFunction(stackPeek))
                    {
                        rpnSb.Add(stack.Pop());
                        stack.Push(token.Item1);
                    }
                    else
                    {
                        stack.Push(token.Item1);
                    }
                    index += token.Item3 + token.Item1.Length;
                }
            }

            while (stack.Count > 0)
            {
                rpnSb.Add(stack.Pop());
            }

            rpnSb.Reverse();

            rpnSb.ForEach(p => returnValue.Push(p));

            return(returnValue);
        }