-
Notifications
You must be signed in to change notification settings - Fork 11
Expand file tree
/
Copy pathExpressionParser.java
More file actions
176 lines (164 loc) · 5.78 KB
/
ExpressionParser.java
File metadata and controls
176 lines (164 loc) · 5.78 KB
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
package expression.exceptions;
import java.util.Collections;
import java.util.EnumMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import expression.*;
public class ExpressionParser extends BaseParser {
enum Token {
NUM, END, PLUS, MINUS, MUL, DIV, LEFTSHIFT, RIGHTSHIFT, REVERSE, DIGITS, LP, RP, VARNAME, POW, LOG
}
private final static Map<String, Token> stringToOperator = new LinkedHashMap<>();
static {
stringToOperator.put("//", Token.LOG);
stringToOperator.put("**", Token.POW);
stringToOperator.put("+", Token.PLUS);
stringToOperator.put("-", Token.MINUS);
stringToOperator.put("*", Token.MUL);
stringToOperator.put("/", Token.DIV);
stringToOperator.put(">>", Token.RIGHTSHIFT);
stringToOperator.put("<<", Token.LEFTSHIFT);
stringToOperator.put("reverse", Token.REVERSE);
stringToOperator.put("digits", Token.DIGITS);
stringToOperator.put("\0", Token.END);
stringToOperator.put("(", Token.LP);
stringToOperator.put(")", Token.RP);
}
private final static EnumMap<Token, Integer> priorities = new EnumMap<>(Map.of(
Token.LOG, 1,
Token.POW, 1,
Token.LEFTSHIFT, 4,
Token.RIGHTSHIFT, 4,
Token.PLUS, 3,
Token.MINUS, 3,
Token.MUL, 2,
Token.DIV, 2
));
private final static int maxPriority = Collections.max(priorities.values());
private final static Set<Character> allowedVariables = Set.of('x', 'y', 'z');
private char lastVarName;
private Token lastToken;
public CommonExpression parse(String expression) throws ParserException {
setSource(new StringSource(expression));
nextChar();
skipWhitespace();
getToken();
final CommonExpression result = parseExpression(maxPriority, false);
if (lastToken != Token.END) {
throw error("End of expression expected, got " + lastToken.name());
}
return result;
}
private CommonExpression parseExpression(int level, boolean get) throws ParserException {
if (level == 0) {
return parseBaseExpression(get);
}
CommonExpression result = parseExpression(level - 1, get);
while (priorities.getOrDefault(lastToken, -1) == level) {
result = CreateExpression(lastToken, result, parseExpression(level - 1, true));
}
return result;
}
private CommonExpression parseBaseExpression(boolean get) throws ParserException {
if (get) {
getToken();
}
switch (lastToken) {
case NUM:
getToken();
return new Const(parseInt(false));
case VARNAME:
getToken();
return new Variable(Character.toString(lastVarName));
case MINUS:
skipWhitespace();
if (between('0', '9')) {
getToken();
return new Const(parseInt(true));
} else {
getToken();
return new CheckedNegate(parseBaseExpression(false));
}
case REVERSE:
getToken();
return new Reverse(parseBaseExpression(false));
case DIGITS:
getToken();
return new Digits(parseBaseExpression(false));
case LP:
getToken();
CommonExpression result = parseExpression(maxPriority, false);
if (lastToken != Token.RP) {
throw error("Right parenthesis expected, got " + lastToken.name());
}
getToken();
return result;
default:
throw error("Primary expression expected, got " + lastToken.name());
}
}
private void getToken() throws ParserException {
skipWhitespace();
for (String op : stringToOperator.keySet()) {
if (op.charAt(0) == ch) {
// System.err.println(op + "passed");
if (expect(op)) {
lastToken = stringToOperator.get(op);
return;
}
}
}
if (allowedVariables.contains(ch)) {
lastVarName = ch;
nextChar();
lastToken = Token.VARNAME;
return;
}
if (between('0', '9')) {
lastToken = Token.NUM;
return;
} else {
throw error("Unsupported variable name or operator");
}
}
private int parseInt(boolean negative) throws ParserException {
StringBuilder sb = new StringBuilder();
while (between('0', '9')) {
sb.append(ch);
nextChar();
}
if (negative) {
sb.insert(0, '-');
}
getToken();
try {
return Integer.parseInt(sb.toString());
} catch (NumberFormatException e) {
throw new OverflowException(sb.toString());
}
}
private CommonExpression CreateExpression(Token type, CommonExpression left, CommonExpression right) throws ParserException {
// ? Is this copypaste alright or should a Map with reflection be used?
switch (type) {
case MUL:
return new CheckedMultiply(left, right);
case DIV:
return new CheckedDivide(left, right);
case MINUS:
return new CheckedSubtract(left, right);
case PLUS:
return new CheckedAdd(left, right);
case POW:
return new CheckedPow(left, right);
case LOG:
return new CheckedLog(left, right);
case LEFTSHIFT:
return new CheckedLeftShift(left, right);
case RIGHTSHIFT:
return new CheckedRightShift(left, right);
default:
throw error("Unsupported binary operator: " + type.name());
}
}
}