7
7
import com .mojang .brigadier .exceptions .CommandSyntaxException ;
8
8
import com .mojang .brigadier .exceptions .Dynamic2CommandExceptionType ;
9
9
import com .mojang .brigadier .exceptions .DynamicCommandExceptionType ;
10
+ import com .mojang .brigadier .exceptions .SimpleCommandExceptionType ;
10
11
import com .mojang .brigadier .suggestion .Suggestions ;
11
12
import com .mojang .brigadier .suggestion .SuggestionsBuilder ;
12
13
import net .earthcomputer .clientcommands .TempRules ;
@@ -26,6 +27,7 @@ public class ExpressionArgumentType implements ArgumentType<ExpressionArgumentTy
26
27
27
28
private static final DynamicCommandExceptionType EXPECTED_EXCEPTION = new DynamicCommandExceptionType (obj -> new TranslatableText ("commands.ccalc.expected" , obj ));
28
29
private static final Dynamic2CommandExceptionType INVALID_ARGUMENT_COUNT = new Dynamic2CommandExceptionType ((func , count ) -> new TranslatableText ("commands.ccalc.invalidArgumentCount" , func , count ));
30
+ private static final SimpleCommandExceptionType TOO_DEEPLY_NESTED = new SimpleCommandExceptionType (new TranslatableText ("commands.ccalc.tooDeeplyNested" ));
29
31
30
32
private ExpressionArgumentType () {}
31
33
@@ -40,7 +42,7 @@ public static Expression getExpression(CommandContext<ServerCommandSource> conte
40
42
@ Override
41
43
public Expression parse (StringReader reader ) throws CommandSyntaxException {
42
44
int start = reader .getCursor ();
43
- Expression ret = new Parser (reader ).parseExpression ();
45
+ Expression ret = new Parser (reader ).parse ();
44
46
ret .strVal = reader .getString ().substring (start , reader .getCursor ());
45
47
return ret ;
46
48
}
@@ -53,7 +55,7 @@ public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> cont
53
55
Parser parser = new Parser (reader );
54
56
55
57
try {
56
- parser .parseExpression ();
58
+ parser .parse ();
57
59
} catch (CommandSyntaxException ignore ) {
58
60
}
59
61
@@ -69,105 +71,64 @@ public Collection<String> getExamples() {
69
71
}
70
72
71
73
private static class Parser {
72
- private StringReader reader ;
74
+ private final StringReader reader ;
73
75
private Consumer <SuggestionsBuilder > suggestor ;
74
76
75
77
public Parser (StringReader reader ) {
76
78
this .reader = reader ;
77
79
}
78
80
79
- public Expression parseExpression () throws CommandSyntaxException {
80
- return parseExpression1 ();
81
- }
82
-
83
- private Expression parseExpression1 () throws CommandSyntaxException {
84
- Expression left = parseExpression2 ();
85
-
86
- reader .skipWhitespace ();
87
- if (reader .canRead ()) {
88
- if (reader .peek () == '+' ) {
89
- reader .skip ();
90
- reader .skipWhitespace ();
91
- Expression right = parseExpression1 ();
92
- return new BinaryOpExpression (left , right , Double ::sum , "addition" );
93
- }
94
-
95
- if (reader .peek () == '-' ) {
96
- reader .skip ();
97
- reader .skipWhitespace ();
98
- Expression right = parseExpression1 ();
99
- return new BinaryOpExpression (left , right , (l , r ) -> l - r , "subtraction" );
100
- }
81
+ public Expression parse () throws CommandSyntaxException {
82
+ try {
83
+ return parseExpression ();
84
+ } catch (StackOverflowError e ) {
85
+ suggestor = null ;
86
+ throw TOO_DEEPLY_NESTED .create ();
101
87
}
102
-
103
- return left ;
104
88
}
105
89
106
- private Expression parseExpression2 () throws CommandSyntaxException {
107
- Expression left = parseExpression3 ();
108
-
90
+ // < Expression> ::= <Term> | (<Expression> ("+" | "-") <Term>)
91
+ private Expression parseExpression () throws CommandSyntaxException {
92
+ Expression expr = parseTerm ();
109
93
reader .skipWhitespace ();
110
- if (reader .canRead ()) {
111
- if (reader .peek () == '*' ) {
112
- reader .skip ();
113
- reader .skipWhitespace ();
114
- Expression right = parseExpression2 ();
115
- return new BinaryOpExpression (left , right , (l , r ) -> l * r , "multiplication" );
116
- }
117
-
118
- if (reader .peek () == '/' ) {
119
- reader .skip ();
120
- reader .skipWhitespace ();
121
- Expression right = parseExpression2 ();
122
- return new BinaryOpExpression (left , right , (l , r ) -> l / r , "division" );
123
- }
124
-
125
- if (reader .peek () == '%' ) {
126
- reader .skip ();
127
- reader .skipWhitespace ();
128
- Expression right = parseExpression2 ();
129
- return new BinaryOpExpression (left , right , (l , r ) -> l % r , "modulo" );
130
- }
131
94
132
- if (! StringReader . isAllowedNumber ( reader .peek ())) {
133
- int cursor = reader .getCursor ();
134
- try {
135
- Expression right = parseExpression5 ();
136
- return new BinaryOpExpression ( left , right , ( l , r ) -> l * r , "multiplication" );
137
- } catch ( CommandSyntaxException e ) {
138
- reader . setCursor ( cursor );
139
- }
95
+ while ( reader . canRead () && ( reader . peek () == '+' || reader .peek () == '-' )) {
96
+ char operator = reader .read ();
97
+ reader . skipWhitespace ();
98
+ Expression right = parseTerm ();
99
+ if ( operator == '+' ) {
100
+ expr = new BinaryOpExpression ( expr , right , Double :: sum , "addition" );
101
+ } else {
102
+ expr = new BinaryOpExpression ( expr , right , ( l , r ) -> l - r , "subtraction" );
140
103
}
141
104
}
142
105
143
- return left ;
106
+ return expr ;
144
107
}
145
108
146
- private Expression parseExpression3 () throws CommandSyntaxException {
147
- List <Expression > subExpressions = new ArrayList <>();
148
- subExpressions .add (parseExpression4 ());
149
-
109
+ // <Term> ::= <Unary> | (<Term> ("*" | "/" | "%") <Unary>)
110
+ private Expression parseTerm () throws CommandSyntaxException {
111
+ Expression expr = parseUnary ();
150
112
reader .skipWhitespace ();
151
- while (reader .canRead () && reader .peek () == '^' ) {
152
- reader .skip ();
153
- reader .skipWhitespace ();
154
- subExpressions .add (parseExpression4 ());
155
- reader .skipWhitespace ();
156
- }
157
113
158
- if (subExpressions .size () == 1 ) {
159
- return subExpressions .get (0 );
160
- } else {
161
- Expression right = subExpressions .get (subExpressions .size () - 1 );
162
- for (int i = subExpressions .size () - 2 ; i >= 0 ; i --) {
163
- Expression left = subExpressions .get (i );
164
- right = new BinaryOpExpression (left , right , Math ::pow , "exponentiation" );
114
+ while (reader .canRead () && (reader .peek () == '*' || reader .peek () == '/' || reader .peek () == '%' )) {
115
+ char operator = reader .read ();
116
+ reader .skipWhitespace ();
117
+ Expression right = parseUnary ();
118
+ if (operator == '*' ) {
119
+ expr = new BinaryOpExpression (expr , right , (l , r ) -> l * r , "multiplication" );
120
+ } else if (operator == '/' ) {
121
+ expr = new BinaryOpExpression (expr , right , (l , r ) -> l / r , "division" );
122
+ } else {
123
+ expr = new BinaryOpExpression (expr , right , (l , r ) -> l % r , "modulo" );
165
124
}
166
- return right ;
167
125
}
126
+
127
+ return expr ;
168
128
}
169
129
170
- private Expression parseExpression4 () throws CommandSyntaxException {
130
+ // <Unary> ::= ("+" | "-")* <ImplicitMult>
131
+ private Expression parseUnary () throws CommandSyntaxException {
171
132
boolean negative = false ;
172
133
while (reader .canRead () && (reader .peek () == '+' || reader .peek () == '-' )) {
173
134
if (reader .peek () == '-' )
@@ -176,14 +137,50 @@ private Expression parseExpression4() throws CommandSyntaxException {
176
137
reader .skipWhitespace ();
177
138
}
178
139
179
- Expression right = parseExpression5 ();
140
+ Expression right = parseImplicitMult ();
180
141
if (negative )
181
142
return new NegateExpression (right );
182
143
else
183
144
return right ;
184
145
}
185
146
186
- private Expression parseExpression5 () throws CommandSyntaxException {
147
+ // <ImplicitMult> ::= <Exponentiation> | (<not lookahead Literal> <ImplicitMult>)
148
+ private Expression parseImplicitMult () throws CommandSyntaxException {
149
+ Expression expr = parseExponentiation ();
150
+
151
+ if (reader .canRead () && !StringReader .isAllowedNumber (reader .peek ())) {
152
+ int cursor = reader .getCursor ();
153
+ try {
154
+ Expression right = parseImplicitMult ();
155
+ return new BinaryOpExpression (expr , right , (l , r ) -> l * r , "multiplication" );
156
+ } catch (CommandSyntaxException e ) {
157
+ reader .setCursor (cursor );
158
+ }
159
+ }
160
+
161
+ return expr ;
162
+ }
163
+
164
+ // <Exponentiation> ::= <ConstantOrFunction> | (<ConstantOrFunction> "^" <Unary>)
165
+ private Expression parseExponentiation () throws CommandSyntaxException {
166
+ Expression expr = parseConstantOrFunction ();
167
+ reader .skipWhitespace ();
168
+ if (reader .canRead () && reader .peek () == '^' ) {
169
+ reader .skip ();
170
+ reader .skipWhitespace ();
171
+ Expression exponent = parseUnary ();
172
+ return new BinaryOpExpression (expr , exponent , Math ::pow , "exponentiation" );
173
+ }
174
+
175
+ return expr ;
176
+ }
177
+
178
+ // <ConstantOrFunction> ::= <Parenthesized> | <Constant> | <Function>
179
+ // <Constant> ::= any of the defined constants
180
+ // <Function> ::= <FunctionName> "(" <FunctionArgumentList> ")"
181
+ // <FunctionName> ::= any of the defined function names
182
+ // <FunctionArgumentList> ::= <Expression> | (<Expression> "," <FunctionArgumentList>)
183
+ private Expression parseConstantOrFunction () throws CommandSyntaxException {
187
184
int cursor = reader .getCursor ();
188
185
suggestor = builder -> {
189
186
SuggestionsBuilder newBuilder = builder .createOffset (cursor );
@@ -238,12 +235,13 @@ private Expression parseExpression5() throws CommandSyntaxException {
238
235
if (reader .canRead () && reader .peek () == '(' )
239
236
suggestor = null ;
240
237
241
- Expression ret = parseExpression6 ();
238
+ Expression ret = parseParenthesized ();
242
239
suggestor = null ;
243
240
return ret ;
244
241
}
245
242
246
- private Expression parseExpression6 () throws CommandSyntaxException {
243
+ // <Parenthesized> ::= <Literal> | ("(" <Expression> ")")
244
+ private Expression parseParenthesized () throws CommandSyntaxException {
247
245
if (reader .canRead () && reader .peek () == '(' ) {
248
246
reader .skip ();
249
247
reader .skipWhitespace ();
@@ -254,10 +252,11 @@ private Expression parseExpression6() throws CommandSyntaxException {
254
252
reader .skip ();
255
253
return ret ;
256
254
}
257
- return parseExpression7 ();
255
+ return parseLiteral ();
258
256
}
259
257
260
- private Expression parseExpression7 () throws CommandSyntaxException {
258
+ // <Literal> ::= ("0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" | ".")+
259
+ private Expression parseLiteral () throws CommandSyntaxException {
261
260
int start = reader .getCursor ();
262
261
while (reader .canRead () && isAllowedNumber (reader .peek ()))
263
262
reader .skip ();
@@ -284,11 +283,10 @@ public static abstract class Expression {
284
283
}
285
284
286
285
private static class BinaryOpExpression extends Expression {
287
-
288
- private Expression left ;
289
- private Expression right ;
290
- private DoubleBinaryOperator operator ;
291
- private String type ;
286
+ private final Expression left ;
287
+ private final Expression right ;
288
+ private final DoubleBinaryOperator operator ;
289
+ private final String type ;
292
290
293
291
public BinaryOpExpression (Expression left , Expression right , DoubleBinaryOperator operator , String type ) {
294
292
this .left = left ;
@@ -309,7 +307,7 @@ public Text getParsedTree() {
309
307
}
310
308
311
309
private static class NegateExpression extends Expression {
312
- private Expression right ;
310
+ private final Expression right ;
313
311
314
312
public NegateExpression (Expression right ) {
315
313
this .right = right ;
@@ -334,8 +332,8 @@ private static class ConstantExpression extends Expression {
334
332
"ans" , () -> TempRules .calcAnswer
335
333
);
336
334
337
- private String type ;
338
- private DoubleSupplier constant ;
335
+ private final String type ;
336
+ private final DoubleSupplier constant ;
339
337
340
338
public ConstantExpression (String type , DoubleSupplier constant ) {
341
339
this .type = type ;
@@ -355,7 +353,7 @@ public Text getParsedTree() {
355
353
356
354
public static class FunctionExpression extends Expression {
357
355
358
- private static Map <String , IFunction > FUNCTIONS = ImmutableMap .<String , IFunction >builder ()
356
+ private static final Map <String , IFunction > FUNCTIONS = ImmutableMap .<String , IFunction >builder ()
359
357
.put ("sqrt" , (UnaryFunction ) Math ::sqrt )
360
358
.put ("abs" , (UnaryFunction ) Math ::abs )
361
359
.put ("floor" , (UnaryFunction ) Math ::floor )
@@ -423,9 +421,9 @@ public boolean isAcceptableInputCount(int count) {
423
421
.put ("not" , (UnaryFunction ) val -> (double )(~((int )val )))
424
422
.build ();
425
423
426
- private String type ;
427
- private IFunction function ;
428
- private Expression [] arguments ;
424
+ private final String type ;
425
+ private final IFunction function ;
426
+ private final Expression [] arguments ;
429
427
430
428
public FunctionExpression (String type , IFunction function , Expression ... arguments ) {
431
429
this .type = type ;
@@ -504,7 +502,7 @@ default boolean isAcceptableInputCount(int count) {
504
502
}
505
503
506
504
private static class LiteralExpression extends Expression {
507
- private double val ;
505
+ private final double val ;
508
506
509
507
public LiteralExpression (double val ) {
510
508
this .val = val ;
0 commit comments