From 5008c26a604876bcad09c868aa2ec4a2c8b64e35 Mon Sep 17 00:00:00 2001 From: bjculkin Date: Fri, 31 Mar 2017 08:54:20 -0400 Subject: Move Pratt Parser to new project --- .../bjc/utils/parserutils/pratt/PrattParser.java | 190 +++++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 JPratt/src/main/java/bjc/utils/parserutils/pratt/PrattParser.java (limited to 'JPratt/src/main/java/bjc/utils/parserutils/pratt/PrattParser.java') diff --git a/JPratt/src/main/java/bjc/utils/parserutils/pratt/PrattParser.java b/JPratt/src/main/java/bjc/utils/parserutils/pratt/PrattParser.java new file mode 100644 index 0000000..c53b4e1 --- /dev/null +++ b/JPratt/src/main/java/bjc/utils/parserutils/pratt/PrattParser.java @@ -0,0 +1,190 @@ +package bjc.utils.parserutils.pratt; + +import bjc.utils.data.ITree; +import bjc.utils.funcutils.NumberUtils; +import bjc.utils.parserutils.ParserException; +import bjc.utils.parserutils.pratt.commands.DefaultNonInitialCommand; +import bjc.utils.parserutils.pratt.commands.DefaultInitialCommand; + +import java.util.HashMap; +import java.util.Map; + +/** + * A configurable Pratt parser for expressions. + * + * @author EVE + * + * @param + * The key type for the tokens. + * + * @param + * The value type for the tokens. + * + * @param + * The state type of the parser. + * + * + */ +public class PrattParser { + /* + * Default commands that error when used. + */ + private final NonInitialCommand DEFAULT_LEFT_COMMAND = new DefaultNonInitialCommand<>(); + private final InitialCommand DEFAULT_NULL_COMMAND = new DefaultInitialCommand<>(); + + /* + * Left-commands that depend on what the null command was. + */ + private Map>> dependantLeftCommands; + + /* + * The left commands. + */ + private Map> leftCommands; + /* + * The initial commands. + */ + private Map> nullCommands; + /* + * Initial commands only checked for statements. + */ + private Map> statementCommands; + + /** + * Create a new Pratt parser. + * + */ + public PrattParser() { + dependantLeftCommands = new HashMap<>(); + + leftCommands = new HashMap<>(); + nullCommands = new HashMap<>(); + statementCommands = new HashMap<>(); + } + + /** + * Parse an expression. + * + * @param precedence + * The initial precedence for the expression. + * + * @param tokens + * The tokens for the expression. + * + * @param state + * The state of the parser. + * + * @param isStatement + * Whether or not to parse statements. + * + * @return The expression as an AST. + * + * @throws ParserException + * If something goes wrong during parsing. + */ + public ITree> parseExpression(int precedence, TokenStream tokens, C state, + boolean isStatement) throws ParserException { + if (precedence < 0) { + throw new IllegalArgumentException("Precedence must be greater than zero"); + } + + Token initToken = tokens.current(); + tokens.next(); + + K initKey = initToken.getKey(); + + ITree> ast; + + 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)); + } + + int rightPrec = Integer.MAX_VALUE; + + while (true) { + Token tok = tokens.current(); + + K key = tok.getKey(); + + NonInitialCommand command = leftCommands.getOrDefault(key, DEFAULT_LEFT_COMMAND); + + if (dependantLeftCommands.containsKey(initKey)) { + command = dependantLeftCommands.get(initKey).getOrDefault(key, command); + } + + int leftBind = command.leftBinding(); + + if (NumberUtils.between(precedence, rightPrec, leftBind)) { + tokens.next(); + + ast = command.denote(ast, tok, new ParserContext<>(tokens, this, state)); + rightPrec = command.nextBinding(); + } else { + break; + } + } + + return ast; + } + + /** + * Add a non-initial command to this parser. + * + * @param marker + * The key that marks the command. + * + * @param comm + * The command. + */ + public void addNonInitialCommand(K marker, NonInitialCommand comm) { + leftCommands.put(marker, comm); + } + + /** + * Add a initial command to this parser. + * + * @param marker + * The key that marks the command. + * + * @param comm + * The command. + */ + public void addInitialCommand(K marker, InitialCommand comm) { + nullCommands.put(marker, 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. + * + * @param marker + * The key that marks the command. + * + * @param comm + * The command. + */ + public void addStatementCommand(K marker, InitialCommand comm) { + statementCommands.put(marker, comm); + } + + /** + * Add a dependant non-initial command to this parser. + */ + public void addDependantCommand(K dependant, K marker, NonInitialCommand comm) { + if (dependantLeftCommands.containsKey(dependant)) { + dependantLeftCommands.get(dependant).put(marker, comm); + } else { + Map> comms = new HashMap<>(); + + comms.put(marker, comm); + + dependantLeftCommands.put(dependant, comms); + } + } +} \ No newline at end of file -- cgit v1.2.3