diff options
Diffstat (limited to 'JPratt/src/main/java/bjc/pratt/PrattParser.java')
| -rw-r--r-- | JPratt/src/main/java/bjc/pratt/PrattParser.java | 267 |
1 files changed, 167 insertions, 100 deletions
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 |
