Skip to content

Commit 36bc68a

Browse files
committed
Fix issues with /ccalc. Closes #198, closes #173
1 parent 639834d commit 36bc68a

File tree

2 files changed

+97
-98
lines changed

2 files changed

+97
-98
lines changed

src/main/java/net/earthcomputer/clientcommands/command/arguments/ExpressionArgumentType.java

Lines changed: 96 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import com.mojang.brigadier.exceptions.CommandSyntaxException;
88
import com.mojang.brigadier.exceptions.Dynamic2CommandExceptionType;
99
import com.mojang.brigadier.exceptions.DynamicCommandExceptionType;
10+
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
1011
import com.mojang.brigadier.suggestion.Suggestions;
1112
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
1213
import net.earthcomputer.clientcommands.TempRules;
@@ -26,6 +27,7 @@ public class ExpressionArgumentType implements ArgumentType<ExpressionArgumentTy
2627

2728
private static final DynamicCommandExceptionType EXPECTED_EXCEPTION = new DynamicCommandExceptionType(obj -> new TranslatableText("commands.ccalc.expected", obj));
2829
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"));
2931

3032
private ExpressionArgumentType() {}
3133

@@ -40,7 +42,7 @@ public static Expression getExpression(CommandContext<ServerCommandSource> conte
4042
@Override
4143
public Expression parse(StringReader reader) throws CommandSyntaxException {
4244
int start = reader.getCursor();
43-
Expression ret = new Parser(reader).parseExpression();
45+
Expression ret = new Parser(reader).parse();
4446
ret.strVal = reader.getString().substring(start, reader.getCursor());
4547
return ret;
4648
}
@@ -53,7 +55,7 @@ public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> cont
5355
Parser parser = new Parser(reader);
5456

5557
try {
56-
parser.parseExpression();
58+
parser.parse();
5759
} catch (CommandSyntaxException ignore) {
5860
}
5961

@@ -69,105 +71,64 @@ public Collection<String> getExamples() {
6971
}
7072

7173
private static class Parser {
72-
private StringReader reader;
74+
private final StringReader reader;
7375
private Consumer<SuggestionsBuilder> suggestor;
7476

7577
public Parser(StringReader reader) {
7678
this.reader = reader;
7779
}
7880

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();
10187
}
102-
103-
return left;
10488
}
10589

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();
10993
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-
}
13194

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");
140103
}
141104
}
142105

143-
return left;
106+
return expr;
144107
}
145108

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();
150112
reader.skipWhitespace();
151-
while (reader.canRead() && reader.peek() == '^') {
152-
reader.skip();
153-
reader.skipWhitespace();
154-
subExpressions.add(parseExpression4());
155-
reader.skipWhitespace();
156-
}
157113

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");
165124
}
166-
return right;
167125
}
126+
127+
return expr;
168128
}
169129

170-
private Expression parseExpression4() throws CommandSyntaxException {
130+
// <Unary> ::= ("+" | "-")* <ImplicitMult>
131+
private Expression parseUnary() throws CommandSyntaxException {
171132
boolean negative = false;
172133
while (reader.canRead() && (reader.peek() == '+' || reader.peek() == '-')) {
173134
if (reader.peek() == '-')
@@ -176,14 +137,50 @@ private Expression parseExpression4() throws CommandSyntaxException {
176137
reader.skipWhitespace();
177138
}
178139

179-
Expression right = parseExpression5();
140+
Expression right = parseImplicitMult();
180141
if (negative)
181142
return new NegateExpression(right);
182143
else
183144
return right;
184145
}
185146

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 {
187184
int cursor = reader.getCursor();
188185
suggestor = builder -> {
189186
SuggestionsBuilder newBuilder = builder.createOffset(cursor);
@@ -238,12 +235,13 @@ private Expression parseExpression5() throws CommandSyntaxException {
238235
if (reader.canRead() && reader.peek() == '(')
239236
suggestor = null;
240237

241-
Expression ret = parseExpression6();
238+
Expression ret = parseParenthesized();
242239
suggestor = null;
243240
return ret;
244241
}
245242

246-
private Expression parseExpression6() throws CommandSyntaxException {
243+
// <Parenthesized> ::= <Literal> | ("(" <Expression> ")")
244+
private Expression parseParenthesized() throws CommandSyntaxException {
247245
if (reader.canRead() && reader.peek() == '(') {
248246
reader.skip();
249247
reader.skipWhitespace();
@@ -254,10 +252,11 @@ private Expression parseExpression6() throws CommandSyntaxException {
254252
reader.skip();
255253
return ret;
256254
}
257-
return parseExpression7();
255+
return parseLiteral();
258256
}
259257

260-
private Expression parseExpression7() throws CommandSyntaxException {
258+
// <Literal> ::= ("0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" | ".")+
259+
private Expression parseLiteral() throws CommandSyntaxException {
261260
int start = reader.getCursor();
262261
while (reader.canRead() && isAllowedNumber(reader.peek()))
263262
reader.skip();
@@ -284,11 +283,10 @@ public static abstract class Expression {
284283
}
285284

286285
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;
292290

293291
public BinaryOpExpression(Expression left, Expression right, DoubleBinaryOperator operator, String type) {
294292
this.left = left;
@@ -309,7 +307,7 @@ public Text getParsedTree() {
309307
}
310308

311309
private static class NegateExpression extends Expression {
312-
private Expression right;
310+
private final Expression right;
313311

314312
public NegateExpression(Expression right) {
315313
this.right = right;
@@ -334,8 +332,8 @@ private static class ConstantExpression extends Expression {
334332
"ans", () -> TempRules.calcAnswer
335333
);
336334

337-
private String type;
338-
private DoubleSupplier constant;
335+
private final String type;
336+
private final DoubleSupplier constant;
339337

340338
public ConstantExpression(String type, DoubleSupplier constant) {
341339
this.type = type;
@@ -355,7 +353,7 @@ public Text getParsedTree() {
355353

356354
public static class FunctionExpression extends Expression {
357355

358-
private static Map<String, IFunction> FUNCTIONS = ImmutableMap.<String, IFunction>builder()
356+
private static final Map<String, IFunction> FUNCTIONS = ImmutableMap.<String, IFunction>builder()
359357
.put("sqrt", (UnaryFunction) Math::sqrt)
360358
.put("abs", (UnaryFunction) Math::abs)
361359
.put("floor", (UnaryFunction) Math::floor)
@@ -423,9 +421,9 @@ public boolean isAcceptableInputCount(int count) {
423421
.put("not", (UnaryFunction) val -> (double)(~((int)val)))
424422
.build();
425423

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;
429427

430428
public FunctionExpression(String type, IFunction function, Expression... arguments) {
431429
this.type = type;
@@ -504,7 +502,7 @@ default boolean isAcceptableInputCount(int count) {
504502
}
505503

506504
private static class LiteralExpression extends Expression {
507-
private double val;
505+
private final double val;
508506

509507
public LiteralExpression(double val) {
510508
this.val = val;

src/main/resources/assets/clientcommands/lang/en_us.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"commands.ccalc.parse.function": "(function '%s' with arguments: %s)",
1616
"commands.ccalc.parse.literal": "(literal: %s)",
1717
"commands.ccalc.parse.negate": "(negated: %s)",
18+
"commands.ccalc.tooDeeplyNested": "Expression is too deeply nested",
1819

1920
"commands.ccalcstack.success": "%d %s is %d stacks and %d extra",
2021
"commands.ccalcstack.success.empty": "%d items is %d stacks and %d extra",

0 commit comments

Comments
 (0)