From a3bfc34c1cd181451eeb35286e415c77890a6e22 Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Fri, 22 Sep 2017 16:35:08 -0700 Subject: [PATCH] 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