Krolyxon
Commit 8a095b2

Branches

Refs →

jcalParser.java

158 lines · 4.5 KB · Java

1/* Todo
2* - Add support for trigonometric functions
3* - Remove all edge cases
4*/
5
6import java.util.Stack;
7import java.util.StringJoiner;
8
9public class Parser {
10 private String expr;
11 private String postfix;
12 private Stack<Character> operatorStack;
13
14 public Parser(String infixExpr) {
15 expr = infixExpr.trim().replaceAll("\\s", ""); // remove whitespaces
16 postfix = toPostFix();
17 }
18
19 private boolean isOperand(char c) {
20 switch (c) {
21 case '0':
22 case '1':
23 case '2':
24 case '3':
25 case '4':
26 case '5':
27 case '6':
28 case '7':
29 case '8':
30 case '9':
31 return true;
32 default:
33 return false;
34 }
35 }
36
37 private boolean isOperand(String c) {
38 return isOperand(c.charAt(0));
39 }
40
41 private int getPresedence(char c) {
42 switch (c) {
43 case '^':
44 return 3;
45 case '/':
46 return 2;
47 case '*':
48 return 2;
49 case '+':
50 return 1;
51 case '-':
52 return 1;
53 default:
54 return -1;
55 }
56 }
57
58 private boolean hasLeftAssociativity(char c) {
59 if (c == '^') {
60 return false;
61 } else {
62 return true;
63 }
64 }
65
66 private String toPostFix() {
67 operatorStack = new Stack<>();
68 StringJoiner output = new StringJoiner(" ");
69 StringBuilder operand = new StringBuilder();
70 char[] infix = expr.replaceAll(" ", "").toCharArray();
71
72 // Main Expression Iteration
73 for (int i = 0; i < infix.length; i++) {
74 char c = infix[i];
75
76 // Keep appending digits to the operand StringBuilder and skip iterations till
77 // we find a operator.
78 if (isOperand(c)) {
79 operand.append(infix[i]);
80 if (i < infix.length - 1) {
81 continue;
82 }
83 }
84
85 if (operand.length() > 0) {
86 output.add(operand.toString());
87 operand = new StringBuilder();
88 }
89
90 if (c == '(') {
91 operatorStack.push(c);
92 } else if (c == ')') {
93 while (!operatorStack.isEmpty() && operatorStack.peek() != '(') {
94 output.add(operatorStack.pop().toString());
95 }
96 operatorStack.pop();
97 } else {
98 // Manage precedence order of operatorStack operators
99 while (!operatorStack.isEmpty() && getPresedence(c) <= getPresedence((char) operatorStack.peek())
100 && hasLeftAssociativity(c)) {
101 output.add(operatorStack.pop().toString());
102 }
103
104 // For whatever reason, the last digit gets added to the operatorStack, leading
105 // to a duplicate of last digit in the expression
106 // To fix this, we again make sure that the current char is not a operand.
107 if (!isOperand(c)) {
108 operatorStack.push(c);
109 }
110 }
111 }
112
113 // pop all the operators left in the operatorStack after the loop is done.
114 while (!operatorStack.isEmpty()) {
115 if (operatorStack.peek() == '(') {
116 return "This expression is invalid";
117 }
118 output.add(operatorStack.pop().toString());
119 }
120
121 return output.toString();
122 }
123
124 private Double evaluate(String operator, Double op1, Double op2) {
125 switch (operator.charAt(0)) {
126 case '+':
127 return op2 + op1;
128 case '-':
129 return op1 - op2;
130 case '*':
131 return op2 * op1;
132 case '/':
133 return op1 / op2;
134 case '^':
135 return (Math.pow(op2, op1));
136 default:
137 return 0.0;
138 }
139 }
140
141 public double eval() {
142 Stack<Double> stack = new Stack<Double>();
143 for (String c : postfix.split(" ")) {
144 if (isOperand(c)) {
145 stack.push(Double.parseDouble(c));
146 } else {
147 Double op1 = (Double) (stack.pop());
148 Double op2 = (Double) (stack.pop());
149 stack.push(evaluate(c, op2, op1));
150 }
151 }
152 return stack.pop();
153 }
154
155 public String getPostfix() {
156 return postfix;
157 }
158}