tangled
alpha
login
or
join now
zenfyr.dev
/
EvalEx
0
fork
atom
a fork of EvalEx by ezylang with a handful of breaking changes
0
fork
atom
overview
issues
pulls
pipelines
We did it reddit, we fixed inlining.
zenfyr.dev
2 years ago
7694caf0
dc6e3df8
+233
-33
18 changed files
expand all
collapse all
unified
split
gradle.properties
src
main
java
me
melontini
mevalex
functions
FunctionIfc.java
basic
RandomFunction.java
datetime
DateTimeNowFunction.java
DateTimeTodayFunction.java
operators
OperatorIfc.java
parser
ASTNode.java
ExpressionParser.java
InlinedASTNode.java
test
java
me
melontini
mevalex
BaseExpressionEvaluatorTest.java
ExpressionEvaluationExceptionsTest.java
ExpressionEvaluationMultiThreadedTest.java
ExpressionEvaluatorConstantsTest.java
ExpressionEvaluatorNullTest.java
ExpressionTest.java
parser
ShuntingYardArrayTest.java
ShuntingYardConverterTest.java
ShuntingYardStructureTest.java
+1
-1
gradle.properties
···
2
2
org.gradle.daemon=true
3
3
org.gradle.caching=true
4
4
5
5
-
version=4.0.0
5
5
+
version=4.1.0
+4
src/main/java/me/melontini/mevalex/functions/FunctionIfc.java
···
88
88
int numOfParameters = getFunctionParameterDefinitions().size();
89
89
return hasVarArgs() ? numOfParameters - 1 : numOfParameters;
90
90
}
91
91
+
92
92
+
default boolean canInline() {
93
93
+
return true;
94
94
+
}
91
95
}
+5
src/main/java/me/melontini/mevalex/functions/basic/RandomFunction.java
···
32
32
33
33
return context.expression().convertDoubleValue(secureRandom.nextDouble());
34
34
}
35
35
+
36
36
+
@Override
37
37
+
public boolean canInline() {
38
38
+
return false;
39
39
+
}
35
40
}
+5
src/main/java/me/melontini/mevalex/functions/datetime/DateTimeNowFunction.java
···
43
43
EvaluationContext context, Token functionToken, EvaluationValue... parameterValues) {
44
44
return DateTimeValue.of(Instant.now());
45
45
}
46
46
+
47
47
+
@Override
48
48
+
public boolean canInline() {
49
49
+
return false;
50
50
+
}
46
51
}
+5
src/main/java/me/melontini/mevalex/functions/datetime/DateTimeTodayFunction.java
···
64
64
}
65
65
return expression.getConfiguration().getZoneId();
66
66
}
67
67
+
68
68
+
@Override
69
69
+
public boolean canInline() {
70
70
+
return false;
71
71
+
}
67
72
}
+4
src/main/java/me/melontini/mevalex/operators/OperatorIfc.java
···
129
129
EvaluationValue evaluate(
130
130
EvaluationContext context, Token operatorToken, EvaluationValue... operands)
131
131
throws EvaluationException;
132
132
+
133
133
+
default boolean canInline() {
134
134
+
return true;
135
135
+
}
132
136
}
+3
-3
src/main/java/me/melontini/mevalex/parser/ASTNode.java
···
43
43
public static final ASTNode[] EMPTY = new ASTNode[0];
44
44
45
45
/** The children od the tree. */
46
46
-
ASTNode[] parameters;
46
46
+
protected ASTNode[] parameters;
47
47
48
48
/** The token associated with this tree node. */
49
49
-
Token token;
49
49
+
protected Token token;
50
50
51
51
protected ASTNode(Token token, ASTNode... parameters) {
52
52
this.token = token;
53
53
-
this.parameters = parameters;
53
53
+
this.parameters = parameters.length == 0 ? EMPTY : parameters;
54
54
}
55
55
56
56
public static ASTNode of(Token token) {
+169
-5
src/main/java/me/melontini/mevalex/parser/ExpressionParser.java
···
15
15
*/
16
16
package me.melontini.mevalex.parser;
17
17
18
18
+
import java.util.Arrays;
19
19
+
import java.util.Objects;
18
20
import lombok.Getter;
21
21
+
import me.melontini.mevalex.EvaluationContext;
19
22
import me.melontini.mevalex.EvaluationException;
20
23
import me.melontini.mevalex.Expression;
21
24
import me.melontini.mevalex.config.ExpressionConfiguration;
···
39
42
this.converter = new ShuntingYardConverter(configuration);
40
43
}
41
44
42
42
-
public Expression parse(String expression) throws ParseException {
43
43
-
return new Expression(
44
44
-
expression,
45
45
-
this.toSolvable(converter.toAbstractSyntaxTree(tokenizer.parse(expression), expression)),
46
46
-
configuration);
45
45
+
public Expression parse(String expression) throws ParseException, EvaluationException {
46
46
+
ASTNode root = converter.toAbstractSyntaxTree(tokenizer.parse(expression), expression);
47
47
+
var proxy = new Expression(expression, toSolvable(root), configuration);
48
48
+
return new Expression(expression, toSolvable(inline(proxy, root)), configuration);
49
49
+
}
50
50
+
51
51
+
public ASTNode inline(Expression parent, ASTNode node) throws EvaluationException {
52
52
+
if (node instanceof InlinedASTNode) return tryRound(parent, node);
53
53
+
54
54
+
// We declare the index not inlineable, but its parameters on the other hand...
55
55
+
var parameters = node.getParameters();
56
56
+
for (int i = 0; i < parameters.length; i++) {
57
57
+
switch (parameters[i].getToken().getType()) {
58
58
+
case ARRAY_INDEX, STRUCTURE_SEPARATOR -> parameters[i] = inline(parent, parameters[i]);
59
59
+
}
60
60
+
}
61
61
+
62
62
+
Token token = node.getToken();
63
63
+
return tryRound(
64
64
+
parent,
65
65
+
switch (token.getType()) {
66
66
+
case VARIABLE_OR_CONSTANT -> {
67
67
+
if (!configuration.isAllowOverwriteConstants()) {
68
68
+
var result = configuration.getConstants().get(token.getValue());
69
69
+
if (result != null) yield InlinedASTNode.of(token, result);
70
70
+
}
71
71
+
yield node;
72
72
+
}
73
73
+
case PREFIX_OPERATOR, POSTFIX_OPERATOR -> inlinePrePostfix(parent, token, node);
74
74
+
case INFIX_OPERATOR -> inlineInfix(parent, token, node);
75
75
+
case FUNCTION -> inlineFunction(parent, token, node);
76
76
+
case ARRAY_INDEX -> {
77
77
+
for (int i1 = 0; i1 < 2; i1++)
78
78
+
node.getParameters()[i1] = inline(parent, node.getParameters()[i1]);
79
79
+
yield node;
80
80
+
}
81
81
+
case STRUCTURE_SEPARATOR -> {
82
82
+
node.getParameters()[0] = inline(parent, node.getParameters()[0]);
83
83
+
yield node;
84
84
+
}
85
85
+
default -> throw new IllegalStateException("Unexpected evaluation token: " + token);
86
86
+
});
87
87
+
}
88
88
+
89
89
+
private ASTNode tryRound(Expression parent, ASTNode node) {
90
90
+
if (!(node instanceof InlinedASTNode inlined)) return node;
91
91
+
92
92
+
var result = parent.tryRoundValue(inlined.value());
93
93
+
if (Objects.equals(result.getValue(), inlined.value().getValue())) return inlined;
94
94
+
return InlinedASTNode.of(node.getToken(), result, node.getParameters());
95
95
+
}
96
96
+
97
97
+
private ASTNode inlineFunction(Expression parent, Token token, ASTNode node)
98
98
+
throws EvaluationException {
99
99
+
var function = token.getFunctionDefinition();
100
100
+
if (!function.canInline()) return node;
101
101
+
var parameters = node.getParameters();
102
102
+
103
103
+
EvaluationValue[] result = new EvaluationValue[parameters.length];
104
104
+
boolean allMatch = true;
105
105
+
for (int i = 0; i < parameters.length; i++) {
106
106
+
ASTNode parameter = parameters[i];
107
107
+
108
108
+
if (function.isParameterLazy(i)) {
109
109
+
if (!canInline(parameter)) allMatch = false;
110
110
+
result[i] = SolvableValue.of(toSolvable(node));
111
111
+
} else {
112
112
+
parameters[i] = inline(parent, parameters[i]);
113
113
+
if (!(parameters[i] instanceof InlinedASTNode inlined)) {
114
114
+
allMatch = false;
115
115
+
continue;
116
116
+
}
117
117
+
result[i] = inlined.value();
118
118
+
}
119
119
+
}
120
120
+
if (!allMatch) return node;
121
121
+
122
122
+
return InlinedASTNode.of(
123
123
+
token,
124
124
+
function.evaluate(EvaluationContext.builder(parent).build(), token, result),
125
125
+
parameters);
126
126
+
}
127
127
+
128
128
+
private ASTNode inlineInfix(Expression parent, Token token, ASTNode node)
129
129
+
throws EvaluationException {
130
130
+
var operator = token.getOperatorDefinition();
131
131
+
var parameters = node.getParameters();
132
132
+
133
133
+
if (!operator.isOperandLazy()) {
134
134
+
boolean allMatch = true;
135
135
+
for (int i = 0; i < 2; i++) {
136
136
+
if (!((parameters[i] = inline(parent, parameters[i])) instanceof InlinedASTNode))
137
137
+
allMatch = false;
138
138
+
}
139
139
+
if (!allMatch) return node;
140
140
+
141
141
+
if (operator.canInline()) {
142
142
+
return InlinedASTNode.of(
143
143
+
token,
144
144
+
operator.evaluate(
145
145
+
EvaluationContext.builder(parent).build(),
146
146
+
token,
147
147
+
Arrays.stream(parameters)
148
148
+
.map(node1 -> ((InlinedASTNode) node1).value())
149
149
+
.toArray(EvaluationValue[]::new)),
150
150
+
parameters);
151
151
+
}
152
152
+
return node;
153
153
+
} else {
154
154
+
if (!operator.canInline()) return node;
155
155
+
156
156
+
SolvableValue[] lazy = new SolvableValue[parameters.length];
157
157
+
for (int i = 0; i < parameters.length; i++) {
158
158
+
ASTNode parameter = parameters[i];
159
159
+
if (!canInline(parameter)) return node;
160
160
+
lazy[i] = SolvableValue.of(toSolvable(parameter));
161
161
+
}
162
162
+
return InlinedASTNode.of(
163
163
+
token,
164
164
+
operator.evaluate(EvaluationContext.builder(parent).build(), token, lazy),
165
165
+
parameters);
166
166
+
}
167
167
+
}
168
168
+
169
169
+
/**
170
170
+
* When working with lazy operand we cannot immediately inline the operand as it can throw an
171
171
+
* {@link EvaluationException}.
172
172
+
*
173
173
+
* @return If the node can be safely inlined.
174
174
+
*/
175
175
+
private boolean canInline(ASTNode node) {
176
176
+
if (node instanceof InlinedASTNode) return true;
177
177
+
178
178
+
Token token = node.getToken();
179
179
+
return switch (token.getType()) {
180
180
+
case VARIABLE_OR_CONSTANT -> !configuration.isAllowOverwriteConstants()
181
181
+
&& configuration.getConstants().containsKey(token.getValue());
182
182
+
case PREFIX_OPERATOR, POSTFIX_OPERATOR, INFIX_OPERATOR -> {
183
183
+
if (!token.getOperatorDefinition().canInline()) yield false;
184
184
+
for (ASTNode parameter : node.getParameters()) {
185
185
+
if (!canInline(parameter)) yield false;
186
186
+
}
187
187
+
yield true;
188
188
+
}
189
189
+
case FUNCTION -> {
190
190
+
if (!token.getFunctionDefinition().canInline()) yield false;
191
191
+
for (ASTNode parameter : node.getParameters()) {
192
192
+
if (!canInline(parameter)) yield false;
193
193
+
}
194
194
+
yield true;
195
195
+
}
196
196
+
default -> false;
197
197
+
};
198
198
+
}
199
199
+
200
200
+
private ASTNode inlinePrePostfix(Expression parent, Token token, ASTNode node)
201
201
+
throws EvaluationException {
202
202
+
var operator = token.getOperatorDefinition();
203
203
+
node.getParameters()[0] = inline(parent, node.getParameters()[0]);
204
204
+
if (node.getParameters()[0] instanceof InlinedASTNode inlined && operator.canInline()) {
205
205
+
return InlinedASTNode.of(
206
206
+
token,
207
207
+
operator.evaluate(EvaluationContext.builder(parent).build(), token, inlined.value()),
208
208
+
node.getParameters());
209
209
+
}
210
210
+
return node;
47
211
}
48
212
49
213
public Solvable toSolvable(ASTNode node) {
+14
-2
src/main/java/me/melontini/mevalex/parser/InlinedASTNode.java
···
15
15
*/
16
16
package me.melontini.mevalex.parser;
17
17
18
18
+
import java.util.Arrays;
19
19
+
import java.util.stream.Collectors;
18
20
import lombok.AccessLevel;
19
21
import lombok.EqualsAndHashCode;
20
22
import lombok.Getter;
···
47
49
return new InlinedASTNode(token, constant, nodes);
48
50
}
49
51
50
50
-
static InlinedASTNode trusted(Token token, EvaluationValue constant, ASTNode... nodes) {
51
51
-
return new InlinedASTNode(token, constant, nodes);
52
52
+
public String toJSON() {
53
53
+
if (parameters.length == 0) {
54
54
+
return String.format(
55
55
+
"{" + "\"type\":\"%s\",\"value\":\"%s\",\"result\":\"%s\"}",
56
56
+
token.getType(), token.getValue(), value.getStringValue());
57
57
+
} else {
58
58
+
String childrenJson =
59
59
+
Arrays.stream(parameters).map(ASTNode::toJSON).collect(Collectors.joining(","));
60
60
+
return String.format(
61
61
+
"{" + "\"type\":\"%s\",\"value\":\"%s\",\"result\":\"%s\",\"children\":[%s]}",
62
62
+
token.getType(), token.getValue(), value.getStringValue(), childrenJson);
63
63
+
}
52
64
}
53
65
54
66
@Override
+1
-1
src/test/java/me/melontini/mevalex/BaseExpressionEvaluatorTest.java
···
32
32
return expression.evaluate(EvaluationContext.builder(expression).build()).getStringValue();
33
33
}
34
34
35
35
-
Expression createExpression(String expressionString) throws ParseException {
35
35
+
Expression createExpression(String expressionString) throws ParseException, EvaluationException {
36
36
return parser.parse(expressionString);
37
37
}
38
38
}
+1
-1
src/test/java/me/melontini/mevalex/ExpressionEvaluationExceptionsTest.java
···
26
26
class ExpressionEvaluationExceptionsTest {
27
27
28
28
@Test
29
29
-
void testUnexpectedToken() throws ParseException {
29
29
+
void testUnexpectedToken() throws ParseException, EvaluationException {
30
30
Expression expression = ExpressionConfiguration.defaultExpressionParser().parse("1");
31
31
32
32
assertThatThrownBy(
+1
-1
src/test/java/me/melontini/mevalex/ExpressionEvaluationMultiThreadedTest.java
···
30
30
31
31
class ExpressionEvaluationMultiThreadedTest {
32
32
@Test
33
33
-
void testThreadLocal() throws InterruptedException, ParseException {
33
33
+
void testThreadLocal() throws InterruptedException, ParseException, EvaluationException {
34
34
35
35
AtomicInteger errorCount = new AtomicInteger();
36
36
+1
-1
src/test/java/me/melontini/mevalex/ExpressionEvaluatorConstantsTest.java
···
92
92
}
93
93
94
94
@Test
95
95
-
void testOverwriteConstantsNotAllowed() throws ParseException {
95
95
+
void testOverwriteConstantsNotAllowed() throws ParseException, EvaluationException {
96
96
Expression expression = ExpressionConfiguration.defaultExpressionParser().parse("e");
97
97
assertThatThrownBy(() -> expression.evaluate(builder -> builder.parameter("e", 9)))
98
98
.isInstanceOf(UnsupportedOperationException.class)
+1
-1
src/test/java/me/melontini/mevalex/ExpressionEvaluatorNullTest.java
···
76
76
}
77
77
78
78
@Test
79
79
-
void testFailWithNoHandling() throws ParseException {
79
79
+
void testFailWithNoHandling() throws ParseException, EvaluationException {
80
80
Expression expression1 = createExpression("a * 5");
81
81
assertThatThrownBy(() -> expression1.evaluate(builder -> builder.parameter("a", null)))
82
82
.isInstanceOf(EvaluationException.class)
+6
-5
src/test/java/me/melontini/mevalex/ExpressionTest.java
···
35
35
class ExpressionTest {
36
36
37
37
@Test
38
38
-
void testExpressionDefaults() throws ParseException {
38
38
+
void testExpressionDefaults() throws ParseException, EvaluationException {
39
39
Expression expression = ExpressionConfiguration.defaultExpressionParser().parse("a+b");
40
40
41
41
assertThat(expression.getExpressionString()).isEqualTo("a+b");
···
51
51
}
52
52
53
53
@Test
54
54
-
void testValidateOK() throws ParseException {
54
54
+
void testValidateOK() throws ParseException, EvaluationException {
55
55
ExpressionConfiguration.defaultExpressionParser().parse("1+1");
56
56
}
57
57
···
131
131
132
132
@SuppressWarnings("Convert2Lambda")
133
133
@Test
134
134
-
void testDefaultExpressionOwnsOwnConfigurationEntries() throws ParseException {
134
134
+
void testDefaultExpressionOwnsOwnConfigurationEntries()
135
135
+
throws ParseException, EvaluationException {
135
136
Supplier<ExpressionConfiguration> configuration =
136
137
() ->
137
138
ExpressionConfiguration.builder()
···
158
159
}
159
160
160
161
@Test
161
161
-
void testDoubleConverterDefaultMathContext() throws ParseException {
162
162
+
void testDoubleConverterDefaultMathContext() throws ParseException, EvaluationException {
162
163
Expression defaultMathContextExpression =
163
164
ExpressionConfiguration.defaultExpressionParser().parse("1");
164
165
assertThat(defaultMathContextExpression.convertDoubleValue(1.67987654321).getNumberValue())
···
166
167
}
167
168
168
169
@Test
169
169
-
void testDoubleConverterLimitedMathContext() throws ParseException {
170
170
+
void testDoubleConverterLimitedMathContext() throws ParseException, EvaluationException {
170
171
Expression limitedMathContextExpression =
171
172
new ExpressionParser(
172
173
ExpressionConfiguration.builder().mathContext(new MathContext(3)).build())
+3
-3
src/test/java/me/melontini/mevalex/parser/ShuntingYardArrayTest.java
···
23
23
void testSimpleArray() throws ParseException {
24
24
assertASTTreeIsEqualTo(
25
25
"a[0]",
26
26
-
"{\"type\":\"ARRAY_INDEX\",\"value\":\"[\",\"children\":[{\"type\":\"VARIABLE_OR_CONSTANT\",\"value\":\"a\"},{\"type\":\"NUMBER_LITERAL\",\"value\":\"0\"}]}");
26
26
+
"{\"type\":\"ARRAY_INDEX\",\"value\":\"[\",\"children\":[{\"type\":\"VARIABLE_OR_CONSTANT\",\"value\":\"a\"},{\"type\":\"NUMBER_LITERAL\",\"value\":\"0\",\"result\":\"0\"}]}");
27
27
}
28
28
29
29
@Test
···
37
37
void testArrayNested() throws ParseException {
38
38
assertASTTreeIsEqualTo(
39
39
"a[b[1]]",
40
40
-
"{\"type\":\"ARRAY_INDEX\",\"value\":\"[\",\"children\":[{\"type\":\"VARIABLE_OR_CONSTANT\",\"value\":\"a\"},{\"type\":\"ARRAY_INDEX\",\"value\":\"[\",\"children\":[{\"type\":\"VARIABLE_OR_CONSTANT\",\"value\":\"b\"},{\"type\":\"NUMBER_LITERAL\",\"value\":\"1\"}]}]}");
40
40
+
"{\"type\":\"ARRAY_INDEX\",\"value\":\"[\",\"children\":[{\"type\":\"VARIABLE_OR_CONSTANT\",\"value\":\"a\"},{\"type\":\"ARRAY_INDEX\",\"value\":\"[\",\"children\":[{\"type\":\"VARIABLE_OR_CONSTANT\",\"value\":\"b\"},{\"type\":\"NUMBER_LITERAL\",\"value\":\"1\",\"result\":\"1\"}]}]}");
41
41
}
42
42
43
43
@Test
44
44
void testComplex() throws ParseException {
45
45
assertASTTreeIsEqualTo(
46
46
"a[b[100*(a+b)]-c[2+d[x+y]]]",
47
47
-
"{\"type\":\"ARRAY_INDEX\",\"value\":\"[\",\"children\":[{\"type\":\"VARIABLE_OR_CONSTANT\",\"value\":\"a\"},{\"type\":\"INFIX_OPERATOR\",\"value\":\"-\",\"children\":[{\"type\":\"ARRAY_INDEX\",\"value\":\"[\",\"children\":[{\"type\":\"VARIABLE_OR_CONSTANT\",\"value\":\"b\"},{\"type\":\"INFIX_OPERATOR\",\"value\":\"*\",\"children\":[{\"type\":\"NUMBER_LITERAL\",\"value\":\"100\"},{\"type\":\"INFIX_OPERATOR\",\"value\":\"+\",\"children\":[{\"type\":\"VARIABLE_OR_CONSTANT\",\"value\":\"a\"},{\"type\":\"VARIABLE_OR_CONSTANT\",\"value\":\"b\"}]}]}]},{\"type\":\"ARRAY_INDEX\",\"value\":\"[\",\"children\":[{\"type\":\"VARIABLE_OR_CONSTANT\",\"value\":\"c\"},{\"type\":\"INFIX_OPERATOR\",\"value\":\"+\",\"children\":[{\"type\":\"NUMBER_LITERAL\",\"value\":\"2\"},{\"type\":\"ARRAY_INDEX\",\"value\":\"[\",\"children\":[{\"type\":\"VARIABLE_OR_CONSTANT\",\"value\":\"d\"},{\"type\":\"INFIX_OPERATOR\",\"value\":\"+\",\"children\":[{\"type\":\"VARIABLE_OR_CONSTANT\",\"value\":\"x\"},{\"type\":\"VARIABLE_OR_CONSTANT\",\"value\":\"y\"}]}]}]}]}]}]}");
47
47
+
"{\"type\":\"ARRAY_INDEX\",\"value\":\"[\",\"children\":[{\"type\":\"VARIABLE_OR_CONSTANT\",\"value\":\"a\"},{\"type\":\"INFIX_OPERATOR\",\"value\":\"-\",\"children\":[{\"type\":\"ARRAY_INDEX\",\"value\":\"[\",\"children\":[{\"type\":\"VARIABLE_OR_CONSTANT\",\"value\":\"b\"},{\"type\":\"INFIX_OPERATOR\",\"value\":\"*\",\"children\":[{\"type\":\"NUMBER_LITERAL\",\"value\":\"100\",\"result\":\"100\"},{\"type\":\"INFIX_OPERATOR\",\"value\":\"+\",\"children\":[{\"type\":\"VARIABLE_OR_CONSTANT\",\"value\":\"a\"},{\"type\":\"VARIABLE_OR_CONSTANT\",\"value\":\"b\"}]}]}]},{\"type\":\"ARRAY_INDEX\",\"value\":\"[\",\"children\":[{\"type\":\"VARIABLE_OR_CONSTANT\",\"value\":\"c\"},{\"type\":\"INFIX_OPERATOR\",\"value\":\"+\",\"children\":[{\"type\":\"NUMBER_LITERAL\",\"value\":\"2\",\"result\":\"2\"},{\"type\":\"ARRAY_INDEX\",\"value\":\"[\",\"children\":[{\"type\":\"VARIABLE_OR_CONSTANT\",\"value\":\"d\"},{\"type\":\"INFIX_OPERATOR\",\"value\":\"+\",\"children\":[{\"type\":\"VARIABLE_OR_CONSTANT\",\"value\":\"x\"},{\"type\":\"VARIABLE_OR_CONSTANT\",\"value\":\"y\"}]}]}]}]}]}]}");
48
48
}
49
49
}
+8
-8
src/test/java/me/melontini/mevalex/parser/ShuntingYardConverterTest.java
···
21
21
22
22
@Test
23
23
void testSingleNumber() throws ParseException {
24
24
-
assertASTTreeIsEqualTo("1", "{\"type\":\"NUMBER_LITERAL\",\"value\":\"1\"}");
24
24
+
assertASTTreeIsEqualTo("1", "{\"type\":\"NUMBER_LITERAL\",\"value\":\"1\",\"result\":\"1\"}");
25
25
}
26
26
27
27
@Test
···
33
33
void testPrefix() throws ParseException {
34
34
assertASTTreeIsEqualTo(
35
35
"-1",
36
36
-
"{\"type\":\"PREFIX_OPERATOR\",\"value\":\"-\",\"children\":[{\"type\":\"NUMBER_LITERAL\",\"value\":\"1\"}]}");
36
36
+
"{\"type\":\"PREFIX_OPERATOR\",\"value\":\"-\",\"children\":[{\"type\":\"NUMBER_LITERAL\",\"value\":\"1\",\"result\":\"1\"}]}");
37
37
}
38
38
39
39
@Test
40
40
void testPostfix() throws ParseException {
41
41
assertASTTreeIsEqualTo(
42
42
"1?",
43
43
-
"{\"type\":\"POSTFIX_OPERATOR\",\"value\":\"?\",\"children\":[{\"type\":\"NUMBER_LITERAL\",\"value\":\"1\"}]}");
43
43
+
"{\"type\":\"POSTFIX_OPERATOR\",\"value\":\"?\",\"children\":[{\"type\":\"NUMBER_LITERAL\",\"value\":\"1\",\"result\":\"1\"}]}");
44
44
}
45
45
46
46
@Test
47
47
void testPrefixPostfix() throws ParseException {
48
48
assertASTTreeIsEqualTo(
49
49
"-1?",
50
50
-
"{\"type\":\"PREFIX_OPERATOR\",\"value\":\"-\",\"children\":[{\"type\":\"POSTFIX_OPERATOR\",\"value\":\"?\",\"children\":[{\"type\":\"NUMBER_LITERAL\",\"value\":\"1\"}]}]}");
50
50
+
"{\"type\":\"PREFIX_OPERATOR\",\"value\":\"-\",\"children\":[{\"type\":\"POSTFIX_OPERATOR\",\"value\":\"?\",\"children\":[{\"type\":\"NUMBER_LITERAL\",\"value\":\"1\",\"result\":\"1\"}]}]}");
51
51
}
52
52
53
53
@Test
54
54
void testSequential() throws ParseException {
55
55
assertASTTreeIsEqualTo(
56
56
"1+2+3-3-2-1",
57
57
-
"{\"type\":\"INFIX_OPERATOR\",\"value\":\"-\",\"children\":[{\"type\":\"INFIX_OPERATOR\",\"value\":\"-\",\"children\":[{\"type\":\"INFIX_OPERATOR\",\"value\":\"-\",\"children\":[{\"type\":\"INFIX_OPERATOR\",\"value\":\"+\",\"children\":[{\"type\":\"INFIX_OPERATOR\",\"value\":\"+\",\"children\":[{\"type\":\"NUMBER_LITERAL\",\"value\":\"1\"},{\"type\":\"NUMBER_LITERAL\",\"value\":\"2\"}]},{\"type\":\"NUMBER_LITERAL\",\"value\":\"3\"}]},{\"type\":\"NUMBER_LITERAL\",\"value\":\"3\"}]},{\"type\":\"NUMBER_LITERAL\",\"value\":\"2\"}]},{\"type\":\"NUMBER_LITERAL\",\"value\":\"1\"}]}");
57
57
+
"{\"type\":\"INFIX_OPERATOR\",\"value\":\"-\",\"children\":[{\"type\":\"INFIX_OPERATOR\",\"value\":\"-\",\"children\":[{\"type\":\"INFIX_OPERATOR\",\"value\":\"-\",\"children\":[{\"type\":\"INFIX_OPERATOR\",\"value\":\"+\",\"children\":[{\"type\":\"INFIX_OPERATOR\",\"value\":\"+\",\"children\":[{\"type\":\"NUMBER_LITERAL\",\"value\":\"1\",\"result\":\"1\"},{\"type\":\"NUMBER_LITERAL\",\"value\":\"2\",\"result\":\"2\"}]},{\"type\":\"NUMBER_LITERAL\",\"value\":\"3\",\"result\":\"3\"}]},{\"type\":\"NUMBER_LITERAL\",\"value\":\"3\",\"result\":\"3\"}]},{\"type\":\"NUMBER_LITERAL\",\"value\":\"2\",\"result\":\"2\"}]},{\"type\":\"NUMBER_LITERAL\",\"value\":\"1\",\"result\":\"1\"}]}");
58
58
}
59
59
60
60
@Test
61
61
void testPrecedence() throws ParseException {
62
62
assertASTTreeIsEqualTo(
63
63
"1+2*3-3^2-1/4",
64
64
-
"{\"type\":\"INFIX_OPERATOR\",\"value\":\"-\",\"children\":[{\"type\":\"INFIX_OPERATOR\",\"value\":\"-\",\"children\":[{\"type\":\"INFIX_OPERATOR\",\"value\":\"+\",\"children\":[{\"type\":\"NUMBER_LITERAL\",\"value\":\"1\"},{\"type\":\"INFIX_OPERATOR\",\"value\":\"*\",\"children\":[{\"type\":\"NUMBER_LITERAL\",\"value\":\"2\"},{\"type\":\"NUMBER_LITERAL\",\"value\":\"3\"}]}]},{\"type\":\"INFIX_OPERATOR\",\"value\":\"^\",\"children\":[{\"type\":\"NUMBER_LITERAL\",\"value\":\"3\"},{\"type\":\"NUMBER_LITERAL\",\"value\":\"2\"}]}]},{\"type\":\"INFIX_OPERATOR\",\"value\":\"/\",\"children\":[{\"type\":\"NUMBER_LITERAL\",\"value\":\"1\"},{\"type\":\"NUMBER_LITERAL\",\"value\":\"4\"}]}]}");
64
64
+
"{\"type\":\"INFIX_OPERATOR\",\"value\":\"-\",\"children\":[{\"type\":\"INFIX_OPERATOR\",\"value\":\"-\",\"children\":[{\"type\":\"INFIX_OPERATOR\",\"value\":\"+\",\"children\":[{\"type\":\"NUMBER_LITERAL\",\"value\":\"1\",\"result\":\"1\"},{\"type\":\"INFIX_OPERATOR\",\"value\":\"*\",\"children\":[{\"type\":\"NUMBER_LITERAL\",\"value\":\"2\",\"result\":\"2\"},{\"type\":\"NUMBER_LITERAL\",\"value\":\"3\",\"result\":\"3\"}]}]},{\"type\":\"INFIX_OPERATOR\",\"value\":\"^\",\"children\":[{\"type\":\"NUMBER_LITERAL\",\"value\":\"3\",\"result\":\"3\"},{\"type\":\"NUMBER_LITERAL\",\"value\":\"2\",\"result\":\"2\"}]}]},{\"type\":\"INFIX_OPERATOR\",\"value\":\"/\",\"children\":[{\"type\":\"NUMBER_LITERAL\",\"value\":\"1\",\"result\":\"1\"},{\"type\":\"NUMBER_LITERAL\",\"value\":\"4\",\"result\":\"4\"}]}]}");
65
65
}
66
66
67
67
@Test
68
68
void testBraces() throws ParseException {
69
69
assertASTTreeIsEqualTo(
70
70
"2*(1/(2+3))",
71
71
-
"{\"type\":\"INFIX_OPERATOR\",\"value\":\"*\",\"children\":[{\"type\":\"NUMBER_LITERAL\",\"value\":\"2\"},{\"type\":\"INFIX_OPERATOR\",\"value\":\"/\",\"children\":[{\"type\":\"NUMBER_LITERAL\",\"value\":\"1\"},{\"type\":\"INFIX_OPERATOR\",\"value\":\"+\",\"children\":[{\"type\":\"NUMBER_LITERAL\",\"value\":\"2\"},{\"type\":\"NUMBER_LITERAL\",\"value\":\"3\"}]}]}]}");
71
71
+
"{\"type\":\"INFIX_OPERATOR\",\"value\":\"*\",\"children\":[{\"type\":\"NUMBER_LITERAL\",\"value\":\"2\",\"result\":\"2\"},{\"type\":\"INFIX_OPERATOR\",\"value\":\"/\",\"children\":[{\"type\":\"NUMBER_LITERAL\",\"value\":\"1\",\"result\":\"1\"},{\"type\":\"INFIX_OPERATOR\",\"value\":\"+\",\"children\":[{\"type\":\"NUMBER_LITERAL\",\"value\":\"2\",\"result\":\"2\"},{\"type\":\"NUMBER_LITERAL\",\"value\":\"3\",\"result\":\"3\"}]}]}]}");
72
72
}
73
73
74
74
@Test
75
75
void testFunctions() throws ParseException {
76
76
assertASTTreeIsEqualTo(
77
77
"MAX(1,2,3)-MIN(3,2,SUM(1,2,3))",
78
78
-
"{\"type\":\"INFIX_OPERATOR\",\"value\":\"-\",\"children\":[{\"type\":\"FUNCTION\",\"value\":\"MAX\",\"children\":[{\"type\":\"NUMBER_LITERAL\",\"value\":\"1\"},{\"type\":\"NUMBER_LITERAL\",\"value\":\"2\"},{\"type\":\"NUMBER_LITERAL\",\"value\":\"3\"}]},{\"type\":\"FUNCTION\",\"value\":\"MIN\",\"children\":[{\"type\":\"NUMBER_LITERAL\",\"value\":\"3\"},{\"type\":\"NUMBER_LITERAL\",\"value\":\"2\"},{\"type\":\"FUNCTION\",\"value\":\"SUM\",\"children\":[{\"type\":\"NUMBER_LITERAL\",\"value\":\"1\"},{\"type\":\"NUMBER_LITERAL\",\"value\":\"2\"},{\"type\":\"NUMBER_LITERAL\",\"value\":\"3\"}]}]}]}");
78
78
+
"{\"type\":\"INFIX_OPERATOR\",\"value\":\"-\",\"children\":[{\"type\":\"FUNCTION\",\"value\":\"MAX\",\"children\":[{\"type\":\"NUMBER_LITERAL\",\"value\":\"1\",\"result\":\"1\"},{\"type\":\"NUMBER_LITERAL\",\"value\":\"2\",\"result\":\"2\"},{\"type\":\"NUMBER_LITERAL\",\"value\":\"3\",\"result\":\"3\"}]},{\"type\":\"FUNCTION\",\"value\":\"MIN\",\"children\":[{\"type\":\"NUMBER_LITERAL\",\"value\":\"3\",\"result\":\"3\"},{\"type\":\"NUMBER_LITERAL\",\"value\":\"2\",\"result\":\"2\"},{\"type\":\"FUNCTION\",\"value\":\"SUM\",\"children\":[{\"type\":\"NUMBER_LITERAL\",\"value\":\"1\",\"result\":\"1\"},{\"type\":\"NUMBER_LITERAL\",\"value\":\"2\",\"result\":\"2\"},{\"type\":\"NUMBER_LITERAL\",\"value\":\"3\",\"result\":\"3\"}]}]}]}");
79
79
}
80
80
}
+1
-1
src/test/java/me/melontini/mevalex/parser/ShuntingYardStructureTest.java
···
40
40
void testArrayCombination() throws ParseException {
41
41
assertASTTreeIsEqualTo(
42
42
"order[4].position[2].amount",
43
43
-
"{\"type\":\"STRUCTURE_SEPARATOR\",\"value\":\".\",\"children\":[{\"type\":\"ARRAY_INDEX\",\"value\":\"[\",\"children\":[{\"type\":\"STRUCTURE_SEPARATOR\",\"value\":\".\",\"children\":[{\"type\":\"ARRAY_INDEX\",\"value\":\"[\",\"children\":[{\"type\":\"VARIABLE_OR_CONSTANT\",\"value\":\"order\"},{\"type\":\"NUMBER_LITERAL\",\"value\":\"4\"}]},{\"type\":\"VARIABLE_OR_CONSTANT\",\"value\":\"position\"}]},{\"type\":\"NUMBER_LITERAL\",\"value\":\"2\"}]},{\"type\":\"VARIABLE_OR_CONSTANT\",\"value\":\"amount\"}]}");
43
43
+
"{\"type\":\"STRUCTURE_SEPARATOR\",\"value\":\".\",\"children\":[{\"type\":\"ARRAY_INDEX\",\"value\":\"[\",\"children\":[{\"type\":\"STRUCTURE_SEPARATOR\",\"value\":\".\",\"children\":[{\"type\":\"ARRAY_INDEX\",\"value\":\"[\",\"children\":[{\"type\":\"VARIABLE_OR_CONSTANT\",\"value\":\"order\"},{\"type\":\"NUMBER_LITERAL\",\"value\":\"4\",\"result\":\"4\"}]},{\"type\":\"VARIABLE_OR_CONSTANT\",\"value\":\"position\"}]},{\"type\":\"NUMBER_LITERAL\",\"value\":\"2\",\"result\":\"2\"}]},{\"type\":\"VARIABLE_OR_CONSTANT\",\"value\":\"amount\"}]}");
44
44
}
45
45
46
46
@Test