Restructured StringEquationParser -> Equation, basically swapping inner/outer class

This commit is contained in:
CreepyCre 2021-02-04 15:17:48 +01:00
parent bd030ec374
commit fa93651cb8
6 changed files with 217 additions and 206 deletions

View file

@ -21,7 +21,8 @@ import org.dimdev.dimdoors.pockets.modifier.Modifier;
import org.dimdev.dimdoors.util.Location;
import org.dimdev.dimdoors.util.PocketGenerationParameters;
import org.dimdev.dimdoors.util.Weighted;
import org.dimdev.dimdoors.util.math.StringEquationParser;
import org.dimdev.dimdoors.util.math.Equation;
import org.dimdev.dimdoors.util.math.Equation.EquationParseException;
import org.dimdev.dimdoors.world.pocket.Pocket;
import java.util.ArrayList;
@ -39,7 +40,7 @@ public abstract class PocketGenerator implements Weighted<PocketGenerationParame
private final List<Modifier> modifierList = new ArrayList<>();
private String weight;
private StringEquationParser.Equation weightEquation;
private Equation weightEquation;
public PocketGenerator() { }
@ -64,12 +65,12 @@ public abstract class PocketGenerator implements Weighted<PocketGenerationParame
private void parseWeight() {
try {
this.weightEquation = StringEquationParser.parse(weight);
} catch (StringEquationParser.EquationParseException e) {
this.weightEquation = Equation.parse(weight);
} catch (EquationParseException e) {
LOGGER.error("Could not parse weight equation \"" + weight + "\", defaulting to default weight equation \"" + defaultWeightEquation + "\"", e);
try {
this.weightEquation = StringEquationParser.parse(defaultWeightEquation);
} catch (StringEquationParser.EquationParseException equationParseException) {
this.weightEquation = Equation.parse(defaultWeightEquation);
} catch (EquationParseException equationParseException) {
LOGGER.error("Could not parse default weight equation \"" + defaultWeightEquation + "\", defaulting to fallback weight \"" + fallbackWeight + "\"", equationParseException);
this.weightEquation = stringDoubleMap -> fallbackWeight;
}

View file

@ -4,7 +4,8 @@ import net.minecraft.nbt.CompoundTag;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.dimdev.dimdoors.util.PocketGenerationParameters;
import org.dimdev.dimdoors.util.math.StringEquationParser;
import org.dimdev.dimdoors.util.math.Equation;
import org.dimdev.dimdoors.util.math.Equation.EquationParseException;
import org.dimdev.dimdoors.world.level.DimensionalRegistry;
import org.dimdev.dimdoors.world.pocket.Pocket;
@ -15,17 +16,17 @@ public class VoidGenerator extends PocketGenerator {
private static final Logger LOGGER = LogManager.getLogger();
public static final String KEY = "void";
private String width;
private StringEquationParser.Equation heightEquation;
private Equation heightEquation;
private String height;
private StringEquationParser.Equation widthEquation;
private Equation widthEquation;
private String length;
private StringEquationParser.Equation lengthEquation;
private Equation lengthEquation;
private String offsetX;
private StringEquationParser.Equation offsetXEquation;
private Equation offsetXEquation;
private String offsetY;
private StringEquationParser.Equation offsetYEquation;
private Equation offsetYEquation;
private String offsetZ;
private StringEquationParser.Equation offsetZEquation;
private Equation offsetZEquation;
@Override
public Pocket prepareAndPlacePocket(PocketGenerationParameters parameters) {
@ -53,19 +54,19 @@ public class VoidGenerator extends PocketGenerator {
try {
width = tag.getString("width");
widthEquation = StringEquationParser.parse(width);
widthEquation = Equation.parse(width);
height = tag.getString("height");
heightEquation = StringEquationParser.parse(height);
heightEquation = Equation.parse(height);
length = tag.getString("length");
lengthEquation = StringEquationParser.parse(length);
lengthEquation = Equation.parse(length);
offsetX = tag.contains("offset_x") ? tag.getString("offset_x") : "0";
offsetXEquation = StringEquationParser.parse(offsetX);
offsetXEquation = Equation.parse(offsetX);
offsetY = tag.contains("offset_y") ? tag.getString("offset_y") : "0";
offsetYEquation = StringEquationParser.parse(offsetY);
offsetYEquation = Equation.parse(offsetY);
offsetZ = tag.contains("offset_z") ? tag.getString("offset_z") : "0";
offsetZEquation = StringEquationParser.parse(offsetZ);
} catch (StringEquationParser.EquationParseException e) {
offsetZEquation = Equation.parse(offsetZ);
} catch (EquationParseException e) {
LOGGER.error(e);
}

View file

@ -24,7 +24,8 @@ import org.dimdev.dimdoors.rift.registry.LinkProperties;
import org.dimdev.dimdoors.rift.targets.PocketEntranceMarker;
import org.dimdev.dimdoors.rift.targets.PocketExitMarker;
import org.dimdev.dimdoors.util.PocketGenerationParameters;
import org.dimdev.dimdoors.util.math.StringEquationParser;
import org.dimdev.dimdoors.util.math.Equation;
import org.dimdev.dimdoors.util.math.Equation.EquationParseException;
import org.dimdev.dimdoors.world.pocket.Pocket;
public class DimensionalDoorModifier implements Modifier {
@ -38,9 +39,9 @@ public class DimensionalDoorModifier implements Modifier {
private String x;
private String y;
private String z;
private StringEquationParser.Equation xEquation;
private StringEquationParser.Equation yEquation;
private StringEquationParser.Equation zEquation;
private Equation xEquation;
private Equation yEquation;
private Equation zEquation;
@Override
@ -65,10 +66,10 @@ public class DimensionalDoorModifier implements Modifier {
y = tag.getString("y");
z = tag.getString("z");
xEquation = StringEquationParser.parse(x);
yEquation = StringEquationParser.parse(y);
zEquation = StringEquationParser.parse(z);
} catch (StringEquationParser.EquationParseException e) {
xEquation = Equation.parse(x);
yEquation = Equation.parse(y);
zEquation = Equation.parse(z);
} catch (EquationParseException e) {
LOGGER.error(e);
}
return this;

View file

@ -10,7 +10,8 @@ import org.dimdev.dimdoors.pockets.generator.PocketGenerator;
import org.dimdev.dimdoors.pockets.modifier.Modifier;
import org.dimdev.dimdoors.pockets.virtual.VirtualSingularPocket;
import org.dimdev.dimdoors.util.PocketGenerationParameters;
import org.dimdev.dimdoors.util.math.StringEquationParser;
import org.dimdev.dimdoors.util.math.Equation;
import org.dimdev.dimdoors.util.math.Equation.EquationParseException;
import org.dimdev.dimdoors.world.pocket.Pocket;
import java.util.List;
@ -21,19 +22,19 @@ public abstract class PocketGeneratorReference extends VirtualSingularPocket {
private static final int fallbackWeight = 5; // TODO: make config
private String weight;
private StringEquationParser.Equation weightEquation;
private Equation weightEquation;
private Boolean setupLoot;
private final List<Modifier> modifierList = Lists.newArrayList();
private void parseWeight() {
try {
this.weightEquation = StringEquationParser.parse(weight);
} catch (StringEquationParser.EquationParseException e) {
this.weightEquation = Equation.parse(weight);
} catch (EquationParseException e) {
LOGGER.error("Could not parse weight equation \"" + weight + "\", defaulting to default weight equation \"" + defaultWeightEquation + "\"", e);
try {
this.weightEquation = StringEquationParser.parse(defaultWeightEquation);
} catch (StringEquationParser.EquationParseException equationParseException) {
this.weightEquation = Equation.parse(defaultWeightEquation);
} catch (EquationParseException equationParseException) {
LOGGER.error("Could not parse default weight equation \"" + defaultWeightEquation + "\", defaulting to fallback weight \"" + fallbackWeight + "\"", equationParseException);
this.weightEquation = stringDoubleMap -> fallbackWeight;
}

View file

@ -0,0 +1,179 @@
package org.dimdev.dimdoors.util.math;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.*;
import java.util.function.BiFunction;
public interface Equation {
double apply(Map<String, Double> variableMap);
static Equation parse(String equationString) throws EquationParseException {
return StringEquationParser.INSTANCE.parse(equationString);
}
class StringEquationParser {
private static final Logger LOGGER = LogManager.getLogger();
public static StringEquationParser INSTANCE = new StringEquationParser();
private final static List<EquationParser> parseRules = new ArrayList<>();
static {
// Parenthesis
parseRules.add(toParse -> {
if (!toParse.startsWith("(") || !toParse.endsWith(")")) return Optional.empty();
return Optional.of(Equation.parse(toParse.substring(1, toParse.length() - 1)));
});
// try to parse as Double
parseRules.add(toParse -> {
try {
Double result = Double.parseDouble(toParse);
return Optional.of(stringDoubleMap -> result);
} catch (NumberFormatException e) {
return Optional.empty();
}
});
// +, -
Map<String, TriFunction<Map<String, Double>, Equation, Equation, Double>> sumOperations = new HashMap<>();
sumOperations.put("+", (stringDoubleMap, first, second) -> first.apply(stringDoubleMap) + second.apply(stringDoubleMap));
sumOperations.put("-", (stringDoubleMap, first, second) -> first.apply(stringDoubleMap) - second.apply(stringDoubleMap));
parseRules.add(new SplitterParser(sumOperations));
// *, /, %
Map<String, TriFunction<Map<String, Double>, Equation, Equation, Double>> dotOperations = new HashMap<>();
dotOperations.put("*", (stringDoubleMap, first, second) -> first.apply(stringDoubleMap) * second.apply(stringDoubleMap));
dotOperations.put("/", (stringDoubleMap, first, second) -> first.apply(stringDoubleMap) / second.apply(stringDoubleMap));
dotOperations.put("%", (stringDoubleMap, first, second) -> first.apply(stringDoubleMap) % second.apply(stringDoubleMap));
parseRules.add(new SplitterParser(dotOperations));
// x^y
Map<String, TriFunction<Map<String, Double>, Equation, Equation, Double>> exponentOperations = new HashMap<>();
exponentOperations.put("^", (stringDoubleMap, first, second) -> Math.pow(first.apply(stringDoubleMap), second.apply(stringDoubleMap)));
parseRules.add(new SplitterParser(exponentOperations));
// H with H(0) = 1: https://en.wikipedia.org/wiki/Heaviside_step_function
parseRules.add(new FunctionParser("H", 1, 1, ((stringDoubleMap, equations) -> equations[0].apply(stringDoubleMap) >= 0 ? 1d : 0d)));
// floor
parseRules.add(new FunctionParser("floor", 1, 1, ((stringDoubleMap, equations) -> Math.floor(equations[0].apply(stringDoubleMap)))));
// ceil
parseRules.add(new FunctionParser("ceil", 1, 1, ((stringDoubleMap, equations) -> Math.ceil(equations[0].apply(stringDoubleMap)))));
// max
parseRules.add(new FunctionParser("max", 2, -1, ((stringDoubleMap, equations) -> {
Double max = equations[0].apply(stringDoubleMap);
for (int i = 1; i < equations.length; i++) {
max = Math.max(max, equations[i].apply(stringDoubleMap));
}
return max;
})));
// min
parseRules.add(new FunctionParser("min", 2, -1, ((stringDoubleMap, equations) -> {
Double min = equations[0].apply(stringDoubleMap);
for (int i = 1; i < equations.length; i++) {
min = Math.min(min, equations[i].apply(stringDoubleMap));
}
return min;
})));
// variable replacer
parseRules.add(new VariableReplacer());
}
public Equation parse(String equationString) throws EquationParseException {
equationString = equationString.replaceAll("\\s", "");
for (EquationParser parser : parseRules) {
Optional<Equation> equation = parser.tryParse(equationString);
if (equation.isPresent()) return equation.get();
}
throw new EquationParseException("\"" + equationString + "\" could not be parsed");
}
private interface EquationParser {
Optional<Equation> tryParse(String toParse) throws EquationParseException;
}
private static class VariableReplacer implements EquationParser {
@Override
public Optional<Equation> tryParse(String toParse) {
if (!toParse.matches("[a-zA-Z_][a-zA-Z0-9_]*")) return Optional.empty();
return Optional.of(stringDoubleMap -> {
if (stringDoubleMap != null && stringDoubleMap.containsKey(toParse)) return stringDoubleMap.get(toParse);
LOGGER.error("Variable \"" + toParse + "\" was not passed to equation! Returning 0 as fallback.");
return 0d;
});
}
}
private static class SplitterParser implements EquationParser {
private final Map<String, TriFunction<Map<String, Double>, Equation, Equation, Double>> operations;
public SplitterParser(Map<String, TriFunction<Map<String, Double>, Equation, Equation, Double>> operations) {
this.operations = operations;
}
@Override
public Optional<Equation> tryParse(String toParse) throws EquationParseException {
int depth = 0;
for (int i = toParse.length() - 1; i >= 1 ; i--) {
String substring = toParse.substring(i);
if (substring.startsWith(")")) depth++;
else if (substring.startsWith("(")) depth--;
for(String symbol : operations.keySet()) {
if (substring.startsWith(symbol) && depth == 0) {
final TriFunction<Map<String, Double>, Equation, Equation, Double> operation = operations.get(symbol);
final Equation first = Equation.parse(toParse.substring(0,i));
final Equation second = Equation.parse(toParse.substring(i+1));
return Optional.of(stringDoubleMap -> operation.apply(stringDoubleMap, first, second));
}
}
}
return Optional.empty();
}
}
private static class FunctionParser implements EquationParser {
private final String functionString;
private final int minArguments;
private final int maxArguments;
private final BiFunction<Map<String, Double>, Equation[], Double> function;
public FunctionParser(String functionString, int minArguments, int maxArguments, BiFunction<Map<String, Double>, Equation[], Double> function) {
this.functionString = functionString + "(";
this.minArguments = minArguments;
this.maxArguments = maxArguments;
this.function = function;
}
@Override
public Optional<Equation> tryParse(String toParse) throws EquationParseException {
if (!toParse.startsWith(functionString) || !toParse.endsWith(")")) return Optional.empty();
String[] arguments = toParse.substring(functionString.length(), toParse.length()-1).split(",");
if (minArguments > arguments.length || (maxArguments < arguments.length && maxArguments != -1)) return Optional.empty();
final Equation[] argumentEquations = new Equation[arguments.length];
for (int i = 0; i < arguments.length; i++) {
argumentEquations[i] = Equation.parse(arguments[i]);
}
return Optional.of(stringDoubleMap -> function.apply(stringDoubleMap, argumentEquations));
}
}
private interface TriFunction<T, U, V, R> {
R apply(T t, U u, V v);
}
}
class EquationParseException extends Exception {
public EquationParseException(String message) {
super(message);
}
}
}

View file

@ -1,172 +0,0 @@
package org.dimdev.dimdoors.util.math;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.*;
import java.util.function.BiFunction;
public class StringEquationParser {
private static final Logger LOGGER = LogManager.getLogger();
private final static List<EquationParser> parseRules = new ArrayList<>();
static {
// Parenthesis
parseRules.add(toParse -> {
if (!toParse.startsWith("(") || !toParse.endsWith(")")) return Optional.empty();
return Optional.of(parse(toParse.substring(1, toParse.length() - 1)));
});
// try to parse as Double
parseRules.add(toParse -> {
try {
Double result = Double.parseDouble(toParse);
return Optional.of(stringDoubleMap -> result);
} catch (NumberFormatException e) {
return Optional.empty();
}
});
// +, -
Map<String, TriFunction<Map<String, Double>, Equation, Equation, Double>> sumOperations = new HashMap<>();
sumOperations.put("+", (stringDoubleMap, first, second) -> first.apply(stringDoubleMap) + second.apply(stringDoubleMap));
sumOperations.put("-", (stringDoubleMap, first, second) -> first.apply(stringDoubleMap) - second.apply(stringDoubleMap));
parseRules.add(new SplitterParser(sumOperations));
// *, /, %
Map<String, TriFunction<Map<String, Double>, Equation, Equation, Double>> dotOperations = new HashMap<>();
dotOperations.put("*", (stringDoubleMap, first, second) -> first.apply(stringDoubleMap) * second.apply(stringDoubleMap));
dotOperations.put("/", (stringDoubleMap, first, second) -> first.apply(stringDoubleMap) / second.apply(stringDoubleMap));
dotOperations.put("%", (stringDoubleMap, first, second) -> first.apply(stringDoubleMap) % second.apply(stringDoubleMap));
parseRules.add(new SplitterParser(dotOperations));
// x^y
Map<String, TriFunction<Map<String, Double>, Equation, Equation, Double>> exponentOperations = new HashMap<>();
exponentOperations.put("^", (stringDoubleMap, first, second) -> Math.pow(first.apply(stringDoubleMap), second.apply(stringDoubleMap)));
parseRules.add(new SplitterParser(exponentOperations));
// H with H(0) = 1: https://en.wikipedia.org/wiki/Heaviside_step_function
parseRules.add(new FunctionParser("H", 1, 1, ((stringDoubleMap, equations) -> equations[0].apply(stringDoubleMap) >= 0 ? 1d : 0d)));
// floor
parseRules.add(new FunctionParser("floor", 1, 1, ((stringDoubleMap, equations) -> Math.floor(equations[0].apply(stringDoubleMap)))));
// ceil
parseRules.add(new FunctionParser("ceil", 1, 1, ((stringDoubleMap, equations) -> Math.ceil(equations[0].apply(stringDoubleMap)))));
// max
parseRules.add(new FunctionParser("max", 2, -1, ((stringDoubleMap, equations) -> {
Double max = equations[0].apply(stringDoubleMap);
for (int i = 1; i < equations.length; i++) {
max = Math.max(max, equations[i].apply(stringDoubleMap));
}
return max;
})));
// min
parseRules.add(new FunctionParser("min", 2, -1, ((stringDoubleMap, equations) -> {
Double min = equations[0].apply(stringDoubleMap);
for (int i = 1; i < equations.length; i++) {
min = Math.min(min, equations[i].apply(stringDoubleMap));
}
return min;
})));
// variable replacer
parseRules.add(new VariableReplacer());
}
public static Equation parse(String equationString) throws EquationParseException {
equationString = equationString.replaceAll("\\s","");
for (EquationParser parser : parseRules) {
Optional<Equation> equation = parser.tryParse(equationString);
if (equation.isPresent()) return equation.get();
}
throw new EquationParseException("\"" + equationString + "\" could not be parsed");
}
public interface Equation {
double apply(Map<String, Double> variableMap);
}
private interface EquationParser {
Optional<Equation> tryParse(String toParse) throws EquationParseException;
}
private static class VariableReplacer implements EquationParser {
@Override
public Optional<Equation> tryParse(String toParse) {
if (!toParse.matches("[a-zA-Z_][a-zA-Z0-9_]*")) return Optional.empty();
return Optional.of(stringDoubleMap -> {
if (stringDoubleMap != null && stringDoubleMap.containsKey(toParse)) return stringDoubleMap.get(toParse);
LOGGER.error("Variable \"" + toParse + "\" was not passed to equation! Returning 0 as fallback.");
return 0d;
});
}
}
private static class SplitterParser implements EquationParser {
private final Map<String, TriFunction<Map<String, Double>, Equation, Equation, Double>> operations;
public SplitterParser(Map<String, TriFunction<Map<String, Double>, Equation, Equation, Double>> operations) {
this.operations = operations;
}
@Override
public Optional<Equation> tryParse(String toParse) throws EquationParseException {
int depth = 0;
for (int i = toParse.length() - 1; i >= 1 ; i--) {
String substring = toParse.substring(i);
if (substring.startsWith(")")) depth++;
else if (substring.startsWith("(")) depth--;
for(String symbol : operations.keySet()) {
if (substring.startsWith(symbol) && depth == 0) {
final TriFunction<Map<String, Double>, Equation, Equation, Double> operation = operations.get(symbol);
final Equation first = parse(toParse.substring(0,i));
final Equation second = parse(toParse.substring(i+1));
return Optional.of(stringDoubleMap -> operation.apply(stringDoubleMap, first, second));
}
}
}
return Optional.empty();
}
}
private static class FunctionParser implements EquationParser {
private final String functionString;
private final int minArguments;
private final int maxArguments;
private final BiFunction<Map<String, Double>, Equation[], Double> function;
public FunctionParser(String functionString, int minArguments, int maxArguments, BiFunction<Map<String, Double>, Equation[], Double> function) {
this.functionString = functionString + "(";
this.minArguments = minArguments;
this.maxArguments = maxArguments;
this.function = function;
}
@Override
public Optional<Equation> tryParse(String toParse) throws EquationParseException {
if (!toParse.startsWith(functionString) || !toParse.endsWith(")")) return Optional.empty();
String[] arguments = toParse.substring(functionString.length(), toParse.length()-1).split(",");
if (minArguments > arguments.length || (maxArguments < arguments.length && maxArguments != -1)) return Optional.empty();
final Equation[] argumentEquations = new Equation[arguments.length];
for (int i = 0; i < arguments.length; i++) {
argumentEquations[i] = parse(arguments[i]);
}
return Optional.of(stringDoubleMap -> function.apply(stringDoubleMap, argumentEquations));
}
}
public static class EquationParseException extends Exception {
public EquationParseException(String message) {
super(message);
}
}
private interface TriFunction<T, U, V, R> {
R apply(T t, U u, V v);
}
}