/* * Copyright (c) 2012 Sam Harwell, Tunnel Vision Laboratories LLC * All rights reserved. * * The source code of this document is proprietary work, and is not licensed for * distribution. For information about licensing, contact Sam Harwell at: * sam@tunnelvisionlabs.com */ package org.antlr.works.editor.antlr4.completion; import java.util.ArrayList; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import org.antlr.v4.runtime.FailedPredicateException; import org.antlr.v4.runtime.Parser; import org.antlr.v4.runtime.RecognitionException; import org.antlr.v4.runtime.Recognizer; import org.antlr.v4.runtime.RuleContext; import org.antlr.v4.runtime.Token; import org.antlr.v4.runtime.atn.ATNConfig; import org.antlr.v4.runtime.atn.ATNState; import org.antlr.v4.runtime.atn.DecisionState; import org.antlr.v4.runtime.atn.PlusBlockStartState; import org.antlr.v4.runtime.atn.PlusLoopbackState; import org.antlr.v4.runtime.atn.StarLoopEntryState; import org.antlr.v4.runtime.atn.StarLoopbackState; import org.antlr.v4.runtime.misc.IntegerList; import org.antlr.v4.runtime.misc.IntervalSet; /** * * @author Sam Harwell * @param <TParser> */ public abstract class AbstractForestParser<TParser extends CodeCompletionParser> implements ForestParser<TParser> { // -J-Dorg.antlr.works.editor.antlr4.completion.AbstractForestParser.level=FINE protected static final Logger LOGGER = Logger.getLogger(AbstractForestParser.class.getName()); @Override public Map<RuleContext, CaretReachedException> getParseTrees(TParser parser) { List<MultipleDecisionData> potentialAlternatives = new ArrayList<>(); IntegerList currentPath = new IntegerList(); Map<RuleContext, CaretReachedException> results = new IdentityHashMap<>(); // make sure the token stream is initialized before getting the index parser.getInputStream().LA(1); int initialToken = parser.getInputStream().index(); while (true) { parser.getInputStream().seek(initialToken); tryParse(parser, potentialAlternatives, currentPath, results); if (!incrementCurrentPath(potentialAlternatives, currentPath)) { break; } } LOGGER.log(Level.FINE, "Forest parser constructed {0} parse trees.", results.size()); if (LOGGER.isLoggable(Level.FINEST)) { for (Map.Entry<RuleContext, CaretReachedException> entry : results.entrySet()) { LOGGER.log(Level.FINEST, entry.getKey().toStringTree(parser instanceof Parser ? (Parser)parser : null)); } } return results; } protected boolean incrementCurrentPath(List<MultipleDecisionData> potentialAlternatives, IntegerList currentPath) { for (int i = currentPath.size() - 1; i >= 0; i--) { if (currentPath.get(i) < potentialAlternatives.get(i).alternatives.length - 1) { currentPath.set(i, currentPath.get(i) + 1); return true; } potentialAlternatives.remove(i); currentPath.removeAt(i); } return false; } protected void tryParse(TParser parser, List<MultipleDecisionData> potentialAlternatives, IntegerList currentPath, Map<RuleContext, CaretReachedException> results) { RuleContext parseTree; try { parser.getInterpreter().setFixedDecisions(potentialAlternatives, currentPath); parseTree = parseImpl(parser); results.put(parseTree, null); } catch (CaretReachedException ex) { if (ex.getTransitions() == null) { return; } for (parseTree = ex.getFinalContext(); parseTree.getParent() != null; parseTree = parseTree.getParent()) { // intentionally blank } if (ex.getCause() instanceof FailedPredicateException) { return; } if (ex.getCause() != null) { IntervalSet alts = new IntervalSet(); IntervalSet semanticAlts = new IntervalSet(); for (ATNConfig c : ex.getTransitions().keySet()) { if (semanticAlts.contains(c.getAlt())) { continue; } alts.add(c.getAlt()); @SuppressWarnings("unchecked") Recognizer<Token, ?> recognizer = parser instanceof Recognizer ? (Recognizer<Token, ?>)parser : null; if (recognizer == null || c.getSemanticContext().eval(recognizer, ex.getFinalContext())) { semanticAlts.add(c.getAlt()); } } if (alts.size() != semanticAlts.size()) { LOGGER.log(Level.FINER, "Forest decision {0} reduced to {1} by predicate evaluation.", new Object[] { alts, semanticAlts }); } MultipleDecisionData decisionData = new MultipleDecisionData(); decisionData.inputIndex = parser.getInputStream().index(); decisionData.decision = 0; if (ex.getCause() != null) { int stateNumber = ex.getCause().getOffendingState(); ATNState state = parser.getATN().states.get(stateNumber); if (state instanceof StarLoopbackState) { assert state.getNumberOfTransitions() == 1 && state.onlyHasEpsilonTransitions(); assert state.transition(0).target instanceof StarLoopEntryState; state = state.transition(0).target; } else if (state instanceof PlusBlockStartState && ((PlusBlockStartState)state).decision == -1) { state = ((PlusBlockStartState)state).loopBackState; assert state instanceof PlusLoopbackState; } if (state instanceof DecisionState) { decisionData.decision = ((DecisionState)state).decision; if (decisionData.decision < 0) { LOGGER.log(Level.WARNING, "No decision number found for state {0}.", state.stateNumber); } } else { if (state != null) { LOGGER.log(Level.WARNING, "No decision number found for state {0}.", state.stateNumber); } else { LOGGER.log(Level.WARNING, "No decision number found for state <null>."); } // continuing is likely to never terminate return; } } assert semanticAlts.getMinElement() >= 1; assert semanticAlts.getMaxElement() <= parser.getATN().decisionToState.get(decisionData.decision).getNumberOfTransitions(); decisionData.alternatives = semanticAlts.toArray(); potentialAlternatives.add(decisionData); currentPath.add(-1); } else { results.put(parseTree, ex); } } catch (RecognitionException ex) { // not a viable path } } protected abstract RuleContext parseImpl(TParser parser); }