Skip to content

Commit 5cd171b

Browse files
committed
Beta 2.2.3.2518.0
-Optimized token searching in `BooleanExpressionEvaluator` and `ExpressionEvaluator` -Now, `compare_items` UI Element Type will allow disabling the `strict` mode, thus not requiring equal data -Fixed `BooleanExpressionEvaluator` failing when using boolean variables without a fallback
1 parent 032a85e commit 5cd171b

5 files changed

Lines changed: 151 additions & 84 deletions

File tree

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
org.gradle.jvmargs=-Xmx3072M
22

33
archives_base_name=FactoryAPI
4-
mod_version=2.2.3.2517.0
4+
mod_version=2.2.3.2518.0
55
mod_id=factory_api
66
mod_name=Factory API
77
mod_description=An API that is the basis for mods like Legacy4J, Factocrafty and Better Furnaces Reforged, that work on different mod loaders.

src/main/java/wily/factoryapi/base/client/UIDefinitionManager.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -281,8 +281,8 @@ protected void applyValue() {
281281
ElementType RENDER_ITEMS = registerConditional("render_items", ElementType::parseRenderItemsElements);
282282
ElementType COMPARE_ITEMS = registerConditional("compare_items", ((uiDefinition, accessorFunction, elementName, element) -> {
283283
parseElements(uiDefinition, elementName, element, (s,d)-> parseItemStackElement(elementName, s, d), "firstItem","secondItem");
284-
parseElements(uiDefinition, elementName, element, (s,d)-> parseBooleanElement(elementName, s, d), "checkCount");
285-
uiDefinition.getDefinitions().add(UIDefinition.createBeforeInit(elementName, a -> a.getElements().put(elementName, ()-> FactoryItemUtil.compareItems(a.getElementValue(elementName+".firstItem", null, ItemStack.class), a.getElementValue(elementName+".secondItem", null, ItemStack.class), a.getBoolean(elementName+".checkCount",true)))));
284+
parseElements(uiDefinition, elementName, element, (s,d)-> parseBooleanElement(elementName, s, d), "checkCount", "strict");
285+
uiDefinition.getDefinitions().add(UIDefinition.createBeforeInit(elementName, a -> a.getElements().put(elementName, ()-> FactoryItemUtil.compareItems(a.getElementValue(elementName+".firstItem", null, ItemStack.class), a.getElementValue(elementName+".secondItem", null, ItemStack.class), a.getBoolean(elementName+".checkCount",true), a.getBoolean(elementName+".strict",true)))));
286286
}));
287287
ElementType CHANCE = registerConditional("chance", ((uiDefinition, accessorFunction, elementName, element) -> {
288288
RandomSource rand = RandomSource.create();

src/main/java/wily/factoryapi/util/BooleanExpressionEvaluator.java

Lines changed: 63 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@
1313
import java.util.regex.Matcher;
1414
import java.util.regex.Pattern;
1515

16-
public record BooleanExpressionEvaluator(String expression, List<Token> tokens, Stack<Boolean> values, Stack<Operator> operators) {
16+
public record BooleanExpressionEvaluator(String expression, List<Token> tokens, Stack<Boolean> values, Stack<Operator> operators, TokenProcessor processor) {
1717
public static final LoadingCache<String, BooleanExpressionEvaluator> EXPRESSION_CACHE = CacheBuilder.newBuilder().expireAfterWrite(Duration.ofMinutes(15)).build(CacheLoader.from(BooleanExpressionEvaluator::create));
1818

1919
private static final Pattern TOKEN_PATTERN = Pattern.compile("(\\$\\{[\\dA-Za-z_.-]+})|(:?(?:false|true))|(:?\\d+\\.?\\d*)|(!|&&|&|\\|\\||\\|)|(==|!=|>=|<=|>|<)");
2020

2121
public BooleanExpressionEvaluator(String expression, List<Token> tokens){
22-
this(expression, tokens, new Stack<>(), new Stack<>());
22+
this(expression, tokens, new Stack<>(), new Stack<>(), new TokenProcessor());
2323
}
2424

2525
public static BooleanExpressionEvaluator create(String expression) {
@@ -35,8 +35,8 @@ public Boolean evaluate(VariableResolver variableResolver) {
3535
values.clear();
3636
operators.clear();
3737
try {
38-
for (Token token : tokens) {
39-
token.process(this, variableResolver);
38+
for (int i = 0; i < tokens.size(); i++) {
39+
processor.process(i, this, variableResolver);
4040
}
4141

4242
while (!operators.isEmpty()) {
@@ -75,67 +75,90 @@ public static List<Token> tokenize(String expression) {
7575
}
7676

7777
public interface Token {
78-
void process(BooleanExpressionEvaluator evaluator, VariableResolver variableResolver);
79-
default Token relativeToken(BooleanExpressionEvaluator evaluator, int ordinal){
80-
int tokenIndex = evaluator.tokens.indexOf(this) + ordinal;
81-
return tokenIndex < evaluator.tokens.size() && tokenIndex >= 0 ? null : evaluator.tokens.get(tokenIndex);
78+
void process(TokenProcessor processor);
79+
}
80+
81+
public static class TokenProcessor {
82+
private BooleanExpressionEvaluator evaluator;
83+
private int index;
84+
private VariableResolver resolver;
85+
86+
public Token relative(int offset){
87+
int relative = index + offset;
88+
return relative < evaluator.tokens.size() && relative >= 0 ? evaluator.tokens.get(relative) : null;
89+
}
90+
91+
public void process(int index, BooleanExpressionEvaluator evaluator, VariableResolver resolver){
92+
this.index = index;
93+
this.evaluator = evaluator;
94+
this.resolver = resolver;
95+
evaluator.tokens.get(index).process(this);
96+
}
97+
98+
public BooleanExpressionEvaluator evaluator(){
99+
return evaluator;
100+
}
101+
102+
public VariableResolver resolver() {
103+
return resolver;
82104
}
83105
}
84106

85107
public record Variable(String name) implements NumberLikeValue, BooleanLikeValue {
86108
@Override
87-
public Number numberValue(BooleanExpressionEvaluator evaluator, VariableResolver variableResolver) {
88-
return variableResolver.getNumber(name, relativeToken(evaluator, 1) instanceof NumberLikeValue value ? value.numberValue(evaluator, variableResolver) : null);
109+
public Number numberValue(TokenProcessor processor) {
110+
return processor.resolver().getNumber(name, processor.relative(1) instanceof NumberLikeValue value ? value.numberValue(processor) : null);
89111
}
90112

91113
@Override
92-
public Boolean booleanValue(BooleanExpressionEvaluator evaluator, VariableResolver variableResolver) {
93-
return variableResolver.getBoolean(name, relativeToken(evaluator, 1) instanceof BooleanLikeValue value ? value.booleanValue(evaluator, variableResolver) : null);
114+
public Boolean booleanValue(TokenProcessor processor) {
115+
return processor.resolver().getBoolean(name, processor.relative(1) instanceof BooleanLikeValue value ? value.booleanValue(processor) : null);
94116
}
95117

96118
@Override
97-
public void process(BooleanExpressionEvaluator evaluator, VariableResolver variableResolver) {
98-
if (numberValue(evaluator, variableResolver) != null) return;
99-
BooleanLikeValue.super.process(evaluator, variableResolver);
119+
public void process(TokenProcessor processor) {
120+
if (numberValue(processor) != null) return;
121+
BooleanLikeValue.super.process(processor);
100122
}
101123
}
102124

103125
public record BooleanValue(boolean value) implements BooleanLikeValue {
104126
@Override
105-
public Boolean booleanValue(BooleanExpressionEvaluator evaluator, VariableResolver variableResolver) {
127+
public Boolean booleanValue(TokenProcessor processor) {
106128
return value;
107129
}
108130

109131
@Override
110-
public void process(BooleanExpressionEvaluator evaluator, VariableResolver variableResolver) {
111-
if (relativeToken(evaluator, -1) instanceof Variable v && v.booleanValue(evaluator, variableResolver) != null) return;
112-
BooleanLikeValue.super.process(evaluator, variableResolver);
132+
public void process(TokenProcessor processor) {
133+
if (processor.relative(-1) instanceof Variable v && v.booleanValue(processor) != null) return;
134+
BooleanLikeValue.super.process(processor);
113135
}
114136
}
115137

116138
public interface BooleanLikeValue extends Token {
117-
Boolean booleanValue(BooleanExpressionEvaluator evaluator, VariableResolver variableResolver);
118-
default void process(BooleanExpressionEvaluator evaluator, VariableResolver variableResolver) {
139+
Boolean booleanValue(TokenProcessor processor);
140+
141+
default void process(TokenProcessor processor) {
119142
Boolean variable;
120-
if ((variable = booleanValue(evaluator, variableResolver)) == null) return;
121-
if (!evaluator.operators.isEmpty() && evaluator.operators.peek().symbol().equals("!")) {
143+
if ((variable = booleanValue(processor)) == null) return;
144+
if (!processor.evaluator().operators.isEmpty() && processor.evaluator().operators.peek().symbol().equals("!")) {
122145
variable = !variable;
123-
evaluator.operators.pop();
146+
processor.evaluator().operators.pop();
124147
}
125-
evaluator.values.push(variable);
148+
processor.evaluator().values.push(variable);
126149
}
127150
}
128151

129152
public interface NumberLikeValue extends Token {
130-
Number numberValue(BooleanExpressionEvaluator evaluator, VariableResolver variableResolver);
153+
Number numberValue(TokenProcessor processor);
131154
@Override
132-
default void process(BooleanExpressionEvaluator evaluator, VariableResolver variableResolver) {
155+
default void process(TokenProcessor processor) {
133156
}
134157
}
135158

136159
public record NumberValue(Number value) implements NumberLikeValue {
137160
@Override
138-
public Number numberValue(BooleanExpressionEvaluator evaluator, VariableResolver variableResolver) {
161+
public Number numberValue(TokenProcessor processor) {
139162
return value();
140163
}
141164
}
@@ -154,9 +177,8 @@ public boolean applyEquality(double b, double a) {
154177
}
155178

156179
@Override
157-
public void process(BooleanExpressionEvaluator evaluator, VariableResolver variableResolver) {
158-
int tokenIndex = evaluator.tokens().indexOf(this);
159-
if (tokenIndex > 0 && tokenIndex < evaluator.tokens().size() - 1 && evaluator.tokens().get(tokenIndex - 1) instanceof NumberLikeValue n && evaluator.tokens().get(tokenIndex + 1) instanceof NumberLikeValue n1) evaluator.values.push(applyEquality(n1.numberValue(evaluator, variableResolver).doubleValue(), n.numberValue(evaluator, variableResolver).doubleValue()));
180+
public void process(TokenProcessor processor) {
181+
if (processor.relative(-1) instanceof NumberLikeValue n && processor.relative(1) instanceof NumberLikeValue n1) processor.evaluator().values.push(applyEquality(n1.numberValue(processor).doubleValue(), n.numberValue(processor).doubleValue()));
160182
}
161183
}
162184

@@ -173,23 +195,23 @@ public boolean operate(boolean b, boolean a) {
173195
}
174196

175197
@Override
176-
public void process(BooleanExpressionEvaluator evaluator, VariableResolver variableResolver) {
198+
public void process(TokenProcessor processor) {
177199
if (!symbol().equals("(") && !symbol().equals(")")){
178-
while (!evaluator.operators.isEmpty()) {
179-
evaluator.values.push(evaluator.operators.pop().operate(evaluator.values.pop(), evaluator.values.pop()));
200+
while (!processor.evaluator().operators.isEmpty()) {
201+
processor.evaluator().values.push(processor.evaluator().operators.pop().operate(processor.evaluator().values.pop(), processor.evaluator().values.pop()));
180202
}
181203
} else if (symbol().equals(")")){
182-
while (evaluator.operators.peek().symbol().equals("(")) {
183-
evaluator.values.push(evaluator.operators.pop().operate(evaluator.values.pop(), evaluator.values.pop()));
204+
while (processor.evaluator().operators.peek().symbol().equals("(")) {
205+
processor.evaluator().values.push(processor.evaluator().operators.pop().operate(processor.evaluator().values.pop(), processor.evaluator().values.pop()));
184206
}
185-
evaluator.operators.pop();
186-
if ("!".equals(evaluator.operators.peek().symbol())) {
187-
evaluator.values.push(!evaluator.values.pop());
188-
evaluator.operators.pop();
207+
processor.evaluator().operators.pop();
208+
if ("!".equals(processor.evaluator().operators.peek().symbol())) {
209+
processor.evaluator().values.push(!processor.evaluator().values.pop());
210+
processor.evaluator().operators.pop();
189211
}
190212
return;
191213
}
192-
evaluator.operators.push(this);
214+
processor.evaluator().operators.push(this);
193215
}
194216
}
195217
}

src/main/java/wily/factoryapi/util/ExpressionEvaluator.java

Lines changed: 67 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@
1515
import java.util.regex.Matcher;
1616
import java.util.regex.Pattern;
1717

18-
public record ExpressionEvaluator(String expression, List<Token> tokens, Stack<Value> values, Stack<Operator> operators, Bearer<Function> function) {
18+
public record ExpressionEvaluator(String expression, List<Token> tokens, Stack<Value> values, Stack<Operator> operators, TokenProcessor processor) {
1919
public static final LoadingCache<String,ExpressionEvaluator> EXPRESSION_CACHE = CacheBuilder.newBuilder().expireAfterWrite(Duration.ofMinutes(12)).build(CacheLoader.from(ExpressionEvaluator::create));
2020

2121
private static final Pattern TOKEN_PATTERN = Pattern.compile("(\\$\\{[\\dA-Za-z_.-]+})|(:?\\d+\\.?\\d*)|(#[A-Z\\d]+)|([+\\-/*%&|^()]|>>|<<)|(sqrt|cbrt|pow|min|max|clamp)");
2222

2323
public ExpressionEvaluator(String expression, List<Token> tokens){
24-
this(expression, tokens, new Stack<>(), new Stack<>(), Bearer.of(null));
24+
this(expression, tokens, new Stack<>(), new Stack<>(), new TokenProcessor());
2525
}
2626

2727
public static ExpressionEvaluator create(String expression) {
@@ -35,10 +35,10 @@ public static ExpressionEvaluator of(String expression) {
3535
public Number evaluate(VariableResolver variableResolver) {
3636
values.clear();
3737
operators.clear();
38-
function.set(null);
38+
processor.clearFunction();
3939
try {
40-
for (Token token : tokens) {
41-
token.process(this, variableResolver);
40+
for (int i = 0; i < tokens.size(); i++) {
41+
processor.process(i, this, variableResolver);
4242
}
4343

4444
while (!operators.isEmpty()) {
@@ -85,22 +85,52 @@ public static List<Token> tokenize(String expression) {
8585
return tokens;
8686
}
8787

88-
public void pushValueOrApplyFunction(Value value) {
89-
if (function.isEmpty()){
90-
values.push(value);
91-
} else {
92-
function.get().args.add(value);
93-
if (function.get().canEvaluate()){
94-
values.push(function.get().tryEvaluate());
95-
function.get().args.clear();
96-
function.set(null);
97-
}
88+
public interface Token {
89+
void process(TokenProcessor processor);
90+
}
91+
92+
public static class TokenProcessor {
93+
private ExpressionEvaluator evaluator;
94+
private int index;
95+
private VariableResolver resolver;
96+
private Function function;
97+
98+
public Token relative(int offset){
99+
int relative = index + offset;
100+
return relative < evaluator.tokens.size() && relative >= 0 ? evaluator.tokens.get(relative) : null;
98101
}
99102

100-
}
103+
public void process(int index, ExpressionEvaluator evaluator, VariableResolver resolver){
104+
this.index = index;
105+
this.evaluator = evaluator;
106+
this.resolver = resolver;
107+
evaluator.tokens.get(index).process(this);
108+
}
101109

102-
public interface Token {
103-
void process(ExpressionEvaluator evaluator, VariableResolver variableResolver);
110+
public ExpressionEvaluator evaluator(){
111+
return evaluator;
112+
}
113+
114+
public VariableResolver resolver() {
115+
return resolver;
116+
}
117+
118+
public void clearFunction(){
119+
function = null;
120+
}
121+
122+
public void pushValueOrApplyFunction(Value value) {
123+
if (function == null){
124+
evaluator.values().push(value);
125+
} else {
126+
function.args.add(value);
127+
if (function.canEvaluate()){
128+
evaluator.values().push(function.tryEvaluate());
129+
function.args.clear();
130+
clearFunction();
131+
}
132+
}
133+
}
104134
}
105135

106136
public record Value(Number value, boolean isInteger, boolean isFallback) implements Token {
@@ -116,18 +146,18 @@ public boolean isValid(){
116146
}
117147

118148
@Override
119-
public void process(ExpressionEvaluator evaluator, VariableResolver variableResolver) {
120-
if (!isFallback() || !evaluator.values.peek().isValid()) {
121-
if (isFallback()) evaluator.values.pop();
122-
evaluator.pushValueOrApplyFunction(this);
149+
public void process(TokenProcessor processor) {
150+
if (!isFallback() || !processor.evaluator().values.peek().isValid()) {
151+
if (isFallback()) processor.evaluator().values.pop();
152+
processor.pushValueOrApplyFunction(this);
123153
}
124154
}
125155
}
126156

127157
public record Variable(String name) implements Token {
128158
@Override
129-
public void process(ExpressionEvaluator evaluator, VariableResolver variableResolver) {
130-
evaluator.pushValueOrApplyFunction(Value.of(variableResolver.getNumber(name(), null)));
159+
public void process(TokenProcessor processor) {
160+
processor.pushValueOrApplyFunction(Value.of(processor.resolver().getNumber(name(), null)));
131161
}
132162
}
133163

@@ -160,19 +190,19 @@ public Value operate(Value b, Value a) {
160190
}
161191

162192
@Override
163-
public void process(ExpressionEvaluator evaluator, VariableResolver variableResolver) {
193+
public void process(TokenProcessor processor) {
164194
if (!symbol().equals("(") && !symbol().equals(")")){
165-
while (!evaluator.operators.isEmpty() && hasPrecedence(evaluator.operators.peek())) {
166-
evaluator.values.push(evaluator.operators.pop().operate(evaluator.values.pop(), evaluator.values.pop()));
195+
while (!processor.evaluator().operators.isEmpty() && hasPrecedence(processor.evaluator().operators.peek())) {
196+
processor.evaluator().values.push(processor.evaluator().operators.pop().operate(processor.evaluator().values.pop(), processor.evaluator().values.pop()));
167197
}
168198
} else if (symbol().equals(")")){
169-
while (!evaluator.operators.peek().symbol().equals("(")) {
170-
evaluator.values.push(evaluator.operators.pop().operate(evaluator.values.pop(), evaluator.values.pop()));
199+
while (!processor.evaluator().operators.peek().symbol().equals("(")) {
200+
processor.evaluator().values.push(processor.evaluator().operators.pop().operate(processor.evaluator().values.pop(), processor.evaluator().values.pop()));
171201
}
172-
evaluator.operators.pop();
202+
processor.evaluator().operators.pop();
173203
return;
174204
}
175-
evaluator.operators.push(this);
205+
processor.evaluator().operators.push(this);
176206
}
177207
}
178208

@@ -214,14 +244,14 @@ public Value tryEvaluate(){
214244
}
215245

216246
@Override
217-
public void process(ExpressionEvaluator evaluator, VariableResolver variableResolver) {
218-
if (evaluator.function.isPresent()){
219-
String message = "Last function with incomplete arguments: %s with %s of %s".formatted(evaluator.function.get().type, evaluator.function.get().args.size(), evaluator.function.get().argsCount);
220-
evaluator.function.get().args.clear();
221-
evaluator.function.set(this);
247+
public void process(TokenProcessor processor) {
248+
if (processor.function != null){
249+
String message = "Last function with incomplete arguments: %s with %s of %s".formatted(processor.function.type, processor.function.args.size(), processor.function.argsCount);
250+
processor.function.args.clear();
251+
processor.function = this;
222252
throw new UnsupportedOperationException(message);
223253
}
224-
evaluator.function.set(this);
254+
processor.function = this;
225255
}
226256
}
227257
}

0 commit comments

Comments
 (0)