package mulCal.equationParser; import java.math.BigDecimal; import java.util.EmptyStackException; import java.util.List; import java.util.Stack; import mulCal.equationParser.Tokenize.Token; import mulCal.equationParser.Tokenize.TokenType; //http://andreinc.net/2010/10/05/converting-infix-to-rpn-shunting-yard-algorithm/ //http://stackoverflow.com/questions/114586/smart-design-of-a-math-parser //http://stackoverflow.com/questions/28256/equation-expression-parser-with-precedence //http://www.softwaremonkey.org/Code/MathEval //http://scriptasylum.com/tutorials/infix_postfix/algorithms/postfix-evaluation/index.htm //http://www.javapractices.com/topic/TopicAction.do?Id=87 public class PostfixEval { public static BigDecimal eval(List<Token> tokens) throws Exception { Stack<BigDecimal> stack = new Stack<BigDecimal>(); if (tokens.size() == 0) { throw new Exception("PostfixEval.eval: No equation given."); } for (Token token : tokens) { if (token.type == TokenType.NUMBER) { stack.push(new BigDecimal(token.text)); } else if (token.type == TokenType.FUNCTION ) { // pop stack as value // eval token.text ( value ) as result // push result to stack try { BigDecimal value = stack.pop(); BigDecimal result = EvaluateFunction(token.text, value); stack.push(result); } catch (EmptyStackException e) { throw new Exception("PostfixEval.eval: Too many operators."); } } else if (token.type == TokenType.OPERATOR) { BigDecimal value1 = stack.pop(); BigDecimal value2 = stack.pop(); BigDecimal result = EvaluateOperator(token.text, value2, value1); stack.push(result); } else { throw new Exception("PostfixEval.eval: programmer error - unexpected token type " + token.type); } } if (stack.size() != 1) { throw new Exception("PostfixEval.eval: Too many numbers."); } return stack.pop(); } public static BigDecimal EvaluateOperator(String text, BigDecimal value1, BigDecimal value2) throws Exception { if (text.equals("+")) { return value1.add(value2); } else if (text.equals("-")) { return value1.subtract(value2); } else if (text.equals("/")) { try { return value1.divide(value2); } catch (ArithmeticException e) { return value1.divide(value2, 20, BigDecimal.ROUND_HALF_UP); } } else if (text.equals("*")) { return value1.multiply(value2); } else if (text.equals("%")) { return value1.remainder(value2); } else if (text.equals("^")) { return new BigDecimal(Math.pow(value1.doubleValue(),value2.doubleValue())); } else { throw new Exception(String.format("PostfixEval.EvaluateOperator: '%s' is not an operator but one was expected there.", text)); } } public static BigDecimal EvaluateFunction(String text, BigDecimal value) throws Exception { if (text.equals("nul")) { return value; } else if (text.equals("sin")) { return new BigDecimal(Math.sin(value.doubleValue())); } else if (text.equals("cos")) { return new BigDecimal(Math.cos(value.doubleValue())); } else if (text.equals("tan")) { return new BigDecimal(Math.tan(value.doubleValue())); } else if (text.equals("exp")) { return new BigDecimal(Math.exp(value.doubleValue())); } else if (text.equals("log")) { return new BigDecimal(Math.log(value.doubleValue())); } else if (text.equals("sqrt")) { return new BigDecimal(Math.sqrt(value.doubleValue())); } else if (text.equals("signum")) { return new BigDecimal(value.signum()); } else if (text.equals("abs")) { return value.abs(); } else if (text.equals("-")) { return value.negate(); } else { throw new Exception(String.format("PostfixEval.EvaluateFunction: '%s' is not a function but one was expected there.", text)); } } }