package mulCal.equationParser; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Stack; import mulCal.equationParser.Tokenize.Token; public class InfixToRPN { // Associativity constants for operators private static final int LEFT_ASSOC = 0; private static final int RIGHT_ASSOC = 1; // Supported operators private static final Map<String, int[]> Operators = new HashMap<String, int[]>(); static { // Map<"token", []{precedence, associativity}> Operators.put("+", new int[] { 0, LEFT_ASSOC }); Operators.put("-", new int[] { 0, LEFT_ASSOC }); Operators.put("*", new int[] { 5, LEFT_ASSOC }); Operators.put("/", new int[] { 5, LEFT_ASSOC }); Operators.put("%", new int[] { 5, LEFT_ASSOC }); Operators.put("^", new int[] { 10, RIGHT_ASSOC }); } public static List<Token> toRPN(List<Token> tokens) throws Exception { // http://en.wikipedia.org/wiki/Shunting-yard_algorithm List<Token> out = new ArrayList<Token>(); Stack<Token> stack = new Stack<Token>(); for (Token token : tokens) { if (token.type == Tokenize.TokenType.NUMBER) { // If the token is a number, then add it to the output queue. out.add(token); } else if (token.type == Tokenize.TokenType.FUNCTION) { // If the token is a function token, then push it onto the stack. stack.push(token); } else if (token.type == Tokenize.TokenType.OPERATOR) { // If the token is an operator, o1, then: // while there is an operator token, o2, at the top of the stack, and // either o1 is left-associative and its precedence is less than or equal to that of o2, // or o1 is right-associative and its precedence is less than that of o2, // pop o2 off the stack, onto the output queue; // push o1 onto the stack. while (!stack.empty() && stack.peek().type == Tokenize.TokenType.OPERATOR && ((leftAssociative(token) && precidence(token) <= precidence(stack.peek())) || (!leftAssociative(token) && precidence(token) < precidence(stack.peek())))) { out.add(stack.pop()); } stack.push(token); } else if (token.type == Tokenize.TokenType.OPENBRACKET) { // If the token is a left parenthesis, then push it onto the stack. stack.push(token); } else if (token.type == Tokenize.TokenType.CLOSEBRACKET) { // If the token is a right parenthesis: // Until the token at the top of the stack is a left parenthesis, pop operators off the stack onto the output queue. // Pop the left parenthesis from the stack, but not onto the output queue. // If the token at the top of the stack is a function token, pop it onto the output queue. // If the stack runs out without finding a left parenthesis, then there are mismatched parentheses. while (!stack.empty() && stack.peek().type != Tokenize.TokenType.OPENBRACKET) { out.add(stack.pop()); } if (!stack.empty() && stack.peek().type == Tokenize.TokenType.OPENBRACKET) { stack.pop(); } else { throw new Exception("InfixToRPN.toRPN: Mismatched parentheses (you forgot a bracket)."); } if (!stack.empty() && stack.peek().type == Tokenize.TokenType.FUNCTION) { out.add(stack.pop()); } } else { throw new Exception("InfixToRPN.toRPN: Programmer error - unexpected token type " + token.type); } } // When there are no more tokens to read: // While there are still operator tokens in the stack: // If the operator token on the top of the stack is a parenthesis, // then there are mismatched parentheses. // Pop the operator onto the output queue. while (!stack.empty()) { if (stack.peek().type == Tokenize.TokenType.OPENBRACKET || stack.peek().type == Tokenize.TokenType.CLOSEBRACKET ) { throw new Exception("InfixToRPN.toRPN: Mismatched parentheses (you forgot a bracket)."); } out.add(stack.pop()); } return out; } private static int precidence(Token token) { if (!isOperator(token)) { throw new IllegalArgumentException("InfixToRPN.precidence: Invalid token: " + token.text); } return Operators.get(token.text)[0]; } private static boolean leftAssociative(Token token) { if (!isOperator(token)) { throw new IllegalArgumentException("InfixToRPN.leftAssociative: Invalid token: " + token.text); } if (Operators.get(token.text)[1] == LEFT_ASSOC) { return true; } return false; } private static boolean isOperator(Token token) { return Operators.containsKey(token.text); } }