/* [The "BSD licence"] Copyright (c) 2005-2006 Terence Parr All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.antlr.analysis; import org.antlr.tool.ErrorManager; import org.antlr.tool.Grammar; import org.antlr.tool.GrammarAST; import org.antlr.tool.ANTLRParser; import org.antlr.misc.Utils; import org.antlr.misc.MultiMap; import java.util.*; import antlr.Token; /** Collection of information about what is wrong with a decision as * discovered while building the DFA predictor. * * The information is collected during NFA->DFA conversion and, while * some of this is available elsewhere, it is nice to have it all tracked * in one spot so a great error message can be easily had. I also like * the fact that this object tracks it all for later perusing to make an * excellent error message instead of lots of imprecise on-the-fly warnings * (during conversion). * * A decision normally only has one problem; e.g., some input sequence * can be matched by multiple alternatives. Unfortunately, some decisions * such as * * a : ( A | B ) | ( A | B ) | A ; * * have multiple problems. So in general, you should approach a decision * as having multiple flaws each one uniquely identified by a DFAState. * For example, statesWithSyntacticallyAmbiguousAltsSet tracks the set of * all DFAStates where ANTLR has discovered a problem. Recall that a decision * is represented internall with a DFA comprised of multiple states, each of * which could potentially have problems. * * Because of this, you need to iterate over this list of DFA states. You'll * note that most of the informational methods like * getSampleNonDeterministicInputSequence() require a DFAState. This state * will be one of the iterated states from stateToSyntacticallyAmbiguousAltsSet. * * This class is not thread safe due to shared use of visited maps etc... * Only one thread should really need to access one DecisionProbe anyway. */ public class DecisionProbe { public DFA dfa; /** Track all DFA states with nondeterministic alternatives. * By reaching the same DFA state, a path through the NFA for some input * is able to reach the same NFA state by starting at more than one * alternative's left edge. Though, later, we may find that predicates * resolve the issue, but track info anyway. * Note that from the DFA state, you can ask for * which alts are nondeterministic. */ protected Set statesWithSyntacticallyAmbiguousAltsSet = new HashSet(); /** Track just like stateToSyntacticallyAmbiguousAltsMap, but only * for nondeterminisms that arise in the Tokens rule such as keyword vs * ID rule. The state maps to the list of Tokens rule alts that are * in conflict. */ protected Map> stateToSyntacticallyAmbiguousTokensRuleAltsMap = new HashMap>(); /** Was a syntactic ambiguity resolved with predicates? Any DFA * state that predicts more than one alternative, must be resolved * with predicates or it should be reported to the user. */ protected Set statesResolvedWithSemanticPredicatesSet = new HashSet(); /** Track the predicates for each alt per DFA state; * more than one DFA state might have syntactically ambig alt prediction. * Maps DFA state to another map, mapping alt number to a * SemanticContext (pred(s) to execute to resolve syntactic ambiguity). */ protected Map> stateToAltSetWithSemanticPredicatesMap = new HashMap>(); /** Tracks alts insufficiently covered. * For example, p1||true gets reduced to true and so leaves * whole alt uncovered. This maps DFA state to the set of alts */ protected Map>> stateToIncompletelyCoveredAltsMap = new HashMap>>(); /** The set of states w/o emanating edges and w/o resolving sem preds. */ protected Set danglingStates = new HashSet(); /** The overall list of alts within the decision that have at least one * conflicting input sequence. */ protected Set altsWithProblem = new HashSet(); /** If decision with > 1 alt has recursion in > 1 alt, it's nonregular * lookahead. The decision cannot be made with a DFA. * the alts are stored in altsWithProblem. */ protected boolean nonLLStarDecision = false; /** Recursion is limited to a particular depth. If that limit is exceeded * the proposed new NFAConfiguration is recorded for the associated DFA state. */ protected MultiMap stateToRecursionOverflowConfigurationsMap = new MultiMap(); /* protected Map> stateToRecursionOverflowConfigurationsMap = new HashMap>(); */ /** Left recursion discovered. The proposed new NFAConfiguration * is recorded for the associated DFA state. protected Map> stateToLeftRecursiveConfigurationsMap = new HashMap>(); */ /** Did ANTLR have to terminate early on the analysis of this decision? */ protected boolean timedOut = false; /** Used to find paths through syntactically ambiguous DFA. If we've * seen statement number before, what did we learn? */ protected Map stateReachable; public static final Integer REACHABLE_BUSY = Utils.integer(-1); public static final Integer REACHABLE_NO = Utils.integer(0); public static final Integer REACHABLE_YES = Utils.integer(1); /** Used while finding a path through an NFA whose edge labels match * an input sequence. Tracks the input position * we were at the last time at this node. If same input position, then * we'd have reached same state without consuming input...probably an * infinite loop. Stop. Set. The strings look like * stateNumber_labelIndex. */ protected Set statesVisitedAtInputDepth; protected Set statesVisitedDuringSampleSequence; public static boolean verbose = false; public DecisionProbe(DFA dfa) { this.dfa = dfa; } // I N F O R M A T I O N A B O U T D E C I S I O N /** Return a string like "3:22: ( A {;} | B )" that describes this * decision. */ public String getDescription() { return dfa.getNFADecisionStartState().getDescription(); } public boolean isReduced() { return dfa.isReduced(); } public boolean isCyclic() { return dfa.isCyclic(); } /** If no states are dead-ends, no alts are unreachable, there are * no nondeterminisms unresolved by syn preds, all is ok with decision. */ public boolean isDeterministic() { if ( danglingStates.size()==0 && statesWithSyntacticallyAmbiguousAltsSet.size()==0 && dfa.getUnreachableAlts().size()==0 ) { return true; } if ( statesWithSyntacticallyAmbiguousAltsSet.size()>0 ) { Iterator it = statesWithSyntacticallyAmbiguousAltsSet.iterator(); while ( it.hasNext() ) { DFAState d = (DFAState) it.next(); if ( !statesResolvedWithSemanticPredicatesSet.contains(d) ) { return false; } } // no syntactically ambig alts were left unresolved by predicates return true; } return false; } /** Did the analysis complete it's work? */ public boolean analysisTimedOut() { return timedOut; } /** Took too long to analyze a DFA */ public boolean analysisOverflowed() { return stateToRecursionOverflowConfigurationsMap.size()>0; } /** Found recursion in > 1 alt */ public boolean isNonLLStarDecision() { return nonLLStarDecision; } /** How many states does the DFA predictor have? */ public int getNumberOfStates() { return dfa.getNumberOfStates(); } /** Get a list of all unreachable alternatives for this decision. There * may be multiple alternatives with ambiguous input sequences, but this * is the overall list of unreachable alternatives (either due to * conflict resolution or alts w/o accept states). */ public List getUnreachableAlts() { return dfa.getUnreachableAlts(); } /** return set of states w/o emanating edges and w/o resolving sem preds. * These states come about because the analysis algorithm had to * terminate early to avoid infinite recursion for example (due to * left recursion perhaps). */ public Set getDanglingStates() { return danglingStates; } public Set getNonDeterministicAlts() { return altsWithProblem; } /** Return the sorted list of alts that conflict within a single state. * Note that predicates may resolve the conflict. */ public List getNonDeterministicAltsForState(DFAState targetState) { Set nondetAlts = targetState.getNonDeterministicAlts(); if ( nondetAlts==null ) { return null; } List sorted = new LinkedList(); sorted.addAll(nondetAlts); Collections.sort(sorted); // make sure it's 1, 2, ... return sorted; } /** Return all DFA states in this DFA that have NFA configurations that * conflict. You must report a problem for each state in this set * because each state represents a different input sequence. */ public Set getDFAStatesWithSyntacticallyAmbiguousAlts() { return statesWithSyntacticallyAmbiguousAltsSet; } /** Which alts were specifically turned off to resolve nondeterminisms? * This is different than the unreachable alts. Disabled doesn't mean that * the alternative is totally unreachable necessarily, it just means * that for this DFA state, that alt is disabled. There may be other * accept states for that alt that make an alt reachable. */ public Set getDisabledAlternatives(DFAState d) { return d.getDisabledAlternatives(); } /** If a recursion overflow is resolve with predicates, then we need * to shut off the warning that would be generated. */ public void removeRecursiveOverflowState(DFAState d) { Integer stateI = Utils.integer(d.stateNumber); stateToRecursionOverflowConfigurationsMap.remove(stateI); } /** Return a List