summaryrefslogtreecommitdiff
path: root/JPratt/src/main
diff options
context:
space:
mode:
authorbjculkin <bjculkin@WIT-136XG42.wvu-ad.wvu.edu>2017-04-12 10:45:46 -0400
committerbjculkin <bjculkin@WIT-136XG42.wvu-ad.wvu.edu>2017-04-12 10:45:46 -0400
commit694bed833470393ee00eae0a85bff0c6c90e692a (patch)
tree345ddcc9c03c5850ca74051d3c0d49420f508d3d /JPratt/src/main
parent6b881e8833596d669fdee9525e064aea0c8946dc (diff)
Add support for meta-commands
Meta-commands allow you to implement meta-operators, whose behavior changes based off of tokens that follow.
Diffstat (limited to 'JPratt/src/main')
-rw-r--r--JPratt/src/main/java/bjc/pratt/MetaInitialCommand.java11
-rw-r--r--JPratt/src/main/java/bjc/pratt/MetaNonInitialCommand.java19
-rw-r--r--JPratt/src/main/java/bjc/pratt/ParserContext.java19
-rw-r--r--JPratt/src/main/java/bjc/pratt/PrattParser.java119
4 files changed, 134 insertions, 34 deletions
diff --git a/JPratt/src/main/java/bjc/pratt/MetaInitialCommand.java b/JPratt/src/main/java/bjc/pratt/MetaInitialCommand.java
new file mode 100644
index 0000000..067d084
--- /dev/null
+++ b/JPratt/src/main/java/bjc/pratt/MetaInitialCommand.java
@@ -0,0 +1,11 @@
+package bjc.pratt;
+
+/**
+ * A 'meta-command' that yields the actual command to use.
+ *
+ * @author bjculkin
+ *
+ */
+public interface MetaInitialCommand<K, V, C> {
+ InitialCommand<K, V, C> getCommand(ParserContext<K, V, C> ctx);
+}
diff --git a/JPratt/src/main/java/bjc/pratt/MetaNonInitialCommand.java b/JPratt/src/main/java/bjc/pratt/MetaNonInitialCommand.java
new file mode 100644
index 0000000..444c0e4
--- /dev/null
+++ b/JPratt/src/main/java/bjc/pratt/MetaNonInitialCommand.java
@@ -0,0 +1,19 @@
+package bjc.pratt;
+
+/**
+ * A 'meta-command' for non-initial commands.
+ *
+ * @author bjculkin
+ *
+ * @param <K>
+ * The token key type.
+ *
+ * @param <V>
+ * The token value type.
+ *
+ * @param <C>
+ * The parser state type.
+ */
+public interface MetaNonInitialCommand<K, V, C> {
+ NonInitialCommand<K, V, C> getCommand(ParserContext<K, V, C> ctx);
+}
diff --git a/JPratt/src/main/java/bjc/pratt/ParserContext.java b/JPratt/src/main/java/bjc/pratt/ParserContext.java
index 162c5cf..9007f79 100644
--- a/JPratt/src/main/java/bjc/pratt/ParserContext.java
+++ b/JPratt/src/main/java/bjc/pratt/ParserContext.java
@@ -17,15 +17,22 @@ public class ParserContext<K, V, C> {
/**
* The source of tokens.
*/
- public TokenStream<K, V> tokens;
+ public TokenStream<K, V> tokens;
+
/**
* The parser for sub-expressions.
*/
- public PrattParser<K, V, C> parse;
+ public PrattParser<K, V, C> parse;
+
/**
* The state of the parser.
*/
- public C state;
+ public C state;
+
+ /**
+ * The initial command for the current expression.
+ */
+ public K initial;
/**
* Create a new parser context.
@@ -40,8 +47,8 @@ public class ParserContext<K, V, C> {
* Any state needing to be kept during parsing.
*/
public ParserContext(final TokenStream<K, V> tokns, final PrattParser<K, V, C> prse, final C stte) {
- this.tokens = tokns;
- this.parse = prse;
- this.state = stte;
+ tokens = tokns;
+ parse = prse;
+ state = stte;
}
} \ No newline at end of file
diff --git a/JPratt/src/main/java/bjc/pratt/PrattParser.java b/JPratt/src/main/java/bjc/pratt/PrattParser.java
index b3ce4a7..e2654f7 100644
--- a/JPratt/src/main/java/bjc/pratt/PrattParser.java
+++ b/JPratt/src/main/java/bjc/pratt/PrattParser.java
@@ -35,20 +35,25 @@ public class PrattParser<K, V, C> {
/*
* 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, NonInitialCommand<K, V, C>>> dependantLeftCommands;
+ private final Map<K, Map<K, MetaNonInitialCommand<K, V, C>>> dependantMetaLeftCommands;
/*
* The left commands.
*/
- private final Map<K, NonInitialCommand<K, V, C>> leftCommands;
+ private final Map<K, NonInitialCommand<K, V, C>> leftCommands;
+ private final Map<K, MetaNonInitialCommand<K, V, C>> metaLeftCommands;
+
/*
* The initial commands.
*/
- private final Map<K, InitialCommand<K, V, C>> nullCommands;
+ private final Map<K, InitialCommand<K, V, C>> nullCommands;
+ private final Map<K, MetaInitialCommand<K, V, C>> metaNullCommands;
+
/*
* Initial commands only checked for statements.
*/
- private final Map<K, InitialCommand<K, V, C>> statementCommands;
+ private final Map<K, InitialCommand<K, V, C>> statementCommands;
+ private final Map<K, MetaInitialCommand<K, V, C>> metaStatementCommands;
/**
* Create a new Pratt parser.
@@ -56,10 +61,16 @@ public class PrattParser<K, V, C> {
*/
public PrattParser() {
dependantLeftCommands = new HashMap<>();
+ dependantMetaLeftCommands = new HashMap<>();
leftCommands = new HashMap<>();
+ metaLeftCommands = new HashMap<>();
+
nullCommands = new HashMap<>();
+ metaNullCommands = new HashMap<>();
+
statementCommands = new HashMap<>();
+ metaStatementCommands = new HashMap<>();
}
/**
@@ -84,22 +95,20 @@ public class PrattParser<K, V, C> {
*/
public ITree<Token<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);
final Token<K, V> initToken = tokens.current();
tokens.next();
final K initKey = initToken.getKey();
- ITree<Token<K, V>> ast;
+ InitialCommand<K, V, C> nullCommand = getInitialCommand(isStatement, initKey, parserContext);
+ ITree<Token<K, V>> ast = nullCommand.denote(initToken, parserContext);
- if (isStatement && statementCommands.containsKey(initKey)) {
- ast = statementCommands.getOrDefault(initKey, DEFAULT_NULL_COMMAND).denote(initToken,
- new ParserContext<>(tokens, this, state));
- } else {
- ast = nullCommands.getOrDefault(initKey, DEFAULT_NULL_COMMAND).denote(initToken,
- new ParserContext<>(tokens, this, state));
- }
+ parserContext.initial = initKey;
int rightPrec = Integer.MAX_VALUE;
@@ -108,19 +117,15 @@ public class PrattParser<K, V, C> {
final K key = tok.getKey();
- NonInitialCommand<K, V, C> command = leftCommands.getOrDefault(key, DEFAULT_LEFT_COMMAND);
+ NonInitialCommand<K, V, C> leftCommand = getNonInitialCommand(key, parserContext);
- if (dependantLeftCommands.containsKey(initKey)) {
- command = dependantLeftCommands.get(initKey).getOrDefault(key, command);
- }
-
- final int leftBind = command.leftBinding();
+ final int leftBind = leftCommand.leftBinding();
if (NumberUtils.between(precedence, rightPrec, leftBind)) {
tokens.next();
- ast = command.denote(ast, tok, new ParserContext<>(tokens, this, state));
- rightPrec = command.nextBinding();
+ ast = leftCommand.denote(ast, tok, parserContext);
+ rightPrec = leftCommand.nextBinding();
} else {
break;
}
@@ -184,14 +189,72 @@ public class PrattParser<K, V, C> {
* The command.
*/
public void addDependantCommand(final K dependant, final K marker, final NonInitialCommand<K, V, C> comm) {
- if (dependantLeftCommands.containsKey(dependant)) {
- dependantLeftCommands.get(dependant).put(marker, comm);
- } else {
- final Map<K, NonInitialCommand<K, V, C>> comms = new HashMap<>();
+ Map<K, NonInitialCommand<K, V, C>> dependantMap = dependantLeftCommands.getOrDefault(dependant,
+ new HashMap<>());
+
+ dependantMap.put(marker, comm);
+ }
+
+ /**
+ * Lookup an initial command.
+ *
+ * @param isStatement
+ * Whether to look for statement commands or not.
+ *
+ * @param key
+ * The key of the command.
+ *
+ * @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);
+ }
- comms.put(marker, comm);
+ if (metaNullCommands.containsKey(key))
+ return metaNullCommands.get(key).getCommand(ctx);
+ else
+ return nullCommands.getOrDefault(key, DEFAULT_NULL_COMMAND);
+ }
+
+ /**
+ * Lookup a non-initial command.
+ *
+ * @param key
+ * The key of the command.
+ *
+ * @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);
- dependantLeftCommands.put(dependant, comms);
+ if (dependantCommands.containsKey(key)) {
+ return dependantCommands.get(key).getCommand(ctx);
+ }
+ }
+
+ if (dependantLeftCommands.containsKey(ctx.initial)) {
+ Map<K, NonInitialCommand<K, V, C>> dependantCommands = dependantLeftCommands.get(ctx.initial);
+
+ if (dependantCommands.containsKey(key)) {
+ return dependantCommands.getOrDefault(key, DEFAULT_LEFT_COMMAND);
+ }
}
+
+ if (metaLeftCommands.containsKey(key)) {
+ return metaLeftCommands.get(key).getCommand(ctx);
+ } else
+ return leftCommands.getOrDefault(key, DEFAULT_LEFT_COMMAND);
}
} \ No newline at end of file