1
0
mirror of https://github.com/DanilaFe/abacus synced 2024-12-22 07:20:09 -08:00

Throw parse exceptions instead of returning null.

This commit is contained in:
Danila Fedorin 2017-09-22 16:35:08 -07:00
parent 8dc7acd4b3
commit a3bfc34c1c
2 changed files with 32 additions and 10 deletions

View File

@ -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);
}
}

View File

@ -1,5 +1,6 @@
package org.nwapw.abacus.parsing; package org.nwapw.abacus.parsing;
import org.nwapw.abacus.exception.ParseException;
import org.nwapw.abacus.function.Operator; import org.nwapw.abacus.function.Operator;
import org.nwapw.abacus.function.OperatorAssociativity; import org.nwapw.abacus.function.OperatorAssociativity;
import org.nwapw.abacus.function.OperatorType; import org.nwapw.abacus.function.OperatorType;
@ -99,7 +100,7 @@ public class ShuntingYardParser implements Parser<Match<TokenType>>, PluginListe
while (!tokenStack.empty() && tokenStack.peek().getType() != TokenType.OPEN_PARENTH) { while (!tokenStack.empty() && tokenStack.peek().getType() != TokenType.OPEN_PARENTH) {
output.add(tokenStack.pop()); output.add(tokenStack.pop());
} }
if (tokenStack.empty()) return null; if (tokenStack.empty()) throw new ParseException("mismatched parentheses");
if (matchType == TokenType.CLOSE_PARENTH) { if (matchType == TokenType.CLOSE_PARENTH) {
tokenStack.pop(); tokenStack.pop();
} }
@ -111,7 +112,7 @@ public class ShuntingYardParser implements Parser<Match<TokenType>>, PluginListe
if (!(newMatchType == TokenType.OP || if (!(newMatchType == TokenType.OP ||
newMatchType == TokenType.TREE_VALUE_OP || newMatchType == TokenType.TREE_VALUE_OP ||
newMatchType == TokenType.FUNCTION || newMatchType == TokenType.FUNCTION ||
newMatchType == TokenType.TREE_VALUE_FUNCTION)) return null; newMatchType == TokenType.TREE_VALUE_FUNCTION)) throw new ParseException("mismatched parentheses");
output.add(tokenStack.pop()); output.add(tokenStack.pop());
} }
return output; return output;
@ -124,7 +125,7 @@ public class ShuntingYardParser implements Parser<Match<TokenType>>, PluginListe
* @return the construct tree expression. * @return the construct tree expression.
*/ */
public TreeNode constructRecursive(List<Match<TokenType>> matches) { public TreeNode constructRecursive(List<Match<TokenType>> matches) {
if (matches.size() == 0) return null; if (matches.size() == 0) throw new ParseException("no tokens left in input");
Match<TokenType> match = matches.remove(0); Match<TokenType> match = matches.remove(0);
TokenType matchType = match.getType(); TokenType matchType = match.getType();
if (matchType == TokenType.OP || matchType == TokenType.TREE_VALUE_OP) { if (matchType == TokenType.OP || matchType == TokenType.TREE_VALUE_OP) {
@ -133,7 +134,6 @@ public class ShuntingYardParser implements Parser<Match<TokenType>>, PluginListe
if (type == OperatorType.BINARY_INFIX) { if (type == OperatorType.BINARY_INFIX) {
TreeNode right = constructRecursive(matches); TreeNode right = constructRecursive(matches);
TreeNode left = constructRecursive(matches); TreeNode left = constructRecursive(matches);
if (left == null || right == null) return null;
if (matchType == TokenType.OP) { if (matchType == TokenType.OP) {
return new NumberBinaryNode(operator, left, right); return new NumberBinaryNode(operator, left, right);
} else { } else {
@ -141,7 +141,6 @@ public class ShuntingYardParser implements Parser<Match<TokenType>>, PluginListe
} }
} else { } else {
TreeNode applyTo = constructRecursive(matches); TreeNode applyTo = constructRecursive(matches);
if (applyTo == null) return null;
if (matchType == TokenType.OP) { if (matchType == TokenType.OP) {
return new NumberUnaryNode(operator, applyTo); return new NumberUnaryNode(operator, applyTo);
} else { } else {
@ -157,10 +156,9 @@ public class ShuntingYardParser implements Parser<Match<TokenType>>, PluginListe
List<TreeNode> children = new ArrayList<>(); List<TreeNode> children = new ArrayList<>();
while (!matches.isEmpty() && matches.get(0).getType() != TokenType.INTERNAL_FUNCTION_END) { while (!matches.isEmpty() && matches.get(0).getType() != TokenType.INTERNAL_FUNCTION_END) {
TreeNode argument = constructRecursive(matches); TreeNode argument = constructRecursive(matches);
if (argument == null) return null;
children.add(0, argument); children.add(0, argument);
} }
if (matches.isEmpty()) return null; if (matches.isEmpty()) throw new ParseException("incorrectly formatted function call");
matches.remove(0); matches.remove(0);
CallNode node; CallNode node;
if (matchType == TokenType.FUNCTION) { if (matchType == TokenType.FUNCTION) {
@ -170,16 +168,17 @@ public class ShuntingYardParser implements Parser<Match<TokenType>>, PluginListe
} }
return node; return node;
} }
return null; throw new ParseException("unrecognized token");
} }
@Override @Override
public TreeNode constructTree(List<Match<TokenType>> tokens) { public TreeNode constructTree(List<Match<TokenType>> tokens) {
if (tokens.isEmpty()) throw new ParseException("no input tokens");
tokens = intoPostfix(new ArrayList<>(tokens)); tokens = intoPostfix(new ArrayList<>(tokens));
if (tokens == null) return null;
Collections.reverse(tokens); Collections.reverse(tokens);
TreeNode constructedTree = constructRecursive(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 @Override