-
Notifications
You must be signed in to change notification settings - Fork 0
/
Converter.java
211 lines (185 loc) · 7.28 KB
/
Converter.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
import java.util.EmptyStackException;
import java.util.LinkedList;
import java.util.List;
import java.util.Stack;
import java.util.StringTokenizer;
/*
* Kelas yang mengandung segala keperluan untuk konversi dari infix ke postfix.
*/
public class Converter {
/**
* @param expression (Infix) in string form
* @return Standardized infix expression in string form - can be tokenized
*/
public static String standardizeInfix(String expression) {
char[] charArr = expression.toCharArray();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < charArr.length - 1; i++) {
if (isParenthesis(charArr[i])) {
if (parenthesisPriority(charArr[i]) == 1) {
sb.append(" ");
}
sb.append(charArr[i]);
sb.append(" ");
} else if (isOperator(charArr[i]) != isOperator(charArr[i+1])) {
sb.append(charArr[i]);
sb.append(" ");
} else {
sb.append(charArr[i]);
}
}
return sb.toString();
}
/**
* @param chr - token
* @return integer - tingkat prioritas operator <p>
* $: 4, *: 3, /:3, +:2, -:2, ():1, non-operator:-1
*/
public static Integer operatorPriority(char chr) {
switch(chr) {
case '$':
return 4;
case '*':
return 3;
case '/':
return 3;
case '+':
return 2;
case '-':
return 2;
case '–': // BERBEDA DENGAN - BIASA!
return 2;
case '(':
return 1;
case ')':
return 1;
}
return -1;
}
/**
* @param chr - token
* @return boolean - True jika chr adalah operator, false jika bukan.
*/
public static Boolean isOperator(char chr) {
if (operatorPriority(chr) > 0) {
return true;
} else {
return false;
}
}
/**
* @param str - token multi-character
* @return boolean - false jika ada satu atau lebih karakter non-operator
*/
public static Boolean isOperator(String str) {
for (char chr : str.toCharArray()) {
if (!isOperator(chr)) {
return false;
}
}
return true;
}
/**
* @param chr - token
* @return integer 1 untuk kurung awal, 2 untuk kurung akhir
*/
public static int parenthesisPriority (char chr) {
switch(chr) {
case '(':
return 1;
case ')':
return 2;
}
return -1;
}
/**
* @param chr - token
* @return boolean - True jika chr adalah kurung awal/akhir, false jika bukan.
*/
public static Boolean isParenthesis(char chr) {
if (parenthesisPriority(chr) > 0) return true;
return false;
}
/**
* Mengkonversi infix yang sudah tokenized menjadi postfix.
* @param tokens of Infix expression (StringTokenizer)
* @return Postfix expression (List<String>)
* @throws CalculationException
*/
public static List<String> getPostfixExpression(StringTokenizer tokens) throws CalculationException{
List<String> postfix = new LinkedList<String>();
Stack<Character> operatorStack = new Stack<Character>();
int operatorCount = 0, operandCount = 0, parenthesisBalance = 0;
// Meng-iterasi setiap token pada tokenized infix expression
try {
while (tokens.hasMoreTokens()) {
String next = tokens.nextToken();
if (isOperator(next)) {
// jika token selanjutnya adalah operator
char nextOperator = next.charAt(0);
if (operandCount + operatorCount == 0 && operatorPriority(nextOperator) == 2) {
// jika expression dimulai dengan + atau -, perbaiki (tetap valid)
postfix.add("0");
operandCount++;
} else if (operatorStack.empty()) {
// jangan peek jika kosong. soalnya pasti di-push juga.
operatorStack.push(nextOperator);
if (parenthesisPriority(nextOperator) == 1) parenthesisBalance++;
if (parenthesisPriority(nextOperator) == 2) parenthesisBalance--;
continue;
} else if (parenthesisPriority(nextOperator) == 1) {
// jika kurungnya adalah kurung awal.
operatorStack.push(nextOperator);
parenthesisBalance++;
continue;
} else if (parenthesisPriority(nextOperator) == 2){
// jika kurungnya kurung akhir, pop operator sampai ketemu kurung awal.
parenthesisBalance--;
while (parenthesisPriority(operatorStack.peek()) < 0) {
postfix.add(operatorStack.pop().toString());
operatorCount++;
}
if (parenthesisPriority(operatorStack.peek()) == 1) operatorStack.pop();
continue;
} else while (operatorPriority(nextOperator) < 4 &&
operatorPriority(nextOperator) <= operatorPriority(operatorStack.peek())) {
// jika operator selanjutnya bukan pangkat, namun prioritas lebih tinggi dari yang di top of stack
postfix.add(operatorStack.pop().toString());
operatorCount++;
if (operatorStack.empty()) {
break;
}
}
// kondisi "standard": push operator to stack
operatorStack.push(nextOperator);
continue;
} else {
// jika token selanjutnya operand
postfix.add(next);
operandCount++;
}
}
// Ketika stack tidak bisa dievaluasi lengkap karena kurung yang tidak tepat
} catch (EmptyStackException e) {
throw new CalculationException("Missing parenthesis.", postfix);
}
// Ketika infix sudah habis/selesai, pop semua operator yang tersisa pada stack.
while (!operatorStack.empty()) {
if (isParenthesis(operatorStack.peek())) {
operatorStack.pop(); // jangan masukkan tanda kurung ke postfix
} else {
postfix.add(operatorStack.pop().toString());
operatorCount++;
}
}
// Jika kekurangan/kelebihan operand
if (operandCount != operatorCount + 1) {
throw new CalculationException("Missing operator/operands.", postfix);
}
// Jika ada kurung awal yang tidak diakhiri / kurung akhir yang tidak berawal
if (parenthesisBalance != 0) {
throw new CalculationException("Missing parenthesis", postfix);
}
return postfix;
}
}