From 3057f66e667414d653b5a8381f2a6ced81e8c10a Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Thu, 21 Sep 2017 23:05:48 -0700 Subject: [PATCH 1/7] Throw the exception instead of returning null. --- .../abacus/exception/ContextException.java | 24 +++++++++++++++++++ .../abacus/context/ChainSearchDelegate.kt | 7 +++--- .../nwapw/abacus/context/EvaluationContext.kt | 6 ++--- 3 files changed, 31 insertions(+), 6 deletions(-) create mode 100644 core/src/main/java/org/nwapw/abacus/exception/ContextException.java diff --git a/core/src/main/java/org/nwapw/abacus/exception/ContextException.java b/core/src/main/java/org/nwapw/abacus/exception/ContextException.java new file mode 100644 index 0000000..dd2db0e --- /dev/null +++ b/core/src/main/java/org/nwapw/abacus/exception/ContextException.java @@ -0,0 +1,24 @@ +package org.nwapw.abacus.exception; + +/** + * Exception thrown by the Context in cases where lookup fails + * where it should not. + */ +public class ContextException extends AbacusException { + + /** + * Creates a new ContextException without an extra message. + */ + public ContextException() { + this(""); + } + + /** + * Creates a ContextException with the given message. + * @param message the message to use. + */ + public ContextException(String message){ + super("Context exception", message); + } + +} diff --git a/core/src/main/kotlin/org/nwapw/abacus/context/ChainSearchDelegate.kt b/core/src/main/kotlin/org/nwapw/abacus/context/ChainSearchDelegate.kt index b1245b9..64d933a 100644 --- a/core/src/main/kotlin/org/nwapw/abacus/context/ChainSearchDelegate.kt +++ b/core/src/main/kotlin/org/nwapw/abacus/context/ChainSearchDelegate.kt @@ -1,5 +1,6 @@ package org.nwapw.abacus.context +import org.nwapw.abacus.exception.ContextException import kotlin.reflect.KProperty /** @@ -16,14 +17,14 @@ import kotlin.reflect.KProperty */ class ChainSearchDelegate(private val valueGetter: EvaluationContext.() -> V?) { - operator fun getValue(selfRef: Any, property: KProperty<*>): V? { - var currentRef = selfRef as? EvaluationContext ?: return null + operator fun getValue(selfRef: Any, property: KProperty<*>): V { + var currentRef = selfRef as EvaluationContext var returnedValue = currentRef.valueGetter() while (returnedValue == null) { currentRef = currentRef.parent ?: break returnedValue = currentRef.valueGetter() } - return returnedValue + return returnedValue ?: throw ContextException() } } \ No newline at end of file diff --git a/core/src/main/kotlin/org/nwapw/abacus/context/EvaluationContext.kt b/core/src/main/kotlin/org/nwapw/abacus/context/EvaluationContext.kt index d4fdadd..293a5ff 100644 --- a/core/src/main/kotlin/org/nwapw/abacus/context/EvaluationContext.kt +++ b/core/src/main/kotlin/org/nwapw/abacus/context/EvaluationContext.kt @@ -43,13 +43,13 @@ open class EvaluationContext(val parent: EvaluationContext? = null, /** * The implementation inherited from this context's parent. */ - val inheritedNumberImplementation: NumberImplementation? - by ChainSearchDelegate { numberImplementation} + val inheritedNumberImplementation: NumberImplementation + by ChainSearchDelegate { numberImplementation } /** * The reducer inherited from this context's parent. */ - val inheritedReducer: Reducer? + val inheritedReducer: Reducer by ChainSearchDelegate { reducer } /** From fbdf2c7e52a2f37d69e4221047e1bc8c91ada4af Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Thu, 21 Sep 2017 23:09:13 -0700 Subject: [PATCH 2/7] Eliminate warnings related to null returns that have been removed. --- .../src/main/java/org/nwapw/abacus/plugin/StandardPlugin.java | 4 ++-- core/src/main/kotlin/org/nwapw/abacus/tree/NumberReducer.kt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/nwapw/abacus/plugin/StandardPlugin.java b/core/src/main/java/org/nwapw/abacus/plugin/StandardPlugin.java index 3d20019..9d95303 100755 --- a/core/src/main/java/org/nwapw/abacus/plugin/StandardPlugin.java +++ b/core/src/main/java/org/nwapw/abacus/plugin/StandardPlugin.java @@ -332,7 +332,7 @@ public class StandardPlugin extends Plugin { //We'll use the series \sigma_{n >= 1) ((1/3^n + 1/4^n) * 1/n) //In the following, a=1/3^n, b=1/4^n, c = 1/n. //a is also an error bound. - NumberInterface a = implementation.instanceForString("1"), b = a, c = a; + NumberInterface a = implementation.instanceForString("1"), b = a, c; NumberInterface sum = implementation.instanceForString("0"); NumberInterface one = implementation.instanceForString("1"); int n = 0; @@ -715,7 +715,7 @@ public class StandardPlugin extends Plugin { * @return the value of the series */ private static NumberInterface sinTaylor(MutableEvaluationContext context, NumberInterface x) { - NumberInterface power = x, multiplier = x.multiply(x).negate(), currentTerm = x, sum = x; + NumberInterface power = x, multiplier = x.multiply(x).negate(), currentTerm, sum = x; NumberInterface maxError = x.getMaxError(); int n = 1; do { diff --git a/core/src/main/kotlin/org/nwapw/abacus/tree/NumberReducer.kt b/core/src/main/kotlin/org/nwapw/abacus/tree/NumberReducer.kt index 7eff50a..e03b77e 100644 --- a/core/src/main/kotlin/org/nwapw/abacus/tree/NumberReducer.kt +++ b/core/src/main/kotlin/org/nwapw/abacus/tree/NumberReducer.kt @@ -17,7 +17,7 @@ class NumberReducer(val abacus: Abacus, context: EvaluationContext) : Reducer { - context.inheritedNumberImplementation?.instanceForString(treeNode.number) + context.inheritedNumberImplementation.instanceForString(treeNode.number) ?: throw EvaluationException("no number implementation selected.") } is VariableNode -> { From 76fcd8ec1cb8eea256b51879e80a2fd2c12b7745 Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Thu, 21 Sep 2017 23:17:45 -0700 Subject: [PATCH 3/7] Remove unused elvis. --- core/src/main/kotlin/org/nwapw/abacus/tree/NumberReducer.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/core/src/main/kotlin/org/nwapw/abacus/tree/NumberReducer.kt b/core/src/main/kotlin/org/nwapw/abacus/tree/NumberReducer.kt index e03b77e..80d79cd 100644 --- a/core/src/main/kotlin/org/nwapw/abacus/tree/NumberReducer.kt +++ b/core/src/main/kotlin/org/nwapw/abacus/tree/NumberReducer.kt @@ -18,7 +18,6 @@ class NumberReducer(val abacus: Abacus, context: EvaluationContext) : Reducer { context.inheritedNumberImplementation.instanceForString(treeNode.number) - ?: throw EvaluationException("no number implementation selected.") } is VariableNode -> { val variable = context.getVariable(treeNode.variable) From 8dc7acd4b3f6dd5c2612f546569f8d17cfed5a0e Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Fri, 22 Sep 2017 11:58:19 -0700 Subject: [PATCH 4/7] Add a separate class of exceptions for NumberReducer. --- .../exception/NumberReducerException.java | 25 +++++++++++++++++++ ...Exception.java => ReductionException.java} | 10 +++----- .../org/nwapw/abacus/tree/NumberReducer.kt | 7 +++--- .../org/nwapw/abacus/fx/AbacusController.java | 3 --- 4 files changed, 33 insertions(+), 12 deletions(-) create mode 100644 core/src/main/java/org/nwapw/abacus/exception/NumberReducerException.java rename core/src/main/java/org/nwapw/abacus/exception/{EvaluationException.java => ReductionException.java} (51%) diff --git a/core/src/main/java/org/nwapw/abacus/exception/NumberReducerException.java b/core/src/main/java/org/nwapw/abacus/exception/NumberReducerException.java new file mode 100644 index 0000000..694e0bf --- /dev/null +++ b/core/src/main/java/org/nwapw/abacus/exception/NumberReducerException.java @@ -0,0 +1,25 @@ +package org.nwapw.abacus.exception; + +/** + * Exception thrown by the NumberReducer if something goes wrong when + * transforming a parse tree into a single value. + */ +public class NumberReducerException extends AbacusException { + + /** + * Creates a new NumberReducerException with + * no additional message. + */ + public NumberReducerException() { + this(""); + } + + /** + * Creates a new NumberReducerException with the given message. + * @param message the message. + */ + public NumberReducerException(String message) { + super("Error evaluating expression", message); + } + +} diff --git a/core/src/main/java/org/nwapw/abacus/exception/EvaluationException.java b/core/src/main/java/org/nwapw/abacus/exception/ReductionException.java similarity index 51% rename from core/src/main/java/org/nwapw/abacus/exception/EvaluationException.java rename to core/src/main/java/org/nwapw/abacus/exception/ReductionException.java index 5eed066..d9173a6 100644 --- a/core/src/main/java/org/nwapw/abacus/exception/EvaluationException.java +++ b/core/src/main/java/org/nwapw/abacus/exception/ReductionException.java @@ -1,16 +1,14 @@ package org.nwapw.abacus.exception; /** - * An exception thrown primarily from Tree Value operators and functions, - * which have to deal with the result of a Reducer as well as the results - * of Applicable. + * An exception thrown from TreeReducers. */ -public class EvaluationException extends AbacusException { +public class ReductionException extends AbacusException { /** * Creates a new EvaluationException with the default string. */ - public EvaluationException() { + public ReductionException() { this(""); } @@ -18,7 +16,7 @@ public class EvaluationException extends AbacusException { * Creates a new EvaluationError with the given message string. * @param message the message string. */ - public EvaluationException(String message) { + public ReductionException(String message) { super("Evaluation error", message); } diff --git a/core/src/main/kotlin/org/nwapw/abacus/tree/NumberReducer.kt b/core/src/main/kotlin/org/nwapw/abacus/tree/NumberReducer.kt index 80d79cd..1d9885a 100644 --- a/core/src/main/kotlin/org/nwapw/abacus/tree/NumberReducer.kt +++ b/core/src/main/kotlin/org/nwapw/abacus/tree/NumberReducer.kt @@ -2,7 +2,8 @@ package org.nwapw.abacus.tree import org.nwapw.abacus.Abacus import org.nwapw.abacus.context.EvaluationContext -import org.nwapw.abacus.exception.EvaluationException +import org.nwapw.abacus.exception.NumberReducerException +import org.nwapw.abacus.exception.ReductionException import org.nwapw.abacus.number.NumberInterface class NumberReducer(val abacus: Abacus, context: EvaluationContext) : Reducer { @@ -24,7 +25,7 @@ class NumberReducer(val abacus: Abacus, context: EvaluationContext) : Reducer { val child = children[0] as NumberInterface @@ -57,7 +58,7 @@ class NumberReducer(val abacus: Abacus, context: EvaluationContext) : Reducer throw EvaluationException("unrecognized tree node.") + else -> throw ReductionException("unrecognized tree node.") } } diff --git a/fx/src/main/java/org/nwapw/abacus/fx/AbacusController.java b/fx/src/main/java/org/nwapw/abacus/fx/AbacusController.java index f903618..8c4700d 100644 --- a/fx/src/main/java/org/nwapw/abacus/fx/AbacusController.java +++ b/fx/src/main/java/org/nwapw/abacus/fx/AbacusController.java @@ -13,11 +13,8 @@ import javafx.util.StringConverter; import org.nwapw.abacus.Abacus; import org.nwapw.abacus.config.Configuration; import org.nwapw.abacus.exception.AbacusException; -import org.nwapw.abacus.exception.ComputationInterruptedException; import org.nwapw.abacus.function.Documentation; import org.nwapw.abacus.function.DocumentationType; -import org.nwapw.abacus.exception.DomainException; -import org.nwapw.abacus.exception.EvaluationException; import org.nwapw.abacus.number.*; import org.nwapw.abacus.plugin.ClassFinder; import org.nwapw.abacus.plugin.PluginListener; From a3bfc34c1cd181451eeb35286e415c77890a6e22 Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Fri, 22 Sep 2017 16:35:08 -0700 Subject: [PATCH 5/7] Throw parse exceptions instead of returning null. --- .../abacus/exception/ParseException.java | 23 +++++++++++++++++++ .../abacus/parsing/ShuntingYardParser.java | 19 ++++++++------- 2 files changed, 32 insertions(+), 10 deletions(-) create mode 100644 core/src/main/java/org/nwapw/abacus/exception/ParseException.java diff --git a/core/src/main/java/org/nwapw/abacus/exception/ParseException.java b/core/src/main/java/org/nwapw/abacus/exception/ParseException.java new file mode 100644 index 0000000..cd394a0 --- /dev/null +++ b/core/src/main/java/org/nwapw/abacus/exception/ParseException.java @@ -0,0 +1,23 @@ +package org.nwapw.abacus.exception; + +/** + * Exception thrown by parsers. + */ +public class ParseException extends AbacusException { + + /** + * Creates a new ParseException with no additional message. + */ + public ParseException(){ + this(""); + } + + /** + * Creates a new ParseException with the given additional message. + * @param message the message. + */ + public ParseException(String message){ + super("Failed to parse string", message); + } + +} diff --git a/core/src/main/java/org/nwapw/abacus/parsing/ShuntingYardParser.java b/core/src/main/java/org/nwapw/abacus/parsing/ShuntingYardParser.java index 611f5c9..f405aa9 100644 --- a/core/src/main/java/org/nwapw/abacus/parsing/ShuntingYardParser.java +++ b/core/src/main/java/org/nwapw/abacus/parsing/ShuntingYardParser.java @@ -1,5 +1,6 @@ package org.nwapw.abacus.parsing; +import org.nwapw.abacus.exception.ParseException; import org.nwapw.abacus.function.Operator; import org.nwapw.abacus.function.OperatorAssociativity; import org.nwapw.abacus.function.OperatorType; @@ -99,7 +100,7 @@ public class ShuntingYardParser implements Parser>, PluginListe while (!tokenStack.empty() && tokenStack.peek().getType() != TokenType.OPEN_PARENTH) { output.add(tokenStack.pop()); } - if (tokenStack.empty()) return null; + if (tokenStack.empty()) throw new ParseException("mismatched parentheses"); if (matchType == TokenType.CLOSE_PARENTH) { tokenStack.pop(); } @@ -111,7 +112,7 @@ public class ShuntingYardParser implements Parser>, PluginListe if (!(newMatchType == TokenType.OP || newMatchType == TokenType.TREE_VALUE_OP || newMatchType == TokenType.FUNCTION || - newMatchType == TokenType.TREE_VALUE_FUNCTION)) return null; + newMatchType == TokenType.TREE_VALUE_FUNCTION)) throw new ParseException("mismatched parentheses"); output.add(tokenStack.pop()); } return output; @@ -124,7 +125,7 @@ public class ShuntingYardParser implements Parser>, PluginListe * @return the construct tree expression. */ public TreeNode constructRecursive(List> matches) { - if (matches.size() == 0) return null; + if (matches.size() == 0) throw new ParseException("no tokens left in input"); Match match = matches.remove(0); TokenType matchType = match.getType(); if (matchType == TokenType.OP || matchType == TokenType.TREE_VALUE_OP) { @@ -133,7 +134,6 @@ public class ShuntingYardParser implements Parser>, PluginListe if (type == OperatorType.BINARY_INFIX) { TreeNode right = constructRecursive(matches); TreeNode left = constructRecursive(matches); - if (left == null || right == null) return null; if (matchType == TokenType.OP) { return new NumberBinaryNode(operator, left, right); } else { @@ -141,7 +141,6 @@ public class ShuntingYardParser implements Parser>, PluginListe } } else { TreeNode applyTo = constructRecursive(matches); - if (applyTo == null) return null; if (matchType == TokenType.OP) { return new NumberUnaryNode(operator, applyTo); } else { @@ -157,10 +156,9 @@ public class ShuntingYardParser implements Parser>, PluginListe List children = new ArrayList<>(); while (!matches.isEmpty() && matches.get(0).getType() != TokenType.INTERNAL_FUNCTION_END) { TreeNode argument = constructRecursive(matches); - if (argument == null) return null; children.add(0, argument); } - if (matches.isEmpty()) return null; + if (matches.isEmpty()) throw new ParseException("incorrectly formatted function call"); matches.remove(0); CallNode node; if (matchType == TokenType.FUNCTION) { @@ -170,16 +168,17 @@ public class ShuntingYardParser implements Parser>, PluginListe } return node; } - return null; + throw new ParseException("unrecognized token"); } @Override public TreeNode constructTree(List> tokens) { + if (tokens.isEmpty()) throw new ParseException("no input tokens"); tokens = intoPostfix(new ArrayList<>(tokens)); - if (tokens == null) return null; Collections.reverse(tokens); TreeNode constructedTree = constructRecursive(tokens); - return tokens.size() == 0 ? constructedTree : null; + if(tokens.size() == 0) return constructedTree; + throw new ParseException("could not parse all input"); } @Override From 31996219ad714dc3e866a79feaa4eca08defac62 Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Sat, 23 Sep 2017 15:31:35 -0700 Subject: [PATCH 6/7] Switch the Lexer and TreeBuilder to using exceptions. --- .../abacus/exception/TokenizeException.java | 23 +++++++++++++++++++ .../nwapw/abacus/parsing/LexerTokenizer.java | 5 +++- .../org/nwapw/abacus/parsing/TreeBuilder.java | 4 +--- .../main/kotlin/org/nwapw/abacus/Abacus.kt | 2 +- .../nwapw/abacus/tests/CalculationTests.java | 2 -- .../org/nwapw/abacus/fx/AbacusController.java | 3 --- 6 files changed, 29 insertions(+), 10 deletions(-) create mode 100644 core/src/main/java/org/nwapw/abacus/exception/TokenizeException.java diff --git a/core/src/main/java/org/nwapw/abacus/exception/TokenizeException.java b/core/src/main/java/org/nwapw/abacus/exception/TokenizeException.java new file mode 100644 index 0000000..df92ca1 --- /dev/null +++ b/core/src/main/java/org/nwapw/abacus/exception/TokenizeException.java @@ -0,0 +1,23 @@ +package org.nwapw.abacus.exception; + +/** + * Exception thrown by Lexers when they are unable to tokenize the input string. + */ +public class TokenizeException extends AbacusException { + + /** + * Create a new tokenize exception with no additional data. + */ + public TokenizeException() { + this(""); + } + + /** + * Create a new tokenize exception with the given message. + * @param message the message to use. + */ + public TokenizeException(String message){ + super("Failed to tokenize string", message); + } + +} diff --git a/core/src/main/java/org/nwapw/abacus/parsing/LexerTokenizer.java b/core/src/main/java/org/nwapw/abacus/parsing/LexerTokenizer.java index be57744..1096abb 100644 --- a/core/src/main/java/org/nwapw/abacus/parsing/LexerTokenizer.java +++ b/core/src/main/java/org/nwapw/abacus/parsing/LexerTokenizer.java @@ -1,5 +1,6 @@ package org.nwapw.abacus.parsing; +import org.nwapw.abacus.exception.TokenizeException; import org.nwapw.abacus.lexing.Lexer; import org.nwapw.abacus.lexing.pattern.Match; import org.nwapw.abacus.lexing.pattern.Pattern; @@ -42,7 +43,9 @@ public class LexerTokenizer implements Tokenizer>, PluginListen @Override public List> tokenizeString(String string) { - return lexer.lexAll(string, 0, TOKEN_SORTER); + List> tokens = lexer.lexAll(string, 0, TOKEN_SORTER); + if(tokens == null) throw new TokenizeException(); + return tokens; } @Override diff --git a/core/src/main/java/org/nwapw/abacus/parsing/TreeBuilder.java b/core/src/main/java/org/nwapw/abacus/parsing/TreeBuilder.java index b3d597e..78c2331 100644 --- a/core/src/main/java/org/nwapw/abacus/parsing/TreeBuilder.java +++ b/core/src/main/java/org/nwapw/abacus/parsing/TreeBuilder.java @@ -42,9 +42,7 @@ public class TreeBuilder { * @return the resulting tree. */ public TreeNode fromString(String input) { - List tokens = tokenizer.tokenizeString(input); - if (tokens == null) return null; - return parser.constructTree(tokens); + return parser.constructTree(tokenizer.tokenizeString(input)); } } diff --git a/core/src/main/kotlin/org/nwapw/abacus/Abacus.kt b/core/src/main/kotlin/org/nwapw/abacus/Abacus.kt index 4b78256..4272aa8 100644 --- a/core/src/main/kotlin/org/nwapw/abacus/Abacus.kt +++ b/core/src/main/kotlin/org/nwapw/abacus/Abacus.kt @@ -88,7 +88,7 @@ class Abacus(val configuration: Configuration) { * @param input the input string to parse * @return the resulting tree, null if the tree builder or the produced tree are null. */ - fun parseString(input: String): TreeNode? = treeBuilder.fromString(input) + fun parseString(input: String): TreeNode = treeBuilder.fromString(input) /** * Evaluates the given tree. * diff --git a/core/src/test/java/org/nwapw/abacus/tests/CalculationTests.java b/core/src/test/java/org/nwapw/abacus/tests/CalculationTests.java index 1aee10e..b1c0cd4 100755 --- a/core/src/test/java/org/nwapw/abacus/tests/CalculationTests.java +++ b/core/src/test/java/org/nwapw/abacus/tests/CalculationTests.java @@ -22,7 +22,6 @@ public class CalculationTests { private void testOutput(String input, String parseOutput, String output) { TreeNode parsedTree = abacus.parseString(input); - Assert.assertNotNull(parsedTree); Assert.assertEquals(parsedTree.toString(), parseOutput); NumberInterface result = abacus.evaluateTree(parsedTree).getValue(); Assert.assertNotNull(result); @@ -31,7 +30,6 @@ public class CalculationTests { private void testDomainException(String input, String parseOutput) { TreeNode parsedTree = abacus.parseString(input); - Assert.assertNotNull(parsedTree); Assert.assertEquals(parsedTree.toString(), parseOutput); try { abacus.evaluateTree(parsedTree); diff --git a/fx/src/main/java/org/nwapw/abacus/fx/AbacusController.java b/fx/src/main/java/org/nwapw/abacus/fx/AbacusController.java index 8c4700d..25f8632 100644 --- a/fx/src/main/java/org/nwapw/abacus/fx/AbacusController.java +++ b/fx/src/main/java/org/nwapw/abacus/fx/AbacusController.java @@ -142,9 +142,6 @@ public class AbacusController implements PluginListener { private String attemptCalculation() { try { TreeNode constructedTree = abacus.parseString(inputField.getText()); - if (constructedTree == null) { - return ERR_SYNTAX; - } EvaluationResult result = abacus.evaluateTree(constructedTree); NumberInterface evaluatedNumber = result.getValue(); String resultingString = evaluatedNumber.toString(); From ea4588be44afa53cdff2908edcff6ab93adbd6cd Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Sat, 23 Sep 2017 15:43:07 -0700 Subject: [PATCH 7/7] Add more descriptive message to context exceptions. --- .../main/kotlin/org/nwapw/abacus/context/ChainSearchDelegate.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/kotlin/org/nwapw/abacus/context/ChainSearchDelegate.kt b/core/src/main/kotlin/org/nwapw/abacus/context/ChainSearchDelegate.kt index 64d933a..7f4e21a 100644 --- a/core/src/main/kotlin/org/nwapw/abacus/context/ChainSearchDelegate.kt +++ b/core/src/main/kotlin/org/nwapw/abacus/context/ChainSearchDelegate.kt @@ -24,7 +24,7 @@ class ChainSearchDelegate(private val valueGetter: EvaluationContext.() - currentRef = currentRef.parent ?: break returnedValue = currentRef.valueGetter() } - return returnedValue ?: throw ContextException() + return returnedValue ?: throw ContextException("context chain does not contain value") } } \ No newline at end of file