TableDrivenParser.java
package org.sterling.source.parser;
import static java.util.Arrays.asList;
import static org.sterling.source.parser.ParserState.state;
import java.util.*;
import org.sterling.SterlingException;
import org.sterling.source.exception.ParserException;
import org.sterling.source.exception.ScannerException;
import org.sterling.source.scanner.Scanner;
import org.sterling.source.syntax.NodeKind;
import org.sterling.source.syntax.SourceNode;
public class TableDrivenParser {
private static final ParserAction MATCH = new ParserAction() {
@Override
public void invoke(TableDrivenParser parser) throws SterlingException {
parser.match();
}
};
private final Scanner scanner;
private final ParserTable table;
private final Deque<SourceNode> stack;
private final TryManager tries;
public TableDrivenParser(NodeKind firstKind, ParserTable table, Scanner scanner) {
this.table = table;
this.scanner = scanner;
this.stack = new ArrayDeque<>();
this.tries = new TryManager();
this.stack.push(firstKind.createNode());
}
public void error() throws SterlingException {
if (!tries.handleError()) {
try {
scanner.requireOne(getPossibleLookAhead());
} catch (ScannerException exception) {
throw new ParserException("Parse error in " + peek().getKind(), exception);
}
}
}
public ParserAction getAction() {
ParserAction action = table.getErrorAction();
if (peek().isTerminal()) {
if (scanner.expect(peek().getKind())) {
action = MATCH;
}
} else {
action = table.getAction(getState());
}
return action;
}
public NodeKind getLookAhead() {
return scanner.expected(getPossibleLookAhead());
}
public ParserState getState() {
return state(peek().getKind(), getLookAhead());
}
public void match() throws SterlingException {
peek().setToken(scanner.require(getState().getStack()));
output();
}
public SourceNode output() {
return stack.pop();
}
public SourceNode parse() throws SterlingException {
SourceNode root = stack.peek();
while (stack.size() > 0) {
getAction().invoke(this);
}
root.prune();
return root;
}
public SourceNode peek() {
return stack.peek();
}
public void push(SourceNode node) {
stack.push(node);
tries.handlePush(node);
}
private void backtrack(SourceNode parent) {
while (peek().childOf(parent)) {
output();
}
parent.clearChildren();
scanner.restore();
}
private void begin() {
scanner.begin();
}
private void beginTry(List<Derive> derivations) {
tries.begin(this, output(), derivations);
}
private void end() {
scanner.end();
}
private List<NodeKind> getPossibleLookAhead() {
if (peek().isTerminal()) {
return asList(peek().getKind());
} else {
return table.getLookAhead(peek());
}
}
public static class Derive implements ParserAction {
private final List<NodeKind> kinds;
public Derive(NodeKind... kinds) {
this.kinds = asList(kinds);
}
public void derive(TableDrivenParser parser, SourceNode parent) {
Deque<SourceNode> children = new ArrayDeque<>();
for (NodeKind kind : kinds) {
SourceNode child = kind.createNode();
parent.addChild(child);
children.push(child);
}
while (children.size() > 0) {
parser.push(children.pop());
}
}
@Override
public void invoke(TableDrivenParser parser) throws ParserException {
derive(parser, parser.output());
}
}
public static class TryDerive implements ParserAction {
private final List<Derive> derivations;
public TryDerive(NodeKind... stacks) {
derivations = new ArrayList<>(stacks.length);
for (NodeKind stack : stacks) {
derivations.add(new Derive(stack));
}
}
@Override
public void invoke(TableDrivenParser parser) throws ParserException {
parser.beginTry(derivations);
}
}
private static final class TryManager {
private final Deque<TryState> tries;
public TryManager() {
tries = new ArrayDeque<>();
}
public void begin(TableDrivenParser parser, SourceNode parent, List<Derive> derivations) {
push(new TryState(parser, parent, derivations));
currentTry().begin();
}
public boolean handleError() {
return hasTries() && currentTry().handleError();
}
public void handlePush(SourceNode node) {
if (hasTries() && currentTry().handlePush(node)) {
tries.pop();
}
}
public void push(TryState state) {
tries.push(state);
}
private TryState currentTry() {
return tries.peek();
}
private boolean hasTries() {
return tries.size() > 0;
}
}
private static final class TryState {
private final TableDrivenParser parser;
private final SourceNode parent;
private final Iterator<Derive> derivations;
public TryState(TableDrivenParser parser, SourceNode parent, List<Derive> derivations) {
this.parser = parser;
this.parent = parent;
this.derivations = derivations.iterator();
}
public void begin() {
parser.begin();
derivations.next().derive(parser, parent);
}
public boolean handleError() {
if (derivations.hasNext()) {
parser.backtrack(parent);
begin();
return true;
} else {
return false;
}
}
public boolean handlePush(SourceNode node) {
if (node.childOf(parent)) {
return false;
} else {
parser.end();
return true;
}
}
}
}