diff options
| author | bjculkin <bjculkin@WIT-136XG42.wvu-ad.wvu.edu> | 2017-04-12 10:45:46 -0400 |
|---|---|---|
| committer | bjculkin <bjculkin@WIT-136XG42.wvu-ad.wvu.edu> | 2017-04-12 10:45:46 -0400 |
| commit | 694bed833470393ee00eae0a85bff0c6c90e692a (patch) | |
| tree | 345ddcc9c03c5850ca74051d3c0d49420f508d3d /JPratt | |
| parent | 6b881e8833596d669fdee9525e064aea0c8946dc (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')
| -rw-r--r-- | JPratt/src/main/java/bjc/pratt/MetaInitialCommand.java | 11 | ||||
| -rw-r--r-- | JPratt/src/main/java/bjc/pratt/MetaNonInitialCommand.java | 19 | ||||
| -rw-r--r-- | JPratt/src/main/java/bjc/pratt/ParserContext.java | 19 | ||||
| -rw-r--r-- | JPratt/src/main/java/bjc/pratt/PrattParser.java | 119 |
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 |
