/* * [The "BSD license"] * Copyright (c) 2010 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.grammar.v3.ANTLRParser; import org.antlr.misc.MultiMap; import org.antlr.misc.Utils; import org.antlr.runtime.Token; import org.antlr.tool.ErrorManager; import org.antlr.tool.Grammar; import org.antlr.tool.GrammarAST; import java.util.*; /** 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 (likely) nonregular * lookahead. The decision cannot be made with a DFA. * the alts are stored in altsWithProblem. */ public 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