Krolyxon
Commit 1ce664e

Branches

Refs →

jcalsrcParser.java

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