Krolyxon
Commit c62432e

Branches

Refs →

jcalParser.java

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