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())); }
/// <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); }