-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathArithParser.java
344 lines (326 loc) · 13.8 KB
/
ArithParser.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
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
/**
* A Parser for our Arith language
* (a simple language of arithmetic expressions).
*
* <code>
* EXPRESSION ::= [ "+" | "-" ] TERM { ( "+" | "-" ) TERM }
* TERM ::= FACTOR { ( "*" | "/" ) FACTOR }
* FACTOR ::= Literal |
* Identifier|
* "(" EXPRESSION ")"
* </code>
*/
public final class ArithParser implements Parser {
private LexicalAnalyzer lexer;
/**
* Parse a program in the Arith language.
* @param sourceCode The source code of the program in the Arith language
* @return an AST of the program
*/
public Node parse(final String sourceCode) {
this.lexer = new LexicalAnalyzer(sourceCode);
// fetch first token
this.lexer.fetchNextToken();
try {
final Node expression = parseExpression();
this.lexer.fetchNextToken();
if (lexer.getCurrentToken().getText().equals("=")) {
this.lexer.fetchNextToken();
this.lexer.fetchNextToken();
return new Equation(expression, this.parseExpression());
} else {
return expression;
}
} catch (UnexpectedTokenException ex) {
return new Error(ex.getMessage());
}
}
/**
* Parse an expression.
* This assumes the lexer already points to the first token of this expression.
*
* <p>EBNF:
* <code>
* EXPRESSION ::= [ "+" | "-" ] TERM { ( "+" | "-" ) TERM }
* </code>
*
* @return a Node representing the expression.
* @throws UnexpectedTokenException if there isn't the expected token.
*/
private Node parseExpression() throws UnexpectedTokenException {
Node expressionNode;
//check if first node has before "-" or "+"
if (lexer.getCurrentToken().getType() == TokenType.MINUS) {
// has "+" before first Term and skip the sign
this.lexer.fetchNextToken();
expressionNode = new Negation(this.parseTerm());
} else if (lexer.getCurrentToken().getType() == TokenType.PLUS) {
// has "+" before first Term and skip the sign
this.lexer.fetchNextToken();
expressionNode = this.parseTerm();
} else {
// when literal has not sign
expressionNode = this.parseTerm();
if (lexer.getCurrentToken().getText().equals("^")) {
this.lexer.fetchNextToken();
expressionNode = new Power(expressionNode, this.parseTerm());
this.lexer.fetchNextToken();
}
}
// do a loop for all TERM in the EXPRESSION
while (lexer.getCurrentToken().getType() == TokenType.PLUS
|| lexer.getCurrentToken().getType() == TokenType.MINUS) {
if (lexer.getCurrentToken().getType() == TokenType.PLUS) {
// do addition if before TERM there's "+"
expressionNode = new Addition(expressionNode, this.parseTerm());
} else if (lexer.getCurrentToken().getType() == TokenType.MINUS) {
// do subtraction if before TERM there's "-"
expressionNode = new Subtraction(expressionNode, this.parseTerm());
}
this.lexer.fetchNextToken();
if (lexer.getCurrentToken().getText().equals("^")) {
this.lexer.fetchNextToken();
expressionNode = new Power(expressionNode, this.parseTerm());
}
}
return expressionNode;
}
/**
* Parse a term.
* This assumes the lexer already points to the first token of this term.
*
* <p>EBNF:
* <code>
* TERM ::= FACTOR { ( "*" | "/" ) FACTOR }
* </code>
*
* @return a Node representing the term.
* @throws UnexpectedTokenException if there isn't the expected token.
*/
private Node parseTerm() throws UnexpectedTokenException {
Node termNode = this.parseFactor();
// do a loop for the case which has multiplication or division
while (lexer.getCurrentToken().getType() == TokenType.STAR
|| lexer.getCurrentToken().getType() == TokenType.SLASH) {
if (lexer.getCurrentToken().getType() == TokenType.STAR) {
// do addition if before TERM there's "+"
termNode = new Multiplication(termNode, this.parseFactor());
} else if (lexer.getCurrentToken().getType() == TokenType.SLASH) {
// do subtraction if before TERM there's "-"
termNode = new Division(termNode, this.parseFactor());
}
this.lexer.fetchNextToken();
if (lexer.getCurrentToken().getText().equals("^")) {
this.lexer.fetchNextToken();
termNode = new Power(termNode, this.parseFactor());
}
}
return termNode;
}
/**
* Parse a factor.
* This assumes the lexer already points to the first token of this factor.
*
* <p>EBNF:
* <code>
* FACTOR ::=
* Literal |
* Identifier |
* Root |
* Limit |
* "(" EXPRESSION ")"
* </code>
*
* @return a Node representing the factor.
* @throws UnexpectedTokenException if there isn't the expected token.
*/
private Node parseFactor() throws UnexpectedTokenException {
Node factorNode;
if (lexer.getCurrentToken().getType() == TokenType.LITERAL) {
// literal case
factorNode = new Literal(Integer.parseInt(lexer.getCurrentToken().getText()));
this.lexer.fetchNextToken();
} else if (lexer.getCurrentToken().getType() == TokenType.IDENTIFIER) {
// identifier case
switch (lexer.getCurrentToken().getText()) {
case "root":
this.lexer.fetchNextToken();
factorNode = this.rootParser(); // go through the expression to exctract root
break;
case "lim":
this.lexer.fetchNextToken();
factorNode = this.limitParser(); // go through the expression to exctract limit
break;
case "string":
this.lexer.fetchNextToken();
this.lexer.fetchNextToken();
factorNode = this.stringParser(); // case -> "String" or "String: Expression"
break;
// variable case
default:
factorNode = new Variable(lexer.getCurrentToken().getText());
this.lexer.fetchNextToken();
break;
}
} else {
// expression case
this.lexer.fetchNextToken();
factorNode = this.parseExpression();
}
return factorNode;
}
/**
* Parse in case there's a limit in the expression.
* @return a Node representing the limit.
* @throws UnexpectedTokenException if there isn't the expected token.
*/
public Node limitParser() throws UnexpectedTokenException {
// set limit variable
final Node variable = this.creationLimitVariable();
this.lexer.fetchNextToken();
// set limit value
final Node value = this.creationLimitValue();
this.lexer.fetchNextToken();
// create the limit
final Node limitNode = this.creationLimit(variable, value);
// check if the user close the limit with a colon
this.parseError(":", "You must close the limit operation with a colon");
this.lexer.fetchNextToken();
return limitNode;
}
/**
* Parse in case there's a root in the expression.
* @return a Node representing the limit.
* @throws UnexpectedTokenException if there isn't the expected token.
*/
public Node rootParser() throws UnexpectedTokenException {
// set root grade
final Node grade = this.creationRootGrade();
this.lexer.fetchNextToken();
// create the root
final Node rootNode = this.creationRoot(grade);
// check if the user close the root with a colon
this.parseError(":", "You must close the root operation with a colon");
this.lexer.fetchNextToken();
return rootNode;
}
/**
* It analyzes the expression if the user want to insert a String in the .tex file.
* @return a Node with the String and if necessary the expression.
* @throws UnexpectedTokenException if there isn't the expected token.
*/
public Node stringParser() throws UnexpectedTokenException {
String stringToInsert = "";
while (lexer.getCurrentToken().getType() != TokenType.CLOSED_PAREN) {
stringToInsert = stringToInsert + lexer.getCurrentToken().getText();
this.lexer.fetchNextToken();
}
this.lexer.fetchNextToken();
if (this.tokenAnalyzer(":")) {
this.lexer.fetchNextToken();
return new StringExpression(stringToInsert, this.parseExpression());
} else {
return new StringMessage(stringToInsert);
}
}
/**
* It analyzes the current Token in the expression.
* @param expectedToken is a String which represents the token that we excpect.
* @return a boolean which is True if the current token is equal to the token
* which we excpect.
*/
public boolean tokenAnalyzer(final String expectedToken) {
return this.lexer.getCurrentToken().getText().equals(expectedToken);
}
/**
* It creates the grade of the root in the expression.
* @return a Node representing the grade of the root in LaTex.
* @throws UnexpectedTokenException if there isn't the expected token.
*/
public Node creationRootGrade() throws UnexpectedTokenException {
// check if there is a colon after the word 'root'
if (this.tokenAnalyzer(":")) {
this.lexer.fetchNextToken();
return this.parseExpression();
} else {
throw new UnexpectedTokenException("You must to start with colon after 'root' word");
}
}
/**
* It creates the root in the expression.
* @param grade is a Node which represents the grade of the root.
* @return a Node representing the root in LaTex.
* @throws UnexpectedTokenException if there isn't the expected token.
*/
public Node creationRoot(final Node grade) throws UnexpectedTokenException {
// check if there is a colon between the grade and the body of the root
if (this.tokenAnalyzer(":")) {
this.lexer.fetchNextToken();
return new Root(grade, this.parseExpression());
} else {
throw new UnexpectedTokenException("The body of the root and the grade "
+ "must be separated by a colon");
}
}
/**
* It creates the variable of the limit in the expression.
* @return a Node representing the variable of the limit in LaTex.
* @throws UnexpectedTokenException if there isn't the expected token.
*/
public Node creationLimitVariable() throws UnexpectedTokenException {
// check if there is a colon after the word 'lim'
if (this.tokenAnalyzer(":")) {
this.lexer.fetchNextToken();
this.lexer.fetchNextToken();
return new Variable(lexer.getCurrentToken().getText());
} else {
throw new UnexpectedTokenException("You must to start with colon after 'lim' word");
}
}
/**
* It creates the value of the limit in the expression.
* @return a Node representing the value of the limit in LaTex.
* @throws UnexpectedTokenException if there isn't the expected token.
*/
public Node creationLimitValue() throws UnexpectedTokenException {
// check if there is a comma which separate the variable and the value
if (this.tokenAnalyzer(",")) {
this.lexer.fetchNextToken();
return this.parseExpression();
} else {
throw new UnexpectedTokenException("Put a comma between the variable "
+ "and the variable value");
}
}
/**
* It creates the limit in the expression.
* @param variable is a Node which represents the variable of the limit.
* @param value is a Node which represents the value of the limit.
* @return a Node representing the limit in LaTex.
* @throws UnexpectedTokenException if there isn't the expected token.
*/
public Node creationLimit(final Node variable, final Node value)
throws UnexpectedTokenException {
if (this.tokenAnalyzer(":")) {
this.lexer.fetchNextToken();
return new Limit(this.parseExpression(), variable, value);
} else {
throw new UnexpectedTokenException("The body of the limit and the variable section "
+ "must be separated by a colon");
}
}
/**
* It checks if the user has written wrong the expression and update errorString with
* a message which explain what's going wrong.
* @param expectedToken is a String which represents the expected token.
* @param errorMessage is a String which represents the message that the
* user will see if there's an error.
* @throws UnexpectedTokenException if there isn't the expected token.
*/
public void parseError(final String expectedToken, final String errorMessage)
throws UnexpectedTokenException {
if (!this.tokenAnalyzer(expectedToken)) {
throw new UnexpectedTokenException(errorMessage);
}
}
}