ParserTable.java

package org.sterling.source.parser;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import org.sterling.SterlingException;
import org.sterling.source.syntax.NodeKind;
import org.sterling.source.syntax.SourceNode;

public class ParserTable {

    private static final ParserAction ERROR = new ParserAction() {
        @Override
        public void invoke(TableDrivenParser parser) throws SterlingException {
            parser.error();
        }
    };

    private static Map<NodeKind, List<NodeKind>> initLookAheadMap(Collection<ParserRule> rules) {
        Map<NodeKind, List<NodeKind>> lookAheadMap = new ConcurrentHashMap<>();
        for (ParserRule rule : rules) {
            ParserState state = rule.getState();
            if (!lookAheadMap.containsKey(state.getStack())) {
                lookAheadMap.put(state.getStack(), new CopyOnWriteArrayList<NodeKind>());
            }
            lookAheadMap.get(state.getStack()).add(state.getLookAhead());
        }
        return lookAheadMap;
    }

    private static Map<ParserState, ParserAction> initTable(Collection<ParserRule> rules) {
        Map<ParserState, ParserAction> stateActionMap = new ConcurrentHashMap<>();
        for (ParserRule rule : rules) {
            if (stateActionMap.containsKey(rule.getState())) {
                throw new IllegalArgumentException("Duplicate rule for parser state " + rule.getState());
            }
            stateActionMap.put(rule.getState(), rule.getAction());
        }
        return stateActionMap;
    }

    private final Map<ParserState, ParserAction> table;
    private final Map<NodeKind, List<NodeKind>> lookAheadMap;
    private final ParserAction errorAction;

    public ParserTable(Collection<ParserRule> rules) {
        this.lookAheadMap = initLookAheadMap(rules);
        this.table = initTable(rules);
        this.errorAction = ERROR;
    }

    public ParserAction getAction(ParserState state) {
        if (table.containsKey(state)) {
            return table.get(state);
        } else {
            return errorAction;
        }
    }

    public ParserAction getErrorAction() {
        return errorAction;
    }

    public List<NodeKind> getLookAhead(SourceNode node) {
        NodeKind stack = node.getKind();
        if (hasLookAhead(stack)) {
            return new ArrayList<>(lookAheadMap.get(stack));
        } else {
            throw new IllegalStateException("No look-ahead tokens registered to " + stack);
        }
    }

    private boolean hasLookAhead(NodeKind stack) {
        return lookAheadMap.containsKey(stack);
    }
}