summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Culkin <scorpress@gmail.com>2022-08-16 23:03:27 -0400
committerBen Culkin <scorpress@gmail.com>2022-08-16 23:03:27 -0400
commitcea3e47938322b97c318dea38dc0d649e196dc1b (patch)
tree0ceef0bafbfed7aa5802e8fc526c0c98276f1fff
parent4869146748ed51eb212935d2b971388fb9e73d37 (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
-rw-r--r--JPratt/src/examples/java/bjc/pratt/examples/lang/AssignCommand.java12
-rw-r--r--JPratt/src/examples/java/bjc/pratt/examples/lang/PrattParserTest.java42
-rw-r--r--JPratt/src/examples/java/bjc/pratt/examples/lang/SwitchCommand.java18
-rw-r--r--JPratt/src/examples/java/bjc/pratt/examples/lang/VarCommand.java16
-rw-r--r--JPratt/src/main/java/bjc/pratt/PrattParser.java267
-rw-r--r--JPratt/src/main/java/bjc/pratt/blocks/ChainParseBlock.java19
-rw-r--r--JPratt/src/main/java/bjc/pratt/blocks/GrammarParseBlock.java53
-rw-r--r--JPratt/src/main/java/bjc/pratt/blocks/ParseBlock.java4
-rw-r--r--JPratt/src/main/java/bjc/pratt/blocks/RepeatingParseBlock.java10
-rw-r--r--JPratt/src/main/java/bjc/pratt/blocks/SimpleParseBlock.java13
-rw-r--r--JPratt/src/main/java/bjc/pratt/blocks/TriggeredParseBlock.java8
-rw-r--r--JPratt/src/main/java/bjc/pratt/commands/AbstractInitialCommand.java4
-rw-r--r--JPratt/src/main/java/bjc/pratt/commands/BinaryCommand.java9
-rw-r--r--JPratt/src/main/java/bjc/pratt/commands/CommandResult.java97
-rw-r--r--JPratt/src/main/java/bjc/pratt/commands/InitialCommand.java4
-rw-r--r--JPratt/src/main/java/bjc/pratt/commands/NonInitialCommand.java4
-rw-r--r--JPratt/src/main/java/bjc/pratt/commands/impls/BlockInitialCommand.java3
-rw-r--r--JPratt/src/main/java/bjc/pratt/commands/impls/ChainCommand.java17
-rw-r--r--JPratt/src/main/java/bjc/pratt/commands/impls/ConstantCommand.java5
-rw-r--r--JPratt/src/main/java/bjc/pratt/commands/impls/DefaultInitialCommand.java3
-rw-r--r--JPratt/src/main/java/bjc/pratt/commands/impls/DefaultNonInitialCommand.java3
-rw-r--r--JPratt/src/main/java/bjc/pratt/commands/impls/DenestingCommand.java8
-rw-r--r--JPratt/src/main/java/bjc/pratt/commands/impls/GroupingCommand.java9
-rw-r--r--JPratt/src/main/java/bjc/pratt/commands/impls/InitialCommands.java13
-rw-r--r--JPratt/src/main/java/bjc/pratt/commands/impls/LeafCommand.java5
-rw-r--r--JPratt/src/main/java/bjc/pratt/commands/impls/PanfixCommand.java39
-rw-r--r--JPratt/src/main/java/bjc/pratt/commands/impls/PostCircumfixCommand.java11
-rw-r--r--JPratt/src/main/java/bjc/pratt/commands/impls/PostfixCommand.java5
-rw-r--r--JPratt/src/main/java/bjc/pratt/commands/impls/PreTernaryCommand.java21
-rw-r--r--JPratt/src/main/java/bjc/pratt/commands/impls/TernaryCommand.java47
-rw-r--r--JPratt/src/main/java/bjc/pratt/commands/impls/TransformingInitialCommand.java9
-rw-r--r--JPratt/src/main/java/bjc/pratt/commands/impls/UnaryCommand.java10
-rw-r--r--JPratt/src/main/java/bjc/pratt/tokens/ExpectionNotMet.java23
-rw-r--r--JPratt/src/main/java/bjc/pratt/tokens/StringTokenStream.java34
-rw-r--r--JPratt/src/main/java/bjc/pratt/tokens/TokenStream.java56
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);
}
/**