diff options
| author | Ben Culkin <scorpress@gmail.com> | 2022-08-16 23:03:27 -0400 |
|---|---|---|
| committer | Ben Culkin <scorpress@gmail.com> | 2022-08-16 23:03:27 -0400 |
| commit | cea3e47938322b97c318dea38dc0d649e196dc1b (patch) | |
| tree | 0ceef0bafbfed7aa5802e8fc526c0c98276f1fff /JPratt | |
| parent | 4869146748ed51eb212935d2b971388fb9e73d37 (diff) | |
Refactor to add backtracking support
This probably doesn't help w/ error messages, but it enables some cool
ideas where syntax can be reused in cases where it would otherwise be
invalid
Diffstat (limited to 'JPratt')
35 files changed, 618 insertions, 283 deletions
diff --git a/JPratt/src/examples/java/bjc/pratt/examples/lang/AssignCommand.java b/JPratt/src/examples/java/bjc/pratt/examples/lang/AssignCommand.java index 78fec0d..138e47e 100644 --- a/JPratt/src/examples/java/bjc/pratt/examples/lang/AssignCommand.java +++ b/JPratt/src/examples/java/bjc/pratt/examples/lang/AssignCommand.java @@ -1,6 +1,8 @@ package bjc.pratt.examples.lang; import bjc.pratt.ParserContext; +import bjc.pratt.commands.CommandResult; +import bjc.pratt.commands.CommandResult.Status; import bjc.pratt.commands.impls.NonBinaryCommand; import bjc.pratt.tokens.StringToken; import bjc.pratt.tokens.Token; @@ -14,7 +16,7 @@ class AssignCommand extends NonBinaryCommand<String, String, TestContext> { } @Override - public Tree<Token<String, String>> denote(final Tree<Token<String, String>> operand, + public CommandResult<String, String> denote(final Tree<Token<String, String>> operand, final Token<String, String> operator, final ParserContext<String, String, TestContext> ctx) throws ParserException { final Token<String, String> name = operand.getHead(); @@ -27,10 +29,12 @@ class AssignCommand extends NonBinaryCommand<String, String, TestContext> { throw new ParserException("Variable name must be simple"); } - final Tree<Token<String, String>> body = ctx.parse.parseExpression(0, ctx.tokens, ctx.state, false); - + final CommandResult<String,String> bodyRes = ctx.parse.parseExpression(0, ctx.tokens, ctx.state, false); + if (bodyRes.status != Status.SUCCESS) return bodyRes; + + Tree<Token<String, String>> body = bodyRes.success(); ctx.state.scopes.top().putKey(name.getValue(), body); - return new SimpleTree<>(new StringToken("assign", "assign"), operand, body); + return CommandResult.success(new SimpleTree<>(new StringToken("assign", "assign"), operand, body)); } } diff --git a/JPratt/src/examples/java/bjc/pratt/examples/lang/PrattParserTest.java b/JPratt/src/examples/java/bjc/pratt/examples/lang/PrattParserTest.java index ba957c9..9d05ca2 100644 --- a/JPratt/src/examples/java/bjc/pratt/examples/lang/PrattParserTest.java +++ b/JPratt/src/examples/java/bjc/pratt/examples/lang/PrattParserTest.java @@ -26,6 +26,8 @@ import java.util.Set; import java.util.function.UnaryOperator; import bjc.pratt.PrattParser; +import bjc.pratt.commands.CommandResult; +import bjc.pratt.commands.CommandResult.Status; import bjc.pratt.commands.InitialCommand; import bjc.pratt.commands.NonInitialCommand; import bjc.pratt.tokens.StringToken; @@ -49,7 +51,7 @@ import bjc.utils.parserutils.splitter.TokenSplitter; public class PrattParserTest { private final static Set<String> ops; private final static Set<String> reserved; - + static { /* * Setup operator hash. @@ -61,9 +63,9 @@ public class PrattParserTest { ops.add("!!!"); ops.add(":="); - + ops.addAll(Arrays.asList("|>", "[|]")); - + ops.addAll(Arrays.asList("->", "=>")); ops.addAll(Arrays.asList("||", "&&")); ops.addAll(Arrays.asList("<=", ">=")); @@ -76,7 +78,7 @@ public class PrattParserTest { ops.addAll(Arrays.asList("(", ")")); ops.addAll(Arrays.asList("[", "]")); ops.addAll(Arrays.asList("{", "}")); - + reserved = new LinkedHashSet<>(); reserved.addAll(Arrays.asList("if", "then", "else")); reserved.addAll(Arrays.asList("and", "or")); @@ -86,23 +88,23 @@ public class PrattParserTest { reserved.addAll(Arrays.asList("try", "throw", "catch", "finally")); reserved.add("var"); } + /** * Main method. * - * @param args - * Unused CLI arguments. + * @param args Unused CLI arguments. */ public static void main(final String[] args) { final ConfigurableTokenSplitter lo = new ConfigurableTokenSplitter(true); lo.addSimpleDelimiters(":="); - + lo.addSimpleDelimiters("|>", "[|]"); - + lo.addSimpleDelimiters("->, =>"); lo.addSimpleDelimiters("||", "&&"); lo.addSimpleDelimiters("<=", ">="); - + lo.addSimpleDelimiters("\u00B1"); // Unicode plus/minus lo.addSimpleDelimiters(".", ",", ";", ":"); lo.addSimpleDelimiters("=", "<", ">"); @@ -142,13 +144,17 @@ public class PrattParserTest { */ tokenStream.next(); - final Tree<Token<String, String>> rawTree = parser.parseExpression(0, tokenStream, ctx, true); + final CommandResult<String, String> rawTree = parser.parseExpression(0, tokenStream, ctx, true); - if (!tokenStream.headIs("(end)")) { - System.out.println("\nMultiple expressions on line"); - } + if (rawTree.status != Status.SUCCESS) { + System.out.println("Command parsing failed."); + } else { + if (!tokenStream.headIs("(end)")) { + System.out.println("\nMultiple expressions on line"); + } - System.out.printf("\nParsed expression:\n%s", rawTree); + System.out.printf("\nParsed expression:\n%s", rawTree.success()); + } } catch (ParserException pex) { pex.printStackTrace(); } @@ -183,8 +189,8 @@ public class PrattParserTest { } String strang = raw.replaceAll("\\.(\\.+)", "$1"); - - if (doSplit) { + + if (doSplit) { ListEx<String> splitStrangs = split.split(strang); splitStrangs.removeMatching(""); @@ -264,7 +270,7 @@ public class PrattParserTest { * Pipeline operator. */ parser.addNonInitialCommand("|>", infixLeft(12)); - + /* * Non-short circuiting conditionals. */ @@ -293,7 +299,7 @@ public class PrattParserTest { * Range operator. */ parser.addNonInitialCommand("[|]", infixNon(18)); - + /* * Add/subtracting operators. */ diff --git a/JPratt/src/examples/java/bjc/pratt/examples/lang/SwitchCommand.java b/JPratt/src/examples/java/bjc/pratt/examples/lang/SwitchCommand.java index fb2ae66..901790e 100644 --- a/JPratt/src/examples/java/bjc/pratt/examples/lang/SwitchCommand.java +++ b/JPratt/src/examples/java/bjc/pratt/examples/lang/SwitchCommand.java @@ -1,6 +1,8 @@ package bjc.pratt.examples.lang; import bjc.pratt.ParserContext; +import bjc.pratt.commands.CommandResult; +import bjc.pratt.commands.CommandResult.Status; import bjc.pratt.commands.InitialCommand; import bjc.pratt.tokens.StringToken; import bjc.pratt.tokens.Token; @@ -10,12 +12,16 @@ import bjc.utils.parserutils.ParserException; class SwitchCommand implements InitialCommand<String, String, TestContext> { @Override - public Tree<Token<String, String>> denote(final Token<String, String> operator, + public CommandResult<String, String> denote(final Token<String, String> operator, final ParserContext<String, String, TestContext> ctx) throws ParserException { - final Tree<Token<String, String>> object = ctx.parse.parseExpression(0, ctx.tokens, ctx.state, false); - - final Tree<Token<String, String>> body = ctx.parse.parseExpression(0, ctx.tokens, ctx.state, false); - - return new SimpleTree<>(new StringToken("switch", "switch"), object, body); + final CommandResult<String,String> objectRes = ctx.parse.parseExpression(0, ctx.tokens, ctx.state, false); + if (objectRes.status != Status.SUCCESS) return objectRes; + Tree<Token<String, String>> object = objectRes.success(); + + final CommandResult<String,String> bodyRes = ctx.parse.parseExpression(0, ctx.tokens, ctx.state, false); + if (bodyRes.status != Status.SUCCESS) return bodyRes; + Tree<Token<String, String>> body = bodyRes.success(); + + return CommandResult.success(new SimpleTree<>(new StringToken("switch", "switch"), object, body)); } } diff --git a/JPratt/src/examples/java/bjc/pratt/examples/lang/VarCommand.java b/JPratt/src/examples/java/bjc/pratt/examples/lang/VarCommand.java index 1f47218..328d6eb 100644 --- a/JPratt/src/examples/java/bjc/pratt/examples/lang/VarCommand.java +++ b/JPratt/src/examples/java/bjc/pratt/examples/lang/VarCommand.java @@ -2,6 +2,8 @@ package bjc.pratt.examples.lang; import bjc.pratt.ParserContext; import bjc.pratt.commands.AbstractInitialCommand; +import bjc.pratt.commands.CommandResult; +import bjc.pratt.commands.CommandResult.Status; import bjc.pratt.tokens.StringToken; import bjc.pratt.tokens.Token; import bjc.data.Tree; @@ -11,7 +13,7 @@ import bjc.utils.parserutils.ParserException; class VarCommand extends AbstractInitialCommand<String, String, TestContext> { @Override - protected Tree<Token<String, String>> intNullDenotation(final Token<String, String> operator, + protected CommandResult<String, String> intNullDenotation(final Token<String, String> operator, final ParserContext<String, String, TestContext> ctx) throws ParserException { final Token<String, String> name = ctx.tokens.current(); @@ -26,11 +28,15 @@ class VarCommand extends AbstractInitialCommand<String, String, TestContext> { ctx.tokens.expect(":="); - final Tree<Token<String, String>> body = ctx.parse.parseExpression(0, ctx.tokens, ctx.state, false); - + final CommandResult<String,String> bodyRes = ctx.parse.parseExpression(0, ctx.tokens, ctx.state, false); + if (bodyRes.status != Status.SUCCESS) return bodyRes; + Tree<Token<String, String>> body = bodyRes.success(); ctx.state.scopes.top().putKey(name.getValue(), body); - - return new SimpleTree<>(new StringToken("var-bind", "var-bind"), new SimpleTree<>(name), body); + + StringToken token = new StringToken("var-bind", "var-bind"); + Tree<Token<String,String>> tree = new SimpleTree<>(token, new SimpleTree<>(name), body); + + return CommandResult.success(tree); } } diff --git a/JPratt/src/main/java/bjc/pratt/PrattParser.java b/JPratt/src/main/java/bjc/pratt/PrattParser.java index 9887aa0..c7d40fc 100644 --- a/JPratt/src/main/java/bjc/pratt/PrattParser.java +++ b/JPratt/src/main/java/bjc/pratt/PrattParser.java @@ -1,16 +1,24 @@ package bjc.pratt; +import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; +import java.util.Iterator; +import java.util.List; import java.util.Map; +import bjc.pratt.commands.CommandResult; +import bjc.pratt.commands.CommandResult.Status; import bjc.pratt.commands.InitialCommand; import bjc.pratt.commands.MetaInitialCommand; import bjc.pratt.commands.MetaNonInitialCommand; import bjc.pratt.commands.NonInitialCommand; import bjc.pratt.commands.impls.DefaultInitialCommand; import bjc.pratt.commands.impls.DefaultNonInitialCommand; +import bjc.pratt.tokens.ExpectionNotMet; import bjc.pratt.tokens.Token; import bjc.pratt.tokens.TokenStream; +import bjc.data.TransformIterator; import bjc.data.Tree; import bjc.utils.funcutils.NumberUtils; import bjc.utils.parserutils.ParserException; @@ -20,14 +28,11 @@ import bjc.utils.parserutils.ParserException; * * @author EVE * - * @param <K> - * The key type for the tokens. + * @param <K> The key type for the tokens. * - * @param <V> - * The value type for the tokens. + * @param <V> The value type for the tokens. * - * @param <C> - * The state type of the parser. + * @param <C> The state type of the parser. * * */ @@ -36,30 +41,33 @@ public class PrattParser<K, V, C> { * Default commands that error when used. */ private final NonInitialCommand<K, V, C> DEFAULT_LEFT_COMMAND = new DefaultNonInitialCommand<>(); + private final List<NonInitialCommand<K, V, C>> DEFAULT_LEFT_LIST = Arrays.asList(DEFAULT_LEFT_COMMAND); + private final InitialCommand<K, V, C> DEFAULT_NULL_COMMAND = new DefaultInitialCommand<>(); + private final List<InitialCommand<K, V, C>> DEFAULT_NULL_LIST = Arrays.asList(DEFAULT_NULL_COMMAND); /* * Left-commands that depend on what the null command was. */ - private final Map<K, Map<K, NonInitialCommand<K, V, C>>> dependantLeftCommands; - private final Map<K, Map<K, MetaNonInitialCommand<K, V, C>>> dependantMetaLeftCommands; + private final Map<K, Map<K, List<NonInitialCommand<K, V, C>>>> dependantLeftCommands; + private final Map<K, Map<K, List<MetaNonInitialCommand<K, V, C>>>> dependantMetaLeftCommands; /* * The left commands. */ - private final Map<K, NonInitialCommand<K, V, C>> leftCommands; - private final Map<K, MetaNonInitialCommand<K, V, C>> metaLeftCommands; + private final Map<K, List<NonInitialCommand<K, V, C>>> leftCommands; + private final Map<K, List<MetaNonInitialCommand<K, V, C>>> metaLeftCommands; /* * The initial commands. */ - private final Map<K, InitialCommand<K, V, C>> nullCommands; - private final Map<K, MetaInitialCommand<K, V, C>> metaNullCommands; + private final Map<K, List<InitialCommand<K, V, C>>> nullCommands; + private final Map<K, List<MetaInitialCommand<K, V, C>>> metaNullCommands; /* * Initial commands only checked for statements. */ - private final Map<K, InitialCommand<K, V, C>> statementCommands; - private final Map<K, MetaInitialCommand<K, V, C>> metaStatementCommands; + private final Map<K, List<InitialCommand<K, V, C>>> statementCommands; + private final Map<K, List<MetaInitialCommand<K, V, C>>> metaStatementCommands; /** * Create a new Pratt parser. @@ -82,185 +90,244 @@ public class PrattParser<K, V, C> { /** * Parse an expression. * - * @param precedence - * The initial precedence for the expression. + * @param precedence The initial precedence for the expression. * - * @param tokens - * The tokens for the expression. + * @param tokens The tokens for the expression. * - * @param state - * The state of the parser. + * @param state The state of the parser. * - * @param isStatement - * Whether or not to parse statements. + * @param isStatement Whether or not to parse statements. * * @return The expression as an AST. * - * @throws ParserException - * If something goes wrong during parsing. + * @throws ParserException If something goes wrong during parsing. */ - public Tree<Token<K, V>> parseExpression(final int precedence, final TokenStream<K, V> tokens, final C state, + public CommandResult<K, V> parseExpression(final int precedence, final TokenStream<K, V> tokens, final C state, final boolean isStatement) throws ParserException { - if(precedence < 0) throw new IllegalArgumentException("Precedence must be greater than zero"); + if (precedence < 0) + throw new IllegalArgumentException("Precedence must be greater than zero"); ParserContext<K, V, C> parserContext = new ParserContext<>(tokens, this, state); + Tree<Token<K, V>> ast = null; + CommandResult<K, V> result; + + tokens.mark(); final Token<K, V> initToken = tokens.current(); tokens.next(); + tokens.mark(); final K initKey = initToken.getKey(); + Iterator<InitialCommand<K, V, C>> nullCommandIter = getInitialCommand(isStatement, initKey, parserContext); + do { + if (!nullCommandIter.hasNext()) { + // Restore to the state we were in before we tried to parse this token. + // Need the commit because rollback doesn't remove marks + tokens.rollback(); + tokens.commit(); + tokens.rollback(); + return CommandResult.backtrack(); + } - InitialCommand<K, V, C> nullCommand = getInitialCommand(isStatement, initKey, parserContext); - Tree<Token<K, V>> ast = nullCommand.denote(initToken, parserContext); - - parserContext.initial = initKey; + InitialCommand<K, V, C> nullCommand = nullCommandIter.next(); + try { + result = nullCommand.denote(initToken, parserContext); + } catch (ExpectionNotMet enm) { + // TODO: Should enm be used for something here? + result = CommandResult.backtrack(); + } + switch (result.status) { + case SUCCESS: + ast = result.success(); + break; + case FAIL: + return result; + case BACKTRACK: + tokens.rollback(); + break; + default: + throw new IllegalStateException("Unhandled result status " + result.status); + } + parserContext.initial = initKey; + } while (result.status != Status.SUCCESS); + tokens.commit(); + // Think this is right... + // Will get rid of all our active marks. + tokens.commit(); int rightPrec = Integer.MAX_VALUE; - while(true) { + outer: while (true) { + tokens.mark(); final Token<K, V> tok = tokens.current(); final K key = tok.getKey(); - NonInitialCommand<K, V, C> leftCommand = getNonInitialCommand(key, parserContext); - - final int leftBind = leftCommand.leftBinding(); - - if(NumberUtils.between(precedence, rightPrec, leftBind)) { - tokens.next(); - - ast = leftCommand.denote(ast, tok, parserContext); - rightPrec = leftCommand.nextBinding(); - } else { - break; - } + Iterator<NonInitialCommand<K, V, C>> leftCommandIter = getNonInitialCommand(key, parserContext); + do { + if (!leftCommandIter.hasNext()) { + // Restore to the state we were in before we tried to parse this token. + // Need the commit because rollback doesn't remove marks + tokens.rollback(); + tokens.commit(); + tokens.rollback(); + return CommandResult.backtrack(); + } + + NonInitialCommand<K, V, C> leftCommand = leftCommandIter.next(); + final int leftBind = leftCommand.leftBinding(); + + if (NumberUtils.between(precedence, rightPrec, leftBind)) { + tokens.next(); + tokens.mark(); + + try { + result = leftCommand.denote(ast, tok, parserContext); + rightPrec = leftCommand.nextBinding(); + } catch (ExpectionNotMet enm) { + result = CommandResult.backtrack(); + } + + switch (result.status) { + case SUCCESS: + tokens.commit(); + tokens.commit(); + ast = result.success(); + break; + case FAIL: + return result; + case BACKTRACK: + tokens.rollback(); + break; + default: + throw new IllegalStateException("Unhandled result status " + result.status); + } + } else { + tokens.commit(); + break outer; + } + } while (result.status != Status.SUCCESS); } - return ast; + return CommandResult.success(ast); } /** * Add a non-initial command to this parser. * - * @param marker - * The key that marks the command. + * @param marker The key that marks the command. * - * @param comm - * The command. + * @param comm The command. */ public void addNonInitialCommand(final K marker, final NonInitialCommand<K, V, C> comm) { - leftCommands.put(marker, comm); + leftCommands.computeIfAbsent(marker, mrk -> new ArrayList<>()).add(comm); } /** * Add a initial command to this parser. * - * @param marker - * The key that marks the command. + * @param marker The key that marks the command. * - * @param comm - * The command. + * @param comm The command. */ public void addInitialCommand(final K marker, final InitialCommand<K, V, C> comm) { - nullCommands.put(marker, comm); + nullCommands.computeIfAbsent(marker, mrk -> new ArrayList<>()).add(comm); } /** * Add a statement command to this parser. * - * The difference between statements and initial commands is that - * statements can only appear at the start of the expression. + * The difference between statements and initial commands is that statements can + * only appear at the start of the expression. * - * @param marker - * The key that marks the command. + * @param marker The key that marks the command. * - * @param comm - * The command. + * @param comm The command. */ public void addStatementCommand(final K marker, final InitialCommand<K, V, C> comm) { - statementCommands.put(marker, comm); + statementCommands.computeIfAbsent(marker, mrk -> new ArrayList<>()).add(comm); } /** * Add a dependent non-initial command to this parser. * - * @param dependant - * The dependent that precedes the command. + * @param dependant The dependent that precedes the command. * - * @param marker - * The token key that marks the command. + * @param marker The token key that marks the command. * - * @param comm - * The command. + * @param comm The command. */ public void addDependantCommand(final K dependant, final K marker, final NonInitialCommand<K, V, C> comm) { - Map<K, NonInitialCommand<K, V, C>> dependantMap = dependantLeftCommands.getOrDefault(dependant, + Map<K, List<NonInitialCommand<K, V, C>>> dependantMap = dependantLeftCommands.getOrDefault(dependant, new HashMap<>()); - dependantMap.put(marker, comm); + dependantMap.computeIfAbsent(marker, mrk -> new ArrayList<>()).add(comm); } /** * Lookup an initial command. * - * @param isStatement - * Whether to look for statement commands or not. + * @param isStatement Whether to look for statement commands or not. * - * @param key - * The key of the command. + * @param key The key of the command. * - * @param ctx - * The context for meta-commands. + * @param ctx The context for meta-commands. * * @return A command attached to that key, or a default implementation. */ - public InitialCommand<K, V, C> getInitialCommand(boolean isStatement, K key, ParserContext<K, V, C> ctx) { - if(isStatement) { - if(metaStatementCommands.containsKey(key)) - return metaStatementCommands.get(key).getCommand(ctx); - else if(statementCommands.containsKey(key)) return statementCommands.get(key); + public Iterator<InitialCommand<K, V, C>> getInitialCommand(boolean isStatement, K key, ParserContext<K, V, C> ctx) { + if (isStatement) { + if (metaStatementCommands.containsKey(key)) { + List<MetaInitialCommand<K, V, C>> lst = metaStatementCommands.get(key); + + return new TransformIterator<>(lst.iterator(), (itm) -> itm.getCommand(ctx)); + } else if (statementCommands.containsKey(key)) { + return statementCommands.get(key).iterator(); + } } - if(metaNullCommands.containsKey(key)) { - return metaNullCommands.get(key).getCommand(ctx); + if (metaNullCommands.containsKey(key)) { + List<MetaInitialCommand<K, V, C>> lst = metaNullCommands.get(key); + + return new TransformIterator<>(lst.iterator(), (itm) -> itm.getCommand(ctx)); } - return nullCommands.getOrDefault(key, DEFAULT_NULL_COMMAND); + return nullCommands.getOrDefault(key, DEFAULT_NULL_LIST).iterator(); } /** * Lookup a non-initial command. * - * @param key - * The key of the command. + * @param key The key of the command. * - * @param ctx - * The context for meta-commands. + * @param ctx The context for meta-commands. * * @return A command attached to that key, or a default implementation. */ - public NonInitialCommand<K, V, C> getNonInitialCommand(K key, ParserContext<K, V, C> ctx) { - if(dependantMetaLeftCommands.containsKey(ctx.initial)) { - Map<K, MetaNonInitialCommand<K, V, C>> dependantCommands = dependantMetaLeftCommands - .get(ctx.initial); + public Iterator<NonInitialCommand<K, V, C>> getNonInitialCommand(K key, ParserContext<K, V, C> ctx) { + if (dependantMetaLeftCommands.containsKey(ctx.initial)) { + Map<K, List<MetaNonInitialCommand<K, V, C>>> dependantCommands = dependantMetaLeftCommands.get(ctx.initial); + + if (dependantCommands.containsKey(key)) { + List<MetaNonInitialCommand<K, V, C>> lst = dependantCommands.get(key); - if(dependantCommands.containsKey(key)) { - return dependantCommands.get(key).getCommand(ctx); + return new TransformIterator<>(lst.iterator(), (itm) -> itm.getCommand(ctx)); } } - if(dependantLeftCommands.containsKey(ctx.initial)) { - Map<K, NonInitialCommand<K, V, C>> dependantCommands = dependantLeftCommands.get(ctx.initial); + if (dependantLeftCommands.containsKey(ctx.initial)) { + Map<K, List<NonInitialCommand<K, V, C>>> dependantCommands = dependantLeftCommands.get(ctx.initial); - if(dependantCommands.containsKey(key)) { - return dependantCommands.getOrDefault(key, DEFAULT_LEFT_COMMAND); + if (dependantCommands.containsKey(key)) { + return dependantCommands.getOrDefault(key, DEFAULT_LEFT_LIST).iterator(); } } - if(metaLeftCommands.containsKey(key)) { - return metaLeftCommands.get(key).getCommand(ctx); + if (metaLeftCommands.containsKey(key)) { + List<MetaNonInitialCommand<K, V, C>> lst = metaLeftCommands.get(key); + return new TransformIterator<>(lst.iterator(), (itm) -> itm.getCommand(ctx)); } - return leftCommands.getOrDefault(key, DEFAULT_LEFT_COMMAND); + return leftCommands.getOrDefault(key, DEFAULT_LEFT_LIST).iterator(); } }
\ No newline at end of file diff --git a/JPratt/src/main/java/bjc/pratt/blocks/ChainParseBlock.java b/JPratt/src/main/java/bjc/pratt/blocks/ChainParseBlock.java index 038b6ea..2717e42 100644 --- a/JPratt/src/main/java/bjc/pratt/blocks/ChainParseBlock.java +++ b/JPratt/src/main/java/bjc/pratt/blocks/ChainParseBlock.java @@ -3,6 +3,8 @@ package bjc.pratt.blocks; import java.util.Set; import bjc.pratt.ParserContext; +import bjc.pratt.commands.CommandResult; +import bjc.pratt.commands.CommandResult.Status; import bjc.pratt.tokens.Token; import bjc.data.Tree; import bjc.data.SimpleTree; @@ -51,9 +53,11 @@ public class ChainParseBlock<K, V, C> implements ParseBlock<K, V, C> { } @Override - public Tree<Token<K, V>> parse(ParserContext<K, V, C> ctx) throws ParserException { - Tree<Token<K, V>> expression = iner.parse(ctx); - + public CommandResult<K, V> parse(ParserContext<K, V, C> ctx) throws ParserException { + CommandResult<K,V> resOuter = iner.parse(ctx); + if (resOuter.status != Status.SUCCESS) return resOuter; + + Tree<Token<K, V>> expression = resOuter.success(); Token<K, V> currentToken = ctx.tokens.current(); if(indicators.contains(currentToken.getKey())) { Tree<Token<K, V>> res = new SimpleTree<>(trm); @@ -63,16 +67,19 @@ public class ChainParseBlock<K, V, C> implements ParseBlock<K, V, C> { res.addChild(new SimpleTree<>(currentToken)); ctx.tokens.next(); - Tree<Token<K, V>> innerExpression = iner.parse(ctx); + CommandResult<K,V> resInner = iner.parse(ctx); + if (resInner.status != Status.SUCCESS) return resInner; + + Tree<Token<K, V>> innerExpression = resInner.success(); res.addChild(innerExpression); currentToken = ctx.tokens.current(); } - return res; + return CommandResult.success(res); } - return expression; + return resOuter; } } diff --git a/JPratt/src/main/java/bjc/pratt/blocks/GrammarParseBlock.java b/JPratt/src/main/java/bjc/pratt/blocks/GrammarParseBlock.java index 2432d6e..3f79665 100644 --- a/JPratt/src/main/java/bjc/pratt/blocks/GrammarParseBlock.java +++ b/JPratt/src/main/java/bjc/pratt/blocks/GrammarParseBlock.java @@ -4,6 +4,8 @@ import java.util.function.Function; import bjc.pratt.ParserContext; import bjc.pratt.PrattParser; +import bjc.pratt.commands.CommandResult; +import bjc.pratt.commands.CommandResult.Status; import bjc.pratt.tokens.Token; import bjc.pratt.tokens.TokenStream; import bjc.data.Tree; @@ -15,23 +17,17 @@ import bjc.utils.parserutils.ParserException; * * @author bjculkin * - * @param <K> - * The key type of the outer tokens. + * @param <K> The key type of the outer tokens. * - * @param <V> - * The value type of the outer tokens. + * @param <V> The value type of the outer tokens. * - * @param <C> - * The state type of the outer parser. + * @param <C> The state type of the outer parser. * - * @param <K2> - * The key type of the inner tokens. + * @param <K2> The key type of the inner tokens. * - * @param <V2> - * The value type of the inner tokens. + * @param <V2> The value type of the inner tokens. * - * @param <C2> - * The state type of the outer parser. + * @param <C2> The state type of the outer parser. */ public class GrammarParseBlock<K, V, C, K2, V2, C2> implements ParseBlock<K, V, C> { private final PrattParser<K2, V2, C2> innr; @@ -46,12 +42,14 @@ public class GrammarParseBlock<K, V, C, K2, V2, C2> implements ParseBlock<K, V, /** * Create a new grammar parser block. * - * @param inner The inner grammar to parse. - * @param precedence The precedence of the expression to parse. - * @param isStatement Is the expression being parsed in statement context? - * @param tokenTransform Function to transform to the new token type. - * @param stateTransform Function to toggle between state types. - * @param expressionTransform Function to transform back to the normal token type. + * @param inner The inner grammar to parse. + * @param precedence The precedence of the expression to parse. + * @param isStatement Is the expression being parsed in statement + * context? + * @param tokenTransform Function to transform to the new token type. + * @param stateTransform Function to toggle between state types. + * @param expressionTransform Function to transform back to the normal token + * type. */ public GrammarParseBlock(final PrattParser<K2, V2, C2> inner, final int precedence, final boolean isStatement, final Function<TokenStream<K, V>, TokenStream<K2, V2>> tokenTransform, @@ -66,16 +64,27 @@ public class GrammarParseBlock<K, V, C, K2, V2, C2> implements ParseBlock<K, V, } @Override - public Tree<Token<K, V>> parse(final ParserContext<K, V, C> ctx) throws ParserException { + public CommandResult<K, V> parse(final ParserContext<K, V, C> ctx) throws ParserException { final C2 newState = stteTransform.to(ctx.state); final TokenStream<K2, V2> newTokens = tkenTransform.apply(ctx.tokens); - final Tree<Token<K2, V2>> expression = innr.parseExpression(prcedence, newTokens, newState, - isStatemnt); + final CommandResult<K2, V2> res = innr.parseExpression(prcedence, newTokens, newState, isStatemnt); + switch (res.status) { + case SUCCESS: + break; + case FAIL: + return CommandResult.fail(); + case BACKTRACK: + return CommandResult.backtrack(); + default: + throw new IllegalStateException("Unhandled status " + res.status); + } + + Tree<Token<K2, V2>> expression = res.success(); ctx.state = stteTransform.from(newState); - return xpressionTransform.apply(expression); + return CommandResult.success(xpressionTransform.apply(expression)); } }
\ No newline at end of file diff --git a/JPratt/src/main/java/bjc/pratt/blocks/ParseBlock.java b/JPratt/src/main/java/bjc/pratt/blocks/ParseBlock.java index 81ba508..fede096 100644 --- a/JPratt/src/main/java/bjc/pratt/blocks/ParseBlock.java +++ b/JPratt/src/main/java/bjc/pratt/blocks/ParseBlock.java @@ -1,6 +1,7 @@ package bjc.pratt.blocks; import bjc.pratt.ParserContext; +import bjc.pratt.commands.CommandResult; import bjc.pratt.tokens.Token; import bjc.data.Tree; import bjc.utils.parserutils.ParserException; @@ -34,6 +35,5 @@ public interface ParseBlock<K, V, C> { * If something goes wrong during parsing, or the block fails * validation. */ - Tree<Token<K, V>> parse(ParserContext<K, V, C> ctx) throws ParserException; - + CommandResult<K, V> parse(ParserContext<K, V, C> ctx) throws ParserException; }
\ No newline at end of file diff --git a/JPratt/src/main/java/bjc/pratt/blocks/RepeatingParseBlock.java b/JPratt/src/main/java/bjc/pratt/blocks/RepeatingParseBlock.java index 4c21358..722e395 100644 --- a/JPratt/src/main/java/bjc/pratt/blocks/RepeatingParseBlock.java +++ b/JPratt/src/main/java/bjc/pratt/blocks/RepeatingParseBlock.java @@ -3,6 +3,8 @@ package bjc.pratt.blocks; import java.util.function.UnaryOperator; import bjc.pratt.ParserContext; +import bjc.pratt.commands.CommandResult; +import bjc.pratt.commands.CommandResult.Status; import bjc.pratt.tokens.Token; import bjc.data.Tree; import bjc.data.SimpleTree; @@ -72,13 +74,15 @@ public class RepeatingParseBlock<K, V, C> implements ParseBlock<K, V, C> { } @Override - public Tree<Token<K, V>> parse(final ParserContext<K, V, C> ctx) throws ParserException { + public CommandResult<K, V> parse(final ParserContext<K, V, C> ctx) throws ParserException { final Tree<Token<K, V>> ret = new SimpleTree<>(mark); Token<K, V> tok = ctx.tokens.current(); while(!tok.getKey().equals(term)) { - final Tree<Token<K, V>> kid = innerBlock.parse(ctx); + final CommandResult<K,V> resKid = innerBlock.parse(ctx); + if (resKid.status != Status.SUCCESS) return resKid; + Tree<Token<K, V>> kid = resKid.success(); ret.addChild(kid); tok = ctx.tokens.current(); @@ -90,7 +94,7 @@ public class RepeatingParseBlock<K, V, C> implements ParseBlock<K, V, C> { } } - return ret; + return CommandResult.success(ret); } } diff --git a/JPratt/src/main/java/bjc/pratt/blocks/SimpleParseBlock.java b/JPratt/src/main/java/bjc/pratt/blocks/SimpleParseBlock.java index 83e1d91..b674815 100644 --- a/JPratt/src/main/java/bjc/pratt/blocks/SimpleParseBlock.java +++ b/JPratt/src/main/java/bjc/pratt/blocks/SimpleParseBlock.java @@ -3,6 +3,8 @@ package bjc.pratt.blocks; import java.util.function.Predicate; import bjc.pratt.ParserContext; +import bjc.pratt.commands.CommandResult; +import bjc.pratt.commands.CommandResult.Status; import bjc.pratt.tokens.Token; import bjc.data.Tree; import bjc.utils.parserutils.ParserException; @@ -49,15 +51,18 @@ public class SimpleParseBlock<K, V, C> implements ParseBlock<K, V, C> { } @Override - public Tree<Token<K, V>> parse(final ParserContext<K, V, C> ctx) throws ParserException { - final Tree<Token<K, V>> res = ctx.parse.parseExpression(pow, ctx.tokens, ctx.state, false); - + public CommandResult<K, V> parse(final ParserContext<K, V, C> ctx) throws ParserException { + final CommandResult<K,V> resBlock = ctx.parse.parseExpression(pow, ctx.tokens, ctx.state, false); + if (resBlock.status != Status.SUCCESS) return resBlock; + + Tree<Token<K, V>> res = resBlock.success(); if(term != null) { ctx.tokens.expect(term); } - if(validatr == null || validatr.test(res)) return res; + if(validatr == null || validatr.test(res)) return CommandResult.success(res); + // TODO: Figure out the right way to handle error context w/ CommandResult throw new ParserException("Block failed validation"); } diff --git a/JPratt/src/main/java/bjc/pratt/blocks/TriggeredParseBlock.java b/JPratt/src/main/java/bjc/pratt/blocks/TriggeredParseBlock.java index bfe5ab3..df17595 100644 --- a/JPratt/src/main/java/bjc/pratt/blocks/TriggeredParseBlock.java +++ b/JPratt/src/main/java/bjc/pratt/blocks/TriggeredParseBlock.java @@ -3,6 +3,8 @@ package bjc.pratt.blocks; import java.util.function.UnaryOperator; import bjc.pratt.ParserContext; +import bjc.pratt.commands.CommandResult; +import bjc.pratt.commands.CommandResult.Status; import bjc.pratt.tokens.Token; import bjc.data.Tree; import bjc.utils.parserutils.ParserException; @@ -45,13 +47,15 @@ public class TriggeredParseBlock<K, V, C> implements ParseBlock<K, V, C> { } @Override - public Tree<Token<K, V>> parse(final ParserContext<K, V, C> ctx) throws ParserException { + public CommandResult<K, V> parse(final ParserContext<K, V, C> ctx) throws ParserException { final C newState = onEntr.apply(ctx.state); final ParserContext<K, V, C> newCtx = new ParserContext<>(ctx.tokens, ctx.parse, newState); - final Tree<Token<K, V>> res = sourc.parse(newCtx); + final CommandResult<K,V> res = sourc.parse(newCtx); + if (res.status != Status.SUCCESS) return res; + ctx.state = onExt.apply(newState); return res; diff --git a/JPratt/src/main/java/bjc/pratt/commands/AbstractInitialCommand.java b/JPratt/src/main/java/bjc/pratt/commands/AbstractInitialCommand.java index 50e884b..48f9ba7 100644 --- a/JPratt/src/main/java/bjc/pratt/commands/AbstractInitialCommand.java +++ b/JPratt/src/main/java/bjc/pratt/commands/AbstractInitialCommand.java @@ -21,7 +21,7 @@ import bjc.utils.parserutils.ParserException; */ public abstract class AbstractInitialCommand<K, V, C> implements InitialCommand<K, V, C> { @Override - public Tree<Token<K, V>> denote(final Token<K, V> operator, + public CommandResult<K, V> denote(final Token<K, V> operator, final ParserContext<K, V, C> ctx) throws ParserException { return intNullDenotation(operator, ctx); } @@ -39,7 +39,7 @@ public abstract class AbstractInitialCommand<K, V, C> implements InitialCommand< * @throws ParserException * If something went wrong while parsing. */ - protected abstract Tree<Token<K, V>> intNullDenotation(Token<K, V> operator, + protected abstract CommandResult<K, V> intNullDenotation(Token<K, V> operator, ParserContext<K, V, C> ctx) throws ParserException; }
\ No newline at end of file diff --git a/JPratt/src/main/java/bjc/pratt/commands/BinaryCommand.java b/JPratt/src/main/java/bjc/pratt/commands/BinaryCommand.java index 069de78..7a65052 100644 --- a/JPratt/src/main/java/bjc/pratt/commands/BinaryCommand.java +++ b/JPratt/src/main/java/bjc/pratt/commands/BinaryCommand.java @@ -1,6 +1,7 @@ package bjc.pratt.commands; import bjc.pratt.ParserContext; +import bjc.pratt.commands.CommandResult.Status; import bjc.pratt.tokens.Token; import bjc.data.Tree; import bjc.data.SimpleTree; @@ -39,12 +40,14 @@ public abstract class BinaryCommand<K, V, C> extends BinaryPostCommand<K, V, C> protected abstract int rightBinding(); @Override - public Tree<Token<K, V>> denote(final Tree<Token<K, V>> operand, + public CommandResult<K, V> denote(final Tree<Token<K, V>> operand, final Token<K, V> operator, final ParserContext<K, V, C> ctx) throws ParserException { - final Tree<Token<K, V>> opr + final CommandResult<K,V> opr = ctx.parse.parseExpression(rightBinding(), ctx.tokens, ctx.state, false); + + if (opr.status != Status.SUCCESS) return opr; - return new SimpleTree<>(operator, operand, opr); + return CommandResult.success(new SimpleTree<>(operator, operand, opr.success())); } }
\ No newline at end of file diff --git a/JPratt/src/main/java/bjc/pratt/commands/CommandResult.java b/JPratt/src/main/java/bjc/pratt/commands/CommandResult.java new file mode 100644 index 0000000..d27400f --- /dev/null +++ b/JPratt/src/main/java/bjc/pratt/commands/CommandResult.java @@ -0,0 +1,97 @@ +package bjc.pratt.commands; + +import bjc.data.Tree; +import bjc.pratt.tokens.Token; + +/** + * Represents the result of executing a command. + * + * @author bjcul + * + * @param <K> The key type of the tokens + * @param <V> The value type of the tokens + */ +public class CommandResult<K, V> { + /** + * Represents the status of a command execution + * + * @author bjcul + * + */ + public static enum Status { + /** + * The command successfully parsed. + */ + SUCCESS, + /** + * The command failed, in a non-recoverable way + */ + FAIL, + /** + * The command failed. Attempt recovery via backtracking + */ + BACKTRACK + } + + /** + * The status of this command. + */ + public final Status status; + + private Tree<Token<K, V>> success; + + private CommandResult(Status status) { + this.status = status; + } + + /** + * Get the success value of this command, or null if it failed. + * + * @return The success value of the command + */ + public Tree<Token<K, V>> success() { + return success; + } + + /** + * Create a success result + * + * @param <K2> The key type of the token + * @param <V2> The value type of the token + * + * @param succ The tree produced by the command + * + * @return A command result representing a success + */ + public static <K2, V2> CommandResult<K2, V2> success(Tree<Token<K2, V2>> succ) { + CommandResult<K2, V2> result = new CommandResult<>(Status.SUCCESS); + result.success = succ; + return result; + } + + /** + * Create a non-backtracking failure result. + * + * @param <K2> The key type of the token + * @param <V2> The value type of the token + * + * @return A command result representing a non-backtracking fail + */ + public static <K2, V2> CommandResult<K2, V2> fail() { + CommandResult<K2, V2> result = new CommandResult<>(Status.FAIL); + return result; + } + + /** + * Create a backtracking failure result. + * + * @param <K2> The key type of the token + * @param <V2> The value type of the token + * + * @return A command result representing a backtracking fail + */ + public static <K2, V2> CommandResult<K2, V2> backtrack() { + CommandResult<K2, V2> result = new CommandResult<>(Status.BACKTRACK); + return result; + } +} diff --git a/JPratt/src/main/java/bjc/pratt/commands/InitialCommand.java b/JPratt/src/main/java/bjc/pratt/commands/InitialCommand.java index 3a2a8ff..ba544b4 100644 --- a/JPratt/src/main/java/bjc/pratt/commands/InitialCommand.java +++ b/JPratt/src/main/java/bjc/pratt/commands/InitialCommand.java @@ -31,10 +31,10 @@ public interface InitialCommand<K, V, C> { * @param ctx * The context for the command. * - * @return The tree for this command. + * @return The result of executing the command. * * @throws ParserException * If something goes wrong during parsing. */ - Tree<Token<K, V>> denote(Token<K, V> operator, ParserContext<K, V, C> ctx) throws ParserException; + CommandResult<K, V> denote(Token<K, V> operator, ParserContext<K, V, C> ctx) throws ParserException; } diff --git a/JPratt/src/main/java/bjc/pratt/commands/NonInitialCommand.java b/JPratt/src/main/java/bjc/pratt/commands/NonInitialCommand.java index 12eecb6..02826a9 100644 --- a/JPratt/src/main/java/bjc/pratt/commands/NonInitialCommand.java +++ b/JPratt/src/main/java/bjc/pratt/commands/NonInitialCommand.java @@ -32,12 +32,12 @@ public abstract class NonInitialCommand<K, V, C> { * @param ctx * The state needed for commands. * - * @return The tree this command forms. + * @return The result of executing the command. * * @throws ParserException * If something went wrong during parsing. */ - public abstract Tree<Token<K, V>> denote(Tree<Token<K, V>> operand, Token<K, V> operator, + public abstract CommandResult<K, V> denote(Tree<Token<K, V>> operand, Token<K, V> operator, ParserContext<K, V, C> ctx) throws ParserException; /** diff --git a/JPratt/src/main/java/bjc/pratt/commands/impls/BlockInitialCommand.java b/JPratt/src/main/java/bjc/pratt/commands/impls/BlockInitialCommand.java index db11484..1d54996 100644 --- a/JPratt/src/main/java/bjc/pratt/commands/impls/BlockInitialCommand.java +++ b/JPratt/src/main/java/bjc/pratt/commands/impls/BlockInitialCommand.java @@ -3,6 +3,7 @@ package bjc.pratt.commands.impls; import bjc.pratt.ParserContext; import bjc.pratt.blocks.ParseBlock; import bjc.pratt.commands.AbstractInitialCommand; +import bjc.pratt.commands.CommandResult; import bjc.pratt.tokens.Token; import bjc.data.Tree; import bjc.utils.parserutils.ParserException; @@ -35,7 +36,7 @@ public class BlockInitialCommand<K, V, C> extends AbstractInitialCommand<K, V, C } @Override - protected Tree<Token<K, V>> intNullDenotation(final Token<K, V> operator, final ParserContext<K, V, C> ctx) + protected CommandResult<K, V> intNullDenotation(final Token<K, V> operator, final ParserContext<K, V, C> ctx) throws ParserException { return blck.parse(ctx); } diff --git a/JPratt/src/main/java/bjc/pratt/commands/impls/ChainCommand.java b/JPratt/src/main/java/bjc/pratt/commands/impls/ChainCommand.java index ed7a088..7311eb9 100644 --- a/JPratt/src/main/java/bjc/pratt/commands/impls/ChainCommand.java +++ b/JPratt/src/main/java/bjc/pratt/commands/impls/ChainCommand.java @@ -4,6 +4,8 @@ import java.util.Set; import bjc.pratt.ParserContext; import bjc.pratt.commands.BinaryPostCommand; +import bjc.pratt.commands.CommandResult; +import bjc.pratt.commands.CommandResult.Status; import bjc.pratt.tokens.Token; import bjc.data.Tree; import bjc.data.SimpleTree; @@ -48,10 +50,12 @@ public class ChainCommand<K, V, C> extends BinaryPostCommand<K, V, C> { } @Override - public Tree<Token<K, V>> denote(final Tree<Token<K, V>> operand, final Token<K, V> operator, + public CommandResult<K, V> denote(final Tree<Token<K, V>> operand, final Token<K, V> operator, final ParserContext<K, V, C> ctx) throws ParserException { - final Tree<Token<K, V>> tree = ctx.parse.parseExpression(1 + leftBinding(), ctx.tokens, ctx.state, + CommandResult<K, V> resOuter = ctx.parse.parseExpression(1 + leftBinding(), ctx.tokens, ctx.state, false); + if (resOuter.status != Status.SUCCESS) return resOuter; + final Tree<Token<K, V>> tree = resOuter.success(); final Tree<Token<K, V>> res = new SimpleTree<>(operator, operand, tree); @@ -59,13 +63,16 @@ public class ChainCommand<K, V, C> extends BinaryPostCommand<K, V, C> { final Token<K, V> tok = ctx.tokens.current(); ctx.tokens.next(); - final Tree<Token<K, V>> other = denote(tree, tok, + CommandResult<K, V> resOther = denote(tree, tok, new ParserContext<>(ctx.tokens, ctx.parse, ctx.state)); + if (resOther.status != Status.SUCCESS) return resOther; + + final Tree<Token<K, V>> other = resOther.success(); - return new SimpleTree<>(chain, res, other); + return CommandResult.success(new SimpleTree<>(chain, res, other)); } - return res; + return CommandResult.success(res); } @Override diff --git a/JPratt/src/main/java/bjc/pratt/commands/impls/ConstantCommand.java b/JPratt/src/main/java/bjc/pratt/commands/impls/ConstantCommand.java index 409adbb..657743c 100644 --- a/JPratt/src/main/java/bjc/pratt/commands/impls/ConstantCommand.java +++ b/JPratt/src/main/java/bjc/pratt/commands/impls/ConstantCommand.java @@ -1,6 +1,7 @@ package bjc.pratt.commands.impls; import bjc.pratt.ParserContext; +import bjc.pratt.commands.CommandResult; import bjc.pratt.commands.InitialCommand; import bjc.pratt.tokens.Token; import bjc.data.Tree; @@ -34,8 +35,8 @@ public class ConstantCommand<K, V, C> implements InitialCommand<K, V, C> { } @Override - public Tree<Token<K, V>> denote(final Token<K, V> operator, final ParserContext<K, V, C> ctx) + public CommandResult<K, V> denote(final Token<K, V> operator, final ParserContext<K, V, C> ctx) throws ParserException { - return val; + return CommandResult.success(val); } }
\ No newline at end of file diff --git a/JPratt/src/main/java/bjc/pratt/commands/impls/DefaultInitialCommand.java b/JPratt/src/main/java/bjc/pratt/commands/impls/DefaultInitialCommand.java index 296cb1c..2dfc576 100644 --- a/JPratt/src/main/java/bjc/pratt/commands/impls/DefaultInitialCommand.java +++ b/JPratt/src/main/java/bjc/pratt/commands/impls/DefaultInitialCommand.java @@ -1,6 +1,7 @@ package bjc.pratt.commands.impls; import bjc.pratt.ParserContext; +import bjc.pratt.commands.CommandResult; import bjc.pratt.commands.InitialCommand; import bjc.pratt.tokens.Token; import bjc.data.Tree; @@ -22,7 +23,7 @@ import bjc.utils.parserutils.ParserException; */ public class DefaultInitialCommand<K, V, C> implements InitialCommand<K, V, C> { @Override - public Tree<Token<K, V>> denote(final Token<K, V> operator, final ParserContext<K, V, C> ctx) + public CommandResult<K, V> denote(final Token<K, V> operator, final ParserContext<K, V, C> ctx) throws ParserException { throw new ParserException("Unexpected token " + operator); } diff --git a/JPratt/src/main/java/bjc/pratt/commands/impls/DefaultNonInitialCommand.java b/JPratt/src/main/java/bjc/pratt/commands/impls/DefaultNonInitialCommand.java index 797473a..2ae9fb7 100644 --- a/JPratt/src/main/java/bjc/pratt/commands/impls/DefaultNonInitialCommand.java +++ b/JPratt/src/main/java/bjc/pratt/commands/impls/DefaultNonInitialCommand.java @@ -1,6 +1,7 @@ package bjc.pratt.commands.impls; import bjc.pratt.ParserContext; +import bjc.pratt.commands.CommandResult; import bjc.pratt.commands.NonInitialCommand; import bjc.pratt.tokens.Token; import bjc.data.Tree; @@ -21,7 +22,7 @@ import bjc.data.Tree; */ public class DefaultNonInitialCommand<K, V, C> extends NonInitialCommand<K, V, C> { @Override - public Tree<Token<K, V>> denote(final Tree<Token<K, V>> operand, final Token<K, V> operator, + public CommandResult<K, V> denote(final Tree<Token<K, V>> operand, final Token<K, V> operator, final ParserContext<K, V, C> ctx) { throw new UnsupportedOperationException("Default command has no left denotation"); } diff --git a/JPratt/src/main/java/bjc/pratt/commands/impls/DenestingCommand.java b/JPratt/src/main/java/bjc/pratt/commands/impls/DenestingCommand.java index fbcd35c..4935c81 100644 --- a/JPratt/src/main/java/bjc/pratt/commands/impls/DenestingCommand.java +++ b/JPratt/src/main/java/bjc/pratt/commands/impls/DenestingCommand.java @@ -2,6 +2,8 @@ package bjc.pratt.commands.impls; import bjc.pratt.ParserContext; import bjc.pratt.commands.AbstractInitialCommand; +import bjc.pratt.commands.CommandResult; +import bjc.pratt.commands.CommandResult.Status; import bjc.pratt.commands.InitialCommand; import bjc.pratt.tokens.Token; import bjc.data.Tree; @@ -39,8 +41,10 @@ public class DenestingCommand<K, V, C> extends AbstractInitialCommand<K, V, C> { } @Override - protected Tree<Token<K, V>> intNullDenotation(final Token<K, V> operator, final ParserContext<K, V, C> ctx) + protected CommandResult<K, V> intNullDenotation(final Token<K, V> operator, final ParserContext<K, V, C> ctx) throws ParserException { - return wrapped.denote(operator, ctx).getChild(0); + CommandResult<K, V> res = wrapped.denote(operator, ctx); + if (res.status != Status.SUCCESS) return res; + return CommandResult.success(res.success().getChild(0)); } }
\ No newline at end of file diff --git a/JPratt/src/main/java/bjc/pratt/commands/impls/GroupingCommand.java b/JPratt/src/main/java/bjc/pratt/commands/impls/GroupingCommand.java index 1515359..44aa2c1 100644 --- a/JPratt/src/main/java/bjc/pratt/commands/impls/GroupingCommand.java +++ b/JPratt/src/main/java/bjc/pratt/commands/impls/GroupingCommand.java @@ -3,6 +3,7 @@ package bjc.pratt.commands.impls; import bjc.pratt.ParserContext; import bjc.pratt.blocks.ParseBlock; import bjc.pratt.commands.AbstractInitialCommand; +import bjc.pratt.commands.CommandResult; import bjc.pratt.tokens.Token; import bjc.data.Tree; import bjc.data.SimpleTree; @@ -43,10 +44,10 @@ public class GroupingCommand<K, V, C> extends AbstractInitialCommand<K, V, C> { } @Override - protected Tree<Token<K, V>> intNullDenotation(final Token<K, V> operator, final ParserContext<K, V, C> ctx) + protected CommandResult<K, V> intNullDenotation(final Token<K, V> operator, final ParserContext<K, V, C> ctx) throws ParserException { - final Tree<Token<K, V>> opr = innerBlock.parse(ctx); - - return new SimpleTree<>(mark, opr); + final CommandResult<K,V> resOpr = innerBlock.parse(ctx); + Tree<Token<K, V>> opr = resOpr.success(); + return CommandResult.success(new SimpleTree<>(mark, opr)); } }
\ No newline at end of file diff --git a/JPratt/src/main/java/bjc/pratt/commands/impls/InitialCommands.java b/JPratt/src/main/java/bjc/pratt/commands/impls/InitialCommands.java index 0cfec29..9801788 100644 --- a/JPratt/src/main/java/bjc/pratt/commands/impls/InitialCommands.java +++ b/JPratt/src/main/java/bjc/pratt/commands/impls/InitialCommands.java @@ -9,6 +9,7 @@ import java.util.function.UnaryOperator; import bjc.pratt.blocks.ParseBlock; import bjc.pratt.commands.InitialCommand; import bjc.pratt.tokens.Token; +import bjc.utils.parserutils.ParserException; import bjc.data.SimpleTree; import bjc.data.Tree; @@ -210,16 +211,6 @@ public class InitialCommands { * @return A command that implements a panfix operator */ public static <K, V, C> InitialCommand<K, V, C> panfix(final int precedence, final K term, final Token<K, V> marker) { - return (operator, ctx) -> { - Tree<Token<K,V>> leftSide = ctx.parse.parseExpression(precedence + 1, ctx.tokens, ctx.state, false); - ctx.tokens.expect(term); - ctx.tokens.next(); - - Tree<Token<K,V>> rightSide = ctx.parse.parseExpression(precedence + 1, ctx.tokens, ctx.state, false); - ctx.tokens.expect(term); - ctx.tokens.next(); - - return new SimpleTree<>(marker, leftSide, rightSide); - }; + return new PanfixCommand<K, V, C>(marker, term, precedence); } }
\ No newline at end of file diff --git a/JPratt/src/main/java/bjc/pratt/commands/impls/LeafCommand.java b/JPratt/src/main/java/bjc/pratt/commands/impls/LeafCommand.java index 1223641..c702323 100644 --- a/JPratt/src/main/java/bjc/pratt/commands/impls/LeafCommand.java +++ b/JPratt/src/main/java/bjc/pratt/commands/impls/LeafCommand.java @@ -1,6 +1,7 @@ package bjc.pratt.commands.impls; import bjc.pratt.ParserContext; +import bjc.pratt.commands.CommandResult; import bjc.pratt.commands.InitialCommand; import bjc.pratt.tokens.Token; import bjc.data.Tree; @@ -23,8 +24,8 @@ import bjc.utils.parserutils.ParserException; */ public class LeafCommand<K, V, C> implements InitialCommand<K, V, C> { @Override - public Tree<Token<K, V>> denote(final Token<K, V> operator, final ParserContext<K, V, C> ctx) + public CommandResult<K, V> denote(final Token<K, V> operator, final ParserContext<K, V, C> ctx) throws ParserException { - return new SimpleTree<>(operator); + return CommandResult.success(new SimpleTree<>(operator)); } }
\ No newline at end of file diff --git a/JPratt/src/main/java/bjc/pratt/commands/impls/PanfixCommand.java b/JPratt/src/main/java/bjc/pratt/commands/impls/PanfixCommand.java new file mode 100644 index 0000000..06e69c1 --- /dev/null +++ b/JPratt/src/main/java/bjc/pratt/commands/impls/PanfixCommand.java @@ -0,0 +1,39 @@ +package bjc.pratt.commands.impls; + +import bjc.data.SimpleTree; +import bjc.data.Tree; +import bjc.pratt.ParserContext; +import bjc.pratt.commands.CommandResult; +import bjc.pratt.commands.InitialCommand; +import bjc.pratt.commands.CommandResult.Status; +import bjc.pratt.tokens.Token; +import bjc.utils.parserutils.ParserException; + +public final class PanfixCommand<K, V, C> implements InitialCommand<K, V, C> { + private final Token<K, V> marker; + private final K term; + private final int precedence; + + public PanfixCommand(Token<K, V> marker, K term, int precedence) { + this.marker = marker; + this.term = term; + this.precedence = precedence; + } + + @Override + public CommandResult<K, V> denote(Token<K, V> operator, ParserContext<K, V, C> ctx) throws ParserException { + CommandResult<K,V> resLeftSide = ctx.parse.parseExpression(precedence + 1, ctx.tokens, ctx.state, false); + if (resLeftSide.status != Status.SUCCESS) return resLeftSide; + Tree<Token<K, V>> leftSide = resLeftSide.success(); + ctx.tokens.expect(term); + ctx.tokens.next(); + + CommandResult<K, V> resRightSide = ctx.parse.parseExpression(precedence + 1, ctx.tokens, ctx.state, false); + if (resLeftSide.status != Status.SUCCESS) return resRightSide; + Tree<Token<K,V>> rightSide = resRightSide.success(); + ctx.tokens.expect(term); + ctx.tokens.next(); + + return CommandResult.success(new SimpleTree<>(marker, leftSide, rightSide)); + } +}
\ No newline at end of file diff --git a/JPratt/src/main/java/bjc/pratt/commands/impls/PostCircumfixCommand.java b/JPratt/src/main/java/bjc/pratt/commands/impls/PostCircumfixCommand.java index 78ac1ef..ec2c8fb 100644 --- a/JPratt/src/main/java/bjc/pratt/commands/impls/PostCircumfixCommand.java +++ b/JPratt/src/main/java/bjc/pratt/commands/impls/PostCircumfixCommand.java @@ -3,6 +3,8 @@ package bjc.pratt.commands.impls; import bjc.pratt.ParserContext; import bjc.pratt.blocks.ParseBlock; import bjc.pratt.commands.BinaryPostCommand; +import bjc.pratt.commands.CommandResult; +import bjc.pratt.commands.CommandResult.Status; import bjc.pratt.tokens.Token; import bjc.data.Tree; import bjc.data.SimpleTree; @@ -50,10 +52,11 @@ public class PostCircumfixCommand<K, V, C> extends BinaryPostCommand<K, V, C> { } @Override - public Tree<Token<K, V>> denote(final Tree<Token<K, V>> operand, final Token<K, V> operator, + public CommandResult<K, V> denote(final Tree<Token<K, V>> operand, final Token<K, V> operator, final ParserContext<K, V, C> ctx) throws ParserException { - final Tree<Token<K, V>> inside = innerBlock.parse(ctx); - - return new SimpleTree<>(mark, operand, inside); + final CommandResult<K,V> insideRes = innerBlock.parse(ctx); + if (insideRes.status != Status.SUCCESS) return insideRes; + Tree<Token<K, V>> inside = insideRes.success(); + return CommandResult.success(new SimpleTree<>(mark, operand, inside)); } }
\ No newline at end of file diff --git a/JPratt/src/main/java/bjc/pratt/commands/impls/PostfixCommand.java b/JPratt/src/main/java/bjc/pratt/commands/impls/PostfixCommand.java index da587c1..ff370d0 100644 --- a/JPratt/src/main/java/bjc/pratt/commands/impls/PostfixCommand.java +++ b/JPratt/src/main/java/bjc/pratt/commands/impls/PostfixCommand.java @@ -2,6 +2,7 @@ package bjc.pratt.commands.impls; import bjc.pratt.ParserContext; import bjc.pratt.commands.BinaryPostCommand; +import bjc.pratt.commands.CommandResult; import bjc.pratt.tokens.Token; import bjc.data.Tree; import bjc.data.SimpleTree; @@ -33,8 +34,8 @@ public class PostfixCommand<K, V, C> extends BinaryPostCommand<K, V, C> { } @Override - public Tree<Token<K, V>> denote(final Tree<Token<K, V>> operand, final Token<K, V> operator, + public CommandResult<K, V> denote(final Tree<Token<K, V>> operand, final Token<K, V> operator, final ParserContext<K, V, C> ctx) throws ParserException { - return new SimpleTree<>(operator, operand); + return CommandResult.success(new SimpleTree<>(operator, operand)); } }
\ No newline at end of file diff --git a/JPratt/src/main/java/bjc/pratt/commands/impls/PreTernaryCommand.java b/JPratt/src/main/java/bjc/pratt/commands/impls/PreTernaryCommand.java index e315804..5d5cbe1 100644 --- a/JPratt/src/main/java/bjc/pratt/commands/impls/PreTernaryCommand.java +++ b/JPratt/src/main/java/bjc/pratt/commands/impls/PreTernaryCommand.java @@ -3,6 +3,8 @@ package bjc.pratt.commands.impls; import bjc.pratt.ParserContext; import bjc.pratt.blocks.ParseBlock; import bjc.pratt.commands.AbstractInitialCommand; +import bjc.pratt.commands.CommandResult; +import bjc.pratt.commands.CommandResult.Status; import bjc.pratt.tokens.Token; import bjc.data.Tree; import bjc.data.SimpleTree; @@ -63,14 +65,19 @@ public class PreTernaryCommand<K, V, C> extends AbstractInitialCommand<K, V, C> } @Override - protected Tree<Token<K, V>> intNullDenotation(final Token<K, V> operator, final ParserContext<K, V, C> ctx) + protected CommandResult<K, V> intNullDenotation(final Token<K, V> operator, final ParserContext<K, V, C> ctx) throws ParserException { - final Tree<Token<K, V>> cond = condBlock.parse(ctx); + final CommandResult<K,V> condRes = condBlock.parse(ctx); + if (condRes.status != Status.SUCCESS) return condRes; + Tree<Token<K, V>> cond = condRes.success(); + + final CommandResult<K,V> op1Res = opblock1.parse(ctx); + if (op1Res.status != Status.SUCCESS) return op1Res; + Tree<Token<K, V>> op1 = op1Res.success(); - final Tree<Token<K, V>> op1 = opblock1.parse(ctx); - - final Tree<Token<K, V>> op2 = opblock2.parse(ctx); - - return new SimpleTree<>(trm, cond, op1, op2); + final CommandResult<K,V> op2Res = opblock2.parse(ctx); + if (op2Res.status != Status.SUCCESS) return op2Res; + Tree<Token<K, V>> op2 = op2Res.success(); + return CommandResult.success(new SimpleTree<>(trm, cond, op1, op2)); } }
\ No newline at end of file diff --git a/JPratt/src/main/java/bjc/pratt/commands/impls/TernaryCommand.java b/JPratt/src/main/java/bjc/pratt/commands/impls/TernaryCommand.java index 174f6fb..786dfec 100644 --- a/JPratt/src/main/java/bjc/pratt/commands/impls/TernaryCommand.java +++ b/JPratt/src/main/java/bjc/pratt/commands/impls/TernaryCommand.java @@ -3,6 +3,8 @@ package bjc.pratt.commands.impls; import bjc.pratt.ParserContext; import bjc.pratt.blocks.ParseBlock; import bjc.pratt.commands.BinaryPostCommand; +import bjc.pratt.commands.CommandResult; +import bjc.pratt.commands.CommandResult.Status; import bjc.pratt.tokens.Token; import bjc.data.Tree; import bjc.data.SimpleTree; @@ -13,14 +15,11 @@ import bjc.utils.parserutils.ParserException; * * @author bjculkin * - * @param <K> - * The key type of the tokens. + * @param <K> The key type of the tokens. * - * @param <V> - * The value type of the tokens. + * @param <V> The value type of the tokens. * - * @param <C> - * The state type of the parser. + * @param <C> The state type of the parser. */ public class TernaryCommand<K, V, C> extends BinaryPostCommand<K, V, C> { private final ParseBlock<K, V, C> innerBlck; @@ -32,25 +31,22 @@ public class TernaryCommand<K, V, C> extends BinaryPostCommand<K, V, C> { /** * Create a new ternary command. * - * @param precedence - * The precedence of this operator. + * @param precedence The precedence of this operator. * - * @param innerBlock - * The representation of the inner block of the expression. + * @param innerBlock The representation of the inner block of the expression. * - * @param marker - * The token to use as the root of the AST node. + * @param marker The token to use as the root of the AST node. * - * @param isNonassoc - * Whether or not the conditional is associative. + * @param isNonassoc Whether or not the conditional is associative. */ public TernaryCommand(final int precedence, final ParseBlock<K, V, C> innerBlock, final Token<K, V> marker, final boolean isNonassoc) { super(precedence); - if(innerBlock == null) + if (innerBlock == null) throw new NullPointerException("Inner block must not be null"); - else if(marker == null) throw new NullPointerException("Marker must not be null"); + else if (marker == null) + throw new NullPointerException("Marker must not be null"); innerBlck = innerBlock; mark = marker; @@ -58,19 +54,22 @@ public class TernaryCommand<K, V, C> extends BinaryPostCommand<K, V, C> { } @Override - public Tree<Token<K, V>> denote(final Tree<Token<K, V>> operand, final Token<K, V> operator, + public CommandResult<K, V> denote(final Tree<Token<K, V>> operand, final Token<K, V> operator, final ParserContext<K, V, C> ctx) throws ParserException { - final Tree<Token<K, V>> inner = innerBlck.parse(ctx); - - final Tree<Token<K, V>> outer = ctx.parse.parseExpression(1 + leftBinding(), ctx.tokens, ctx.state, - false); - - return new SimpleTree<>(mark, inner, operand, outer); + final CommandResult<K, V> innerRes = innerBlck.parse(ctx); + if (innerRes.status != Status.SUCCESS) return innerRes; + Tree<Token<K, V>> inner = innerRes.success(); + + final CommandResult<K,V> outerRes = ctx.parse.parseExpression(1 + leftBinding(), ctx.tokens, ctx.state, false); + if (outerRes.status != Status.SUCCESS) return outerRes; + Tree<Token<K, V>> outer = outerRes.success(); + return CommandResult.success(new SimpleTree<>(mark, inner, operand, outer)); } @Override public int nextBinding() { - if(nonassoc) return leftBinding() - 1; + if (nonassoc) + return leftBinding() - 1; return leftBinding(); } diff --git a/JPratt/src/main/java/bjc/pratt/commands/impls/TransformingInitialCommand.java b/JPratt/src/main/java/bjc/pratt/commands/impls/TransformingInitialCommand.java index d39ec90..36f881d 100644 --- a/JPratt/src/main/java/bjc/pratt/commands/impls/TransformingInitialCommand.java +++ b/JPratt/src/main/java/bjc/pratt/commands/impls/TransformingInitialCommand.java @@ -4,6 +4,8 @@ import java.util.function.UnaryOperator; import bjc.pratt.ParserContext; import bjc.pratt.commands.AbstractInitialCommand; +import bjc.pratt.commands.CommandResult; +import bjc.pratt.commands.CommandResult.Status; import bjc.pratt.commands.InitialCommand; import bjc.pratt.tokens.Token; import bjc.data.Tree; @@ -45,9 +47,12 @@ public class TransformingInitialCommand<K, V, C> extends AbstractInitialCommand< } @Override - protected Tree<Token<K, V>> intNullDenotation(final Token<K, V> operator, final ParserContext<K, V, C> ctx) + protected CommandResult<K, V> intNullDenotation(final Token<K, V> operator, final ParserContext<K, V, C> ctx) throws ParserException { - return transfrm.apply(internl.denote(operator, ctx)); + CommandResult<K,V> result = internl.denote(operator, ctx); + if (result.status != Status.SUCCESS) return result; + + return CommandResult.success(transfrm.apply(result.success())); } } diff --git a/JPratt/src/main/java/bjc/pratt/commands/impls/UnaryCommand.java b/JPratt/src/main/java/bjc/pratt/commands/impls/UnaryCommand.java index 2e7365b..657c004 100644 --- a/JPratt/src/main/java/bjc/pratt/commands/impls/UnaryCommand.java +++ b/JPratt/src/main/java/bjc/pratt/commands/impls/UnaryCommand.java @@ -2,6 +2,8 @@ package bjc.pratt.commands.impls; import bjc.pratt.ParserContext; import bjc.pratt.commands.AbstractInitialCommand; +import bjc.pratt.commands.CommandResult; +import bjc.pratt.commands.CommandResult.Status; import bjc.pratt.tokens.Token; import bjc.data.Tree; import bjc.data.SimpleTree; @@ -37,10 +39,12 @@ public class UnaryCommand<K, V, C> extends AbstractInitialCommand<K, V, C> { } @Override - protected Tree<Token<K, V>> intNullDenotation(final Token<K, V> operator, final ParserContext<K, V, C> ctx) + protected CommandResult<K, V> intNullDenotation(final Token<K, V> operator, final ParserContext<K, V, C> ctx) throws ParserException { - final Tree<Token<K, V>> opr = ctx.parse.parseExpression(nullPwer, ctx.tokens, ctx.state, false); + CommandResult<K,V> result = ctx.parse.parseExpression(nullPwer, ctx.tokens, ctx.state, false); + if (result.status != Status.SUCCESS) return result; + final Tree<Token<K, V>> opr = result.success(); - return new SimpleTree<>(operator, opr); + return CommandResult.success(new SimpleTree<>(operator, opr)); } }
\ No newline at end of file diff --git a/JPratt/src/main/java/bjc/pratt/tokens/ExpectionNotMet.java b/JPratt/src/main/java/bjc/pratt/tokens/ExpectionNotMet.java new file mode 100644 index 0000000..2bd45e2 --- /dev/null +++ b/JPratt/src/main/java/bjc/pratt/tokens/ExpectionNotMet.java @@ -0,0 +1,23 @@ +package bjc.pratt.tokens; + +import bjc.utils.parserutils.ParserException; + +/** + * The exception thrown when an expectation fails. + * + * @author EVE + * + */ +public class ExpectionNotMet extends ParserException { + private static final long serialVersionUID = 4299299480127680805L; + + /** + * Create a new exception with the specified message. + * + * @param msg + * The message of the exception. + */ + public ExpectionNotMet(final String msg) { + super(msg); + } +}
\ No newline at end of file diff --git a/JPratt/src/main/java/bjc/pratt/tokens/StringTokenStream.java b/JPratt/src/main/java/bjc/pratt/tokens/StringTokenStream.java index 044d19a..8c7fcec 100644 --- a/JPratt/src/main/java/bjc/pratt/tokens/StringTokenStream.java +++ b/JPratt/src/main/java/bjc/pratt/tokens/StringTokenStream.java @@ -4,6 +4,8 @@ import static bjc.pratt.tokens.StringToken.litToken; import java.util.Iterator; +import bjc.data.MarkListIterator; + /** * Simple implementation of token stream for strings. * @@ -14,8 +16,8 @@ import java.util.Iterator; * */ public class StringTokenStream extends TokenStream<String, String> { - private final Iterator<Token<String, String>> iter; - + private final MarkListIterator<Token<String, String>> iter; + private Token<String, String> curr; /** @@ -26,8 +28,7 @@ public class StringTokenStream extends TokenStream<String, String> { * */ public StringTokenStream(final Iterator<Token<String, String>> itr) { - iter = itr; - + iter = new MarkListIterator<>(itr); } @Override @@ -50,4 +51,29 @@ public class StringTokenStream extends TokenStream<String, String> { public boolean hasNext() { return iter.hasNext(); } + + @Override + public void mark() { + iter.mark(); + } + + @Override + public void commit() { + iter.commit(); + + if (!iter.hasMark()) { + // No marks outstanding; we can release the previous state + iter.reset(); + } + } + + @Override + public void rollback() { + iter.rollback(); + } + + @Override + public boolean hasMark() { + return iter.hasMark(); + } } diff --git a/JPratt/src/main/java/bjc/pratt/tokens/TokenStream.java b/JPratt/src/main/java/bjc/pratt/tokens/TokenStream.java index 1c550de..dc09bcc 100644 --- a/JPratt/src/main/java/bjc/pratt/tokens/TokenStream.java +++ b/JPratt/src/main/java/bjc/pratt/tokens/TokenStream.java @@ -6,7 +6,6 @@ import java.util.Iterator; import java.util.Set; import bjc.utils.funcutils.StringUtils; -import bjc.utils.parserutils.ParserException; /** * A stream of tokens. @@ -21,26 +20,6 @@ import bjc.utils.parserutils.ParserException; */ public abstract class TokenStream<K, V> implements Iterator<Token<K, V>> { /** - * The exception thrown when an expectation fails. - * - * @author EVE - * - */ - public static class ExpectationException extends ParserException { - private static final long serialVersionUID = 4299299480127680805L; - - /** - * Create a new exception with the specified message. - * - * @param msg - * The message of the exception. - */ - public ExpectationException(final String msg) { - super(msg); - } - } - - /** * Get the current token. * * @return The current token. @@ -54,22 +33,44 @@ public abstract class TokenStream<K, V> implements Iterator<Token<K, V>> { public abstract boolean hasNext(); /** + * Place a mark in the current stream, which can be either returned to or abandoned later on. + */ + public abstract void mark(); + + /** + * Reset the stream to the state it was in when the last mark was taken. + */ + public abstract void rollback(); + + /** + * Check if the stream has at least one mark. + * + * @return Whether or not at least one mark exists. + */ + public abstract boolean hasMark(); + + /** + * Remove the last mark placed into the stream. This prevents returning to it later on. + */ + public abstract void commit(); + + /** * Utility method for checking that the next token is one of a specific * set of types, and then consuming it. * * @param expectedKeys * The expected values * - * @throws ExpectationException + * @throws ExpectionNotMet * If the token is not one of the expected types. */ - public void expect(final Set<K> expectedKeys) throws ExpectationException { + public void expect(final Set<K> expectedKeys) throws ExpectionNotMet { final K curKey = current().getKey(); if(!expectedKeys.contains(curKey)) { final String expectedList = StringUtils.toEnglishList(expectedKeys.toArray(), false); - throw new ExpectationException("One of '" + expectedList + "' was expected, not " + curKey); + throw new ExpectionNotMet("One of '" + expectedList + "' was expected, not " + curKey); } next(); @@ -82,12 +83,13 @@ public abstract class TokenStream<K, V> implements Iterator<Token<K, V>> { * @param expectedKeys * The expected values * - * @throws ExpectationException + * @throws ExpectionNotMet * If the token is not one of the expected types. */ @SafeVarargs - public final void expect(final K... expectedKeys) throws ExpectationException { - expect(new HashSet<>(Arrays.asList(expectedKeys))); + public final void expect(final K... expectedKeys) throws ExpectionNotMet { + HashSet<K> keys = new HashSet<>(Arrays.asList(expectedKeys)); + expect(keys); } /** |
