From fef30042ce7f1a02140ea9862d7ddae603fe1f15 Mon Sep 17 00:00:00 2001 From: Riccardo Azzolini Date: Thu, 8 Aug 2019 14:35:30 +0200 Subject: [PATCH] Avoid using methods which are not available on TeaVM --- .../math/rules/dsl/errorutils/LineMap.java | 10 ++-- .../warppi/math/rules/dsl/frontend/Lexer.java | 24 ++++---- .../math/rules/dsl/frontend/Parser.java | 55 ++++++++++--------- .../rules/dsl/frontend/UnexpectedToken.java | 4 +- .../it/cavallium/warppi/util/MapFactory.java | 52 ++++++++++++++++++ 5 files changed, 100 insertions(+), 45 deletions(-) create mode 100644 core/src/main/java/it/cavallium/warppi/util/MapFactory.java diff --git a/core/src/main/java/it/cavallium/warppi/math/rules/dsl/errorutils/LineMap.java b/core/src/main/java/it/cavallium/warppi/math/rules/dsl/errorutils/LineMap.java index 31ff36e0..baa793c5 100644 --- a/core/src/main/java/it/cavallium/warppi/math/rules/dsl/errorutils/LineMap.java +++ b/core/src/main/java/it/cavallium/warppi/math/rules/dsl/errorutils/LineMap.java @@ -181,11 +181,11 @@ public class LineMap { @Override public String toString() { - return new StringJoiner(", ", "Line{", "}") - .add("number=" + number) - .add("startPosition=" + startPosition) - .add("text='" + text + "'") - .toString(); + return "Line{" + + "number=" + number + + ", startPosition=" + startPosition + + ", text='" + text + '\'' + + '}'; } } } diff --git a/core/src/main/java/it/cavallium/warppi/math/rules/dsl/frontend/Lexer.java b/core/src/main/java/it/cavallium/warppi/math/rules/dsl/frontend/Lexer.java index 0119dfb2..b71721df 100644 --- a/core/src/main/java/it/cavallium/warppi/math/rules/dsl/frontend/Lexer.java +++ b/core/src/main/java/it/cavallium/warppi/math/rules/dsl/frontend/Lexer.java @@ -2,14 +2,9 @@ package it.cavallium.warppi.math.rules.dsl.frontend; import it.cavallium.warppi.math.rules.dsl.DslError; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.function.Consumer; -import java.util.function.Function; import java.util.function.Predicate; -import java.util.stream.Collectors; -import java.util.stream.Stream; import static it.cavallium.warppi.math.rules.dsl.frontend.TokenType.*; @@ -17,14 +12,19 @@ import static it.cavallium.warppi.math.rules.dsl.frontend.TokenType.*; * Converts the source string to a list of tokens. */ public class Lexer { - private static final Map keywords = Stream.of( + private static final Map KEYWORDS; + static { + TokenType[] keywordTokenTypes = { REDUCTION, EXPANSION, CALCULATION, EXISTENCE, ARCCOS, ARCSIN, ARCTAN, COS, SIN, TAN, ROOT, SQRT, LOG, UNDEFINED, PI, E - ).collect(Collectors.toMap( - tokenType -> tokenType.name().toLowerCase(), - Function.identity() - )); + }; + Map map = new HashMap<>(); + for (TokenType type : keywordTokenTypes) { + map.put(type.name().toLowerCase(), type); + } + KEYWORDS = Collections.unmodifiableMap(map); + } private final String source; private final Consumer errorReporter; @@ -160,7 +160,7 @@ public class Lexer { private void keywordOrIdentifier() { matchWhile(Character::isJavaIdentifierPart); - TokenType type = keywords.getOrDefault(currentLexeme(), IDENTIFIER); + TokenType type = KEYWORDS.getOrDefault(currentLexeme(), IDENTIFIER); emitToken(type); } diff --git a/core/src/main/java/it/cavallium/warppi/math/rules/dsl/frontend/Parser.java b/core/src/main/java/it/cavallium/warppi/math/rules/dsl/frontend/Parser.java index 5684c875..4254f7fc 100644 --- a/core/src/main/java/it/cavallium/warppi/math/rules/dsl/frontend/Parser.java +++ b/core/src/main/java/it/cavallium/warppi/math/rules/dsl/frontend/Parser.java @@ -6,6 +6,7 @@ import it.cavallium.warppi.math.rules.dsl.DslError; import it.cavallium.warppi.math.rules.dsl.Pattern; import it.cavallium.warppi.math.rules.dsl.PatternRule; import it.cavallium.warppi.math.rules.dsl.patterns.*; +import it.cavallium.warppi.util.MapFactory; import java.math.BigDecimal; import java.util.*; @@ -19,11 +20,11 @@ import static it.cavallium.warppi.math.rules.dsl.frontend.TokenType.*; * Converts a list of tokens to a list of PatternRules. */ public class Parser { - private static final Map ruleTypes = Map.ofEntries( - Map.entry(REDUCTION, RuleType.REDUCTION), - Map.entry(EXPANSION, RuleType.EXPANSION), - Map.entry(CALCULATION, RuleType.CALCULATION), - Map.entry(EXISTENCE, RuleType.EXISTENCE) + private static final Map RULE_TYPES = MapFactory.fromEntries( + MapFactory.entry(REDUCTION, RuleType.REDUCTION), + MapFactory.entry(EXPANSION, RuleType.EXPANSION), + MapFactory.entry(CALCULATION, RuleType.CALCULATION), + MapFactory.entry(EXISTENCE, RuleType.EXISTENCE) ); private final List tokens; @@ -57,7 +58,7 @@ public class Parser { rules.add(rule()); } catch (final SyntaxException e) { errorReporter.accept(e.getError()); - synchronizeTo(ruleTypes.keySet()); // Skip to the next rule to minimize "false" errors + synchronizeTo(RULE_TYPES.keySet()); // Skip to the next rule to minimize "false" errors } } return rules; @@ -82,12 +83,12 @@ public class Parser { // rule type = REDUCTION | EXPANSION | CALCULATION | EXISTENCE ; private RuleType ruleType() throws SyntaxException { final Token curToken = pop(); - if (!ruleTypes.containsKey(curToken.type)) { + if (!RULE_TYPES.containsKey(curToken.type)) { throw new SyntaxException( - new UnexpectedToken(curToken, ruleTypes.keySet()) + new UnexpectedToken(curToken, RULE_TYPES.keySet()) ); } - return ruleTypes.get(curToken.type); + return RULE_TYPES.get(curToken.type); } // pattern = equation ; @@ -126,18 +127,18 @@ public class Parser { // sum = product , { ( PLUS | MINUS | PLUS_MINUS ) product } ; private Pattern sum() throws SyntaxException { - return matchLeftAssoc(this::product, Map.ofEntries( - Map.entry(PLUS, SumPattern::new), - Map.entry(MINUS, SubtractionPattern::new), - Map.entry(PLUS_MINUS, SumSubtractionPattern::new) + return matchLeftAssoc(this::product, MapFactory.fromEntries( + MapFactory.entry(PLUS, SumPattern::new), + MapFactory.entry(MINUS, SubtractionPattern::new), + MapFactory.entry(PLUS_MINUS, SumSubtractionPattern::new) )); } // product = unary , { ( TIMES | DIVIDE ) unary } ; private Pattern product() throws SyntaxException { - return matchLeftAssoc(this::unary, Map.ofEntries( - Map.entry(TIMES, MultiplicationPattern::new), - Map.entry(DIVIDE, DivisionPattern::new) + return matchLeftAssoc(this::unary, MapFactory.fromEntries( + MapFactory.entry(TIMES, MultiplicationPattern::new), + MapFactory.entry(DIVIDE, DivisionPattern::new) )); } @@ -170,18 +171,18 @@ public class Parser { // function = ( ARCCOS | ARCSIN | ARCTAN | COS | SIN | SQRT | TAN ) , LEFT_PAREN , sum , RIGHT_PAREN // | ( LOG | ROOT ) LEFT_PAREN , sum , COMMA , sum , RIGHT_PAREN ; private Pattern tryFunction() throws SyntaxException { - final Map> oneArg = Map.ofEntries( - Map.entry(ARCCOS, ArcCosinePattern::new), - Map.entry(ARCSIN, ArcSinePattern::new), - Map.entry(ARCTAN, ArcTangentPattern::new), - Map.entry(COS, CosinePattern::new), - Map.entry(SIN, SinePattern::new), - Map.entry(SQRT, arg -> new RootPattern(new NumberPattern(new BigDecimal(2)), arg)), - Map.entry(TAN, TangentPattern::new) + final Map> oneArg = MapFactory.fromEntries( + MapFactory.entry(ARCCOS, ArcCosinePattern::new), + MapFactory.entry(ARCSIN, ArcSinePattern::new), + MapFactory.entry(ARCTAN, ArcTangentPattern::new), + MapFactory.entry(COS, CosinePattern::new), + MapFactory.entry(SIN, SinePattern::new), + MapFactory.entry(SQRT, arg -> new RootPattern(new NumberPattern(new BigDecimal(2)), arg)), + MapFactory.entry(TAN, TangentPattern::new) ); - final Map> twoArg = Map.ofEntries( - Map.entry(LOG, LogarithmPattern::new), - Map.entry(ROOT, RootPattern::new) + final Map> twoArg = MapFactory.fromEntries( + MapFactory.entry(LOG, LogarithmPattern::new), + MapFactory.entry(ROOT, RootPattern::new) ); final TokenType curType = peek().type; diff --git a/core/src/main/java/it/cavallium/warppi/math/rules/dsl/frontend/UnexpectedToken.java b/core/src/main/java/it/cavallium/warppi/math/rules/dsl/frontend/UnexpectedToken.java index c701b2a6..9a533ed3 100644 --- a/core/src/main/java/it/cavallium/warppi/math/rules/dsl/frontend/UnexpectedToken.java +++ b/core/src/main/java/it/cavallium/warppi/math/rules/dsl/frontend/UnexpectedToken.java @@ -2,6 +2,8 @@ package it.cavallium.warppi.math.rules.dsl.frontend; import it.cavallium.warppi.math.rules.dsl.DslError; +import java.util.Arrays; +import java.util.HashSet; import java.util.Objects; import java.util.Set; @@ -19,7 +21,7 @@ public class UnexpectedToken implements DslError { public UnexpectedToken(final Token unexpected, final TokenType... suggested) { this.unexpected = unexpected; - this.suggested = Set.of(suggested); + this.suggested = new HashSet<>(Arrays.asList(suggested)); // TeaVM doesn't support Set.of } @Override diff --git a/core/src/main/java/it/cavallium/warppi/util/MapFactory.java b/core/src/main/java/it/cavallium/warppi/util/MapFactory.java new file mode 100644 index 00000000..1640c0b6 --- /dev/null +++ b/core/src/main/java/it/cavallium/warppi/util/MapFactory.java @@ -0,0 +1,52 @@ +package it.cavallium.warppi.util; + +import java.util.AbstractMap; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * Reimplements map creation methods which are not available on TeaVM. + */ +public class MapFactory { + private MapFactory() {} + + /** + * Returns an unmodifiable map containing keys and values extracted from the given entries. + *

+ * This method is equivalent to {@link Map#ofEntries(Map.Entry...)}. + * + * @param entries Map.Entrys containing the keys and values from which the map is populated + * @param the Map's key type + * @param the Map's value type + * @return a Map containing the specified mappings + * @see MapFactory#entry(K, V) + */ + @SafeVarargs + public static Map fromEntries(Map.Entry... entries) { + HashMap map = new HashMap<>(); + for (Map.Entry entry : entries) { + map.put(entry.getKey(), entry.getValue()); + } + return Collections.unmodifiableMap(map); + } + + /** + * Returns an unmodifiable Map.Entry containing the given key and value. + * These entries are suitable for populating Map instances using the {@link MapFactory#fromEntries(Map.Entry...)} + * or {@link Map#ofEntries(Map.Entry...)} methods. + *

+ * This method can be used as a replacement for {@link Map#entry(K, V)}, if the latter is not available (for example, + * when compiling for TeaVM). However, unlike Map.entry, null keys and values are allowed, + * and the returned Entry is serializable. + * + * @param key the key + * @param value the value + * @param the key's type + * @param the value's type + * @return an Entry containing the specified key and value + */ + public static Map.Entry entry(K key, V value) { + return new AbstractMap.SimpleImmutableEntry<>(key, value); + } +}