package edu.unca.cs.csci202; import java.util.* ; /** * * @author J Dean Brock */ public class SimpleEvalExpr { // A simple tokenizer // Parse an ordinary expression, // such as "(13+17)*19+21" // into an infix list of tokens // such as ( 13 + 17 ) * 19 + 21 private static Deque string2infix(String expr) throws IllegalArgumentException { Deque rpnExp = new LinkedList<>() ; int i = 0 ; while (i infix) throws IllegalArgumentException { // The opCountStack and valCountStack are used to keep // up with some number of operators and values between parentheses Deque opCountStack = new LinkedList<>() ; Deque valCountStack = new LinkedList<>() ; opCountStack.push(0); valCountStack.push(0); // Iterators are so useful Iterator revStack = infix.descendingIterator() ; while (revStack.hasNext()) { Object token = revStack.next() ; // used so much, let's get their values now int presOpCount = opCountStack.peek() ; int presValCount = valCountStack.peek() ; if (token instanceof Character) { char nextC = (Character)token ; if (nextC == '(') { // Left parenthesis // legit only if numbers of values and operators are equal if (presValCount != presOpCount) { throw new IllegalArgumentException("Problem at (") ; } // Push new ( level opCountStack.push(0) ; valCountStack.push(0) ; } else if (nextC == ')') { // Right parenthesis // legit only if one more value than operator if (presValCount != presOpCount+1) { throw new IllegalArgumentException("Problem at )") ; } // pop the ( level, return to old ( opCountStack.pop() ; valCountStack.pop() ; try { // Increment value count at present level valCountStack.push(valCountStack.pop()+1) ; } catch (NoSuchElementException nsee) { // legit only if there was something to pop! throw new IllegalArgumentException("Unpaired )") ; } } else /* nextC is '+' or '*' */ { // Operator // legit only if one more value than operator if (presValCount != presOpCount + 1) { throw new IllegalArgumentException( "Problem at " + nextC) ; } opCountStack.push(opCountStack.pop()+1) ; } } else /* must be an Integer */ { // Value // legit only if numbers of values and operators are equal if (presValCount != presOpCount) { throw new IllegalArgumentException( "Problem at " + (int)token) ; } valCountStack.push(valCountStack.pop()+1) ; } } // Final checks if (valCountStack.size() != 1) { // legit only if there is only one level throw new IllegalArgumentException("Unpaired (") ; } if (valCountStack.peek() != opCountStack.peek()+1) { // legit only if one more value than operator throw new IllegalArgumentException("Bad format") ; } } // Transform an infix list // such as ( 13 + 17 ) * 19 + 21 // into a postfix list // such as 13 17 + 19 * 21 + // The algorithm on page 375 to 378 has a similar function private static Deque infix2postfix(Deque infix) { Deque postfix = new LinkedList<>() ; // Pending operator stack Deque pendingOps = new LinkedList<>() ; Iterator revStack = infix.descendingIterator() ; while (revStack.hasNext()) { Object token = revStack.next() ; if (token instanceof Character) { char nextC = (Character)token ; if (nextC == '(') { // Left parenthesis pendingOps.push('(') ; } else if (nextC == ')') { // Right parenthesis // Move pending operators onto the postfix stack // until a right parenthesis is found while(pendingOps.peek() != '(') { postfix.push(pendingOps.pop()) ; } // Remove right parenthesis from pending operator stack pendingOps.pop() ; } else { // Operator if (pendingOps.isEmpty() || pendingOps.peek() == '(') { // First operator at this level pendingOps.push(nextC); } else { // Get the previous operator // This would require a while loop if there were // more than one level of precedence char lastOp = pendingOps.pop() ; if (lastOp == '+' && nextC == '*') { // Going to a higher precedence ... + x * .... pendingOps.push(lastOp); // Put old back } else { postfix.push(lastOp); // Do the old now if (lastOp == '*' && nextC =='+') { // Going to lower precedence if (!pendingOps.isEmpty() && pendingOps.peek()=='+') { // ..... + a * b + postfix.push(pendingOps.pop()); } } } pendingOps.push(nextC); } } } else /* Object is an Integer */ { // Value // Push it on postfix stack postfix.push(token); } } // Copy pending operators to postfix stack while(!pendingOps.isEmpty()) { postfix.push(pendingOps.pop()) ; } return postfix ; } // Transform a postfix list // such as 13 17 + 19 * 21 + // into a result private static int doPostfixEval(Deque postfix) { // evaluation stack Deque evalStack = new LinkedList<>() ; Iterator revStack = postfix.descendingIterator() ; while (revStack.hasNext()) { Object token = revStack.next() ; if (token instanceof Character) { // Must be + or - // Perform operator on top two values of stack int operand1 = evalStack.pop() ; int operand2 = evalStack.pop() ; int result = (Character)token == '+' ? operand1 + operand2 : operand1 * operand2 ; evalStack.push(result) ; } else /* token instanceof Integer */ { // Push values evalStack.push((Integer)token) ; } } return evalStack.pop() ; } private static void printDeque(Deque d) { Iterator rd = d.descendingIterator() ; while (rd.hasNext()) { Object e = rd.next() ; System.out.printf("%-4s", e.toString()) ; System.out.print(' ') ; } System.out.println() ; } private static final boolean debugPrint = false ; /** * @param expr The expression to evaluate * @return Value of the evaluated expression * @throws IllegalArgumentException When expression has improper syntax */ public static int evaluateExpression(String expr) throws IllegalArgumentException { Deque infixList = string2infix(expr) ; if (debugPrint) { printDeque(infixList) ; } syntaxCheck(infixList) ; Deque rpnList = infix2postfix(infixList) ; if (debugPrint) { printDeque(rpnList) ; } return doPostfixEval(rpnList) ; } private final static String[] expressions = { "15 + e", "202", "+", "181 182" , "5 + 7 )", "()", "5 + (", "5 +" , "15*17+ 23" , "15+17*23", "(15+17)*23", "((15 + 17))", "1 + 3 + 5 + 7", "1 * 3 * 5 * 7", "1 + 3 * 5 + 7", "1 + (3*5) + 7", "(13 + 17 * 19 + 23)", "29 * (31 * 37 + 41)", "43 * (((47)))", "53", "(13 + 17 * 19 + 23) + 29 * (31 * 37 + 41) + 43 * (((47))) + 53" } ; /** * @param args the command line arguments */ public static void main(String[] args) { for (String e : expressions) { System.out.println("Evaluating: " + e) ; try { System.out.println("Result: " + evaluateExpression(e)) ; } catch(IllegalArgumentException ae) { System.out.println("Bad expression: " + ae.getMessage()) ; } System.out.println() ; } } }