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 | |
| 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
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); } /** |
