/// <summary> /// This gets the static instance of Calculator for use as an interactive shell. /// </summary> public static void Run() { try { using (Calculator calculator = new Calculator()) { while (true) { Console.Write("\n> "); string userInput = Console.ReadLine(); userInput = userInput.Trim(); userInput = userInput.Replace(" ", ""); if (!String.IsNullOrEmpty(userInput)) { try { switch (userInput) { default: Equation equation = Equation.Parse(userInput); string result = Calculate(equation); Console.WriteLine("{0}", result); break; case "h": DisplayHelp(); break; case "help": DisplayHelp(); break; case "?": DisplayHelp(); break; case "q": throw new UserQuitException(); case "quit": throw new UserQuitException(); } } catch (TokenUnrecognizedException ex0) { Console.WriteLine("An internal error occurred: TokenUnrecognizedException was caught. {0}", ex0.Message); } catch (FormatException ex0) { Console.WriteLine("An internal error occurred: Probably you attempted unary operations on a binary value. {0}", ex0.Message); } catch (InvalidOperationException ex0) { Console.WriteLine("An internal error occurred: Probably the stack was empty for some reason. {0}", ex0.Message); } } } } } catch (UserQuitException) { Console.WriteLine("Thanks for using PC.NET."); } }
/// <summary> /// Parses a string equation. /// </summary> /// <param name="equation"></param> /// <returns></returns> public static Equation Parse(string userInput) { Equation equation = new Equation(); Token lastToken = Token.CreateNullToken(); char[] parts = userInput.ToCharArray(); int numParts = parts.Length; string tokenValue = String.Empty; for (int index = 0; index < numParts; index++) { tokenValue = String.Format("{0}", parts[index]); if (ConditionalOperatorToken.Matches(tokenValue) || AssignmentOperatorToken.Matches(tokenValue)) { if (tokenValue.Equals("=")) // Could be either an assignment operator or a conditional { if (String.Format("{0}", parts[index + 1]).Equals("=")) { lastToken = new ConditionalOperatorToken(String.Format("{0}=", tokenValue)); index++; } else { lastToken = new AssignmentOperatorToken(); } } else if (tokenValue.Equals("!")) // Could either be a conditional or factorial { if (String.Format("{0}", parts[index + 1]).Equals("=")) { lastToken = new ConditionalOperatorToken(String.Format("{0}=", tokenValue)); index++; } else { lastToken = new FactorialToken(); } } else // It is a conditional that is either one or two characters long, i.e. <, >, <=, >= { if (String.Format("{0}", parts[index + 1]).Equals("=")) { lastToken = new ConditionalOperatorToken(String.Format("{0}=", tokenValue)); index++; } else { lastToken = new ConditionalOperatorToken(tokenValue); } } } else if (SpecialNumberToken.Matches(tokenValue)) { int tokenIndex = index; string strPart = String.Empty; do { tokenValue = String.Format("{0}{1}", tokenValue, strPart); tokenIndex++; if (tokenIndex < numParts) { strPart = String.Format("{0}", parts[tokenIndex]); } else { strPart = String.Empty; } }while (FunctionToken.Matches(strPart)); lastToken = new SpecialNumberToken(tokenValue); index = tokenIndex - 1; } else if (NumberToken.Matches(tokenValue)) // Common case, where the token is a number { int tokenIndex = index; string strPart = String.Empty; do { tokenValue = String.Format("{0}{1}", tokenValue, strPart); tokenIndex++; if (tokenIndex < numParts) { strPart = String.Format("{0}", parts[tokenIndex]); } else { strPart = String.Empty; } }while (NumberToken.Matches(strPart)); lastToken = new NumberToken(tokenValue); index = tokenIndex - 1; } else if (AdditionToken.Matches(tokenValue)) { lastToken = new AdditionToken(); } else if (SubtractionToken.Matches(tokenValue) || NegationToken.Matches(tokenValue)) { if (lastToken.Type == TokenType.Number || lastToken.Type == TokenType.RightPerenthesis) { lastToken = new SubtractionToken(); } else { lastToken = new NegationToken(); } } else if (MultiplicationToken.Matches(tokenValue)) { lastToken = new MultiplicationToken(); } else if (DivisionToken.Matches(tokenValue)) { lastToken = new DivisionToken(); } else if (ModulusToken.Matches(tokenValue)) { lastToken = new ModulusToken(); } else if (LeftPerenthesisToken.Matches(tokenValue)) { lastToken = new LeftPerenthesisToken(); } else if (RightPerenthesisToken.Matches(tokenValue)) { lastToken = new RightPerenthesisToken(); } else if (FunctionToken.Matches(tokenValue)) { int tokenIndex = index; string strPart = String.Empty; do { tokenValue = String.Format("{0}{1}", tokenValue, strPart); tokenIndex++; if (tokenIndex < numParts) { strPart = String.Format("{0}", parts[tokenIndex]); } else { strPart = String.Empty; } }while (FunctionToken.Matches(strPart)); lastToken = new FunctionToken(tokenValue); index = tokenIndex - 1; } else if (BooleanToken.Matches(tokenValue)) { int tokenIndex = index; string strPart = String.Empty; do { tokenValue = String.Format("{0}{1}", tokenValue, strPart); tokenIndex++; if (tokenIndex < numParts) { strPart = String.Format("{0}", parts[tokenIndex]); } else { strPart = String.Empty; } }while (BooleanToken.Matches(strPart)); switch (tokenValue) { case "True": lastToken = new BooleanToken("True"); break; case "False": lastToken = new BooleanToken("False"); break; } index = tokenIndex - 1; } else if (FactorialToken.Matches(tokenValue)) { lastToken = new FactorialToken(); } else if (PowerToken.Matches(tokenValue)) { lastToken = new PowerToken(); } else if (BitwiseOperationToken.Matches(tokenValue)) { lastToken = new BitwiseOperationToken(tokenValue); } else // This case is that we are probably dealing with a variable, but for now I'm going to throw an exception. { // TODO: fix this to handle variables. throw new TokenUnrecognizedException(tokenValue); } if (!lastToken.Equals(Token.CreateNullToken())) { AddComponent(lastToken); } } // Need to transform the equation into a postfix equation so that I can evaluate it more easily. _tokens = ReverseTokens(_tokens); _tokens = PostfixateEquation(_tokens); return(equation); }
private static string Calculate(Equation equation) { string calculationResult = String.Empty; Stack <Token> unusedTokens = new Stack <Token>(); while (equation.Length > 0) { switch (equation.Peek().Type) { case TokenType.BooleanToken: switch (equation.Peek().Value) { case "True": calculationResult = "1"; break; case "False": calculationResult = "0"; break; } equation.Pop(); unusedTokens.Push(new NumberToken(calculationResult)); break; case TokenType.ConditionalOperator: try { string condRight = unusedTokens.Pop().Value; string condLeft = unusedTokens.Pop().Value; switch (equation.Peek().Value) { case "<": calculationResult = Evaluator.LessThan(condLeft, condRight); break; case ">": calculationResult = Evaluator.GreaterThan(condLeft, condRight); break; case "!=": calculationResult = Evaluator.NotEqual(condLeft, condRight); break; case "<=": calculationResult = Evaluator.LesThanOrEqual(condLeft, condRight); break; case ">=": calculationResult = Evaluator.GreaterThanOrEqual(condLeft, condRight); break; case "==": calculationResult = Evaluator.AreEqual(condLeft, condRight); break; default: Console.WriteLine("Somehow the conditional {0} got through your filters. This is an internal error.", equation.Peek().Value); throw new TokenUnrecognizedException(equation.Peek().Value); } } catch (InvalidOperationException ex) { Console.WriteLine("Could not evaluate conditional because not enough operands were present. This may be an internal error. {0}", ex.Message); } equation.Pop(); unusedTokens.Push(new BooleanToken(calculationResult)); break; case TokenType.SpecialNumber: switch (equation.Peek().Value.ToLower()) { case "e": calculationResult = Evaluator.GetE(); break; case "pi": calculationResult = Evaluator.GetPi(); break; case "$": calculationResult = PCState.Instance["$"] as String; if (calculationResult.Equals("True")) { calculationResult = "1"; } else if (calculationResult.Equals("False")) { calculationResult = "0"; } break; } equation.Pop(); unusedTokens.Push(new NumberToken(calculationResult)); break; case TokenType.Number: unusedTokens.Push(equation.Pop()); break; case TokenType.Negation: equation.Pop(); try { string right = unusedTokens.Pop().Value; string result = Evaluator.Negate(right); unusedTokens.Push(new NumberToken(result)); } catch (InvalidOperationException ex) { Console.WriteLine("Could not evaluate negation because not enough operands were present. This may be an internal error. {0}", ex.Message); } break; case TokenType.Subtraction: equation.Pop(); try { string subtrahend = unusedTokens.Pop().Value; string minuend = unusedTokens.Pop().Value; string result = Evaluator.Subtract(minuend, subtrahend); unusedTokens.Push(new NumberToken(result)); } catch (InvalidOperationException ex) { Console.WriteLine("Could not evaluate subtraction because not enough operands were present. This may be an internal error. {0}", ex.Message); } break; case TokenType.Addition: equation.Pop(); try { string leftAddend = unusedTokens.Pop().Value; string rightAddend = unusedTokens.Pop().Value; string result = Evaluator.Add(leftAddend, rightAddend); unusedTokens.Push(new NumberToken(result)); } catch (InvalidOperationException ex) { Console.WriteLine("Could not evaluate addition because not enough operands were present. This may be an internal error. {0}", ex.Message); } break; case TokenType.Division: equation.Pop(); try { string divisor = unusedTokens.Pop().Value; string dividend = unusedTokens.Pop().Value; string result = Evaluator.Divide(dividend, divisor); unusedTokens.Push(new NumberToken(result)); } catch (InvalidOperationException ex) { Console.WriteLine("Could not evaluate division because not enough operands were present. This may be an internal error. {0}", ex.Message); } break; case TokenType.Modulus: equation.Pop(); try { string right = unusedTokens.Pop().Value; string left = unusedTokens.Pop().Value; string result = Evaluator.Modulus(left, right); unusedTokens.Push(new NumberToken(result)); } catch (InvalidOperationException ex) { Console.WriteLine("Could not evaluate modulus because not enough operands were present. This may be an internal error. {0}", ex.Message); } break; case TokenType.Multiplication: equation.Pop(); try { string multiplicand = unusedTokens.Pop().Value; string multiplier = unusedTokens.Pop().Value; string result = Evaluator.Multiply(multiplicand, multiplier); unusedTokens.Push(new NumberToken(result)); } catch (InvalidOperationException ex) { Console.WriteLine("Could not evaluate multiplication because not enough operands were present. This may be an internal error. {0}", ex.Message); } break; case TokenType.Function: Token function = equation.Pop(); try { string localResult = String.Empty; string right = String.Empty; string left = String.Empty; switch (function.Value.ToLower()) { case "sin": right = unusedTokens.Pop().Value; localResult = Evaluator.Sine(right); break; case "cos": right = unusedTokens.Pop().Value; localResult = Evaluator.Cosine(right); break; case "tan": right = unusedTokens.Pop().Value; localResult = Evaluator.Tangent(right); break; case "arcsin": right = unusedTokens.Pop().Value; localResult = Evaluator.InverseSine(right); break; case "arccos": right = unusedTokens.Pop().Value; localResult = Evaluator.InverseCosine(right); break; case "arctan": right = unusedTokens.Pop().Value; localResult = Evaluator.InverseTangent(right); break; case "log": right = unusedTokens.Pop().Value; localResult = Evaluator.Logarithm(String.Format("{0}", 10), right); break; case "ln": right = unusedTokens.Pop().Value; localResult = Evaluator.Logarithm(String.Format("{0}", System.Math.E), right); break; case "bin": right = unusedTokens.Pop().Value; localResult = Evaluator.ToBinary(right); break; case "hex": right = unusedTokens.Pop().Value; localResult = Evaluator.ToHexadecimal(right); break; case "oct": right = unusedTokens.Pop().Value; localResult = Evaluator.ToOctal(right); break; case "mod": right = unusedTokens.Pop().Value; left = unusedTokens.Pop().Value; localResult = Evaluator.Modulus(left, right); break; case "round": right = unusedTokens.Pop().Value; localResult = Evaluator.Round(right); break; case "ceil": right = unusedTokens.Pop().Value; localResult = Evaluator.Ceiling(right); break; case "floor": right = unusedTokens.Pop().Value; localResult = Evaluator.Floor(right); break; case "tanh": right = unusedTokens.Pop().Value; localResult = Evaluator.HyperbolicTangent(right); break; case "sinh": right = unusedTokens.Pop().Value; localResult = Evaluator.HyperbolicSine(right); break; case "cosh": right = unusedTokens.Pop().Value; localResult = Evaluator.HyperbolicCosine(right); break; case "rad": right = unusedTokens.Pop().Value; localResult = Evaluator.ToRadians(right); break; case "deg": right = unusedTokens.Pop().Value; localResult = Evaluator.ToDegrees(right); break; default: throw new InvalidOperationException(function.Value.ToLower()); } unusedTokens.Push(new NumberToken(localResult)); } catch (InvalidOperationException ex) { Console.WriteLine("Could not evaluate function because function is not implemented. This may be an internal error. {0}", ex.Message); } break; case TokenType.Factorial: equation.Pop(); try { string left = unusedTokens.Pop().Value; string localResult = Evaluator.Factorial(left); unusedTokens.Push(new NumberToken(localResult)); } catch (InvalidOperationException ex) { Console.WriteLine("Could not evaluate factorial because not enough operands were present. This may be an internal error. {0}", ex.Message); } break; case TokenType.Power: equation.Pop(); try { string pow = unusedTokens.Pop().Value; string bas = unusedTokens.Pop().Value; string localResult = Evaluator.Power(bas, pow); unusedTokens.Push(new NumberToken(localResult)); } catch (InvalidOperationException ex) { Console.WriteLine("Could not evaluate power function because not enough operands were present. This may be an internal error. {0}", ex.Message); } break; case TokenType.BitwiseOperation: string bitwiseOperator = equation.Pop().Value; try { string right = unusedTokens.Pop().Value; string left = unusedTokens.Pop().Value; string localResult = String.Empty; switch (bitwiseOperator) { case "&": localResult = Evaluator.BitwiseAnd(left, right); break; case "|": localResult = Evaluator.BitwiseOr(left, right); break; case "%": localResult = Evaluator.BitwiseXor(left, right); break; } unusedTokens.Push(new NumberToken(localResult)); } catch (InvalidOperationException ex) { Console.WriteLine("Could not evaluate bitwise operation because not enough operands were present. This may be an internal error. {0}", ex.Message); } break; default: throw new TokenUnrecognizedException(String.Format("{0}", equation.Peek())); } } calculationResult = unusedTokens.Pop().Value; PCState.Instance["$"] = calculationResult; return(calculationResult); }