1 /* 2 * [The "BSD license"] 3 * Copyright (c) 2010 Terence Parr 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. The name of the author may not be used to endorse or promote products 15 * derived from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 package org.antlr.analysis; 29 30 import org.antlr.grammar.v3.ANTLRParser; 31 import org.antlr.misc.MultiMap; 32 import org.antlr.misc.Utils; 33 import org.antlr.runtime.Token; 34 import org.antlr.tool.ErrorManager; 35 import org.antlr.tool.Grammar; 36 import org.antlr.tool.GrammarAST; 37 38 import java.util.*; 39 40 /** Collection of information about what is wrong with a decision as 41 * discovered while building the DFA predictor. 42 * 43 * The information is collected during NFA->DFA conversion and, while 44 * some of this is available elsewhere, it is nice to have it all tracked 45 * in one spot so a great error message can be easily had. I also like 46 * the fact that this object tracks it all for later perusing to make an 47 * excellent error message instead of lots of imprecise on-the-fly warnings 48 * (during conversion). 49 * 50 * A decision normally only has one problem; e.g., some input sequence 51 * can be matched by multiple alternatives. Unfortunately, some decisions 52 * such as 53 * 54 * a : ( A | B ) | ( A | B ) | A ; 55 * 56 * have multiple problems. So in general, you should approach a decision 57 * as having multiple flaws each one uniquely identified by a DFAState. 58 * For example, statesWithSyntacticallyAmbiguousAltsSet tracks the set of 59 * all DFAStates where ANTLR has discovered a problem. Recall that a decision 60 * is represented internall with a DFA comprised of multiple states, each of 61 * which could potentially have problems. 62 * 63 * Because of this, you need to iterate over this list of DFA states. You'll 64 * note that most of the informational methods like 65 * getSampleNonDeterministicInputSequence() require a DFAState. This state 66 * will be one of the iterated states from stateToSyntacticallyAmbiguousAltsSet. 67 * 68 * This class is not thread safe due to shared use of visited maps etc... 69 * Only one thread should really need to access one DecisionProbe anyway. 70 */ 71 public class DecisionProbe { 72 public DFA dfa; 73 74 /** Track all DFA states with nondeterministic alternatives. 75 * By reaching the same DFA state, a path through the NFA for some input 76 * is able to reach the same NFA state by starting at more than one 77 * alternative's left edge. Though, later, we may find that predicates 78 * resolve the issue, but track info anyway. 79 * Note that from the DFA state, you can ask for 80 * which alts are nondeterministic. 81 */ 82 protected Set<DFAState> statesWithSyntacticallyAmbiguousAltsSet = new HashSet<DFAState>(); 83 84 /** Track just like stateToSyntacticallyAmbiguousAltsMap, but only 85 * for nondeterminisms that arise in the Tokens rule such as keyword vs 86 * ID rule. The state maps to the list of Tokens rule alts that are 87 * in conflict. 88 */ 89 protected Map<DFAState, Set<Integer>> stateToSyntacticallyAmbiguousTokensRuleAltsMap = 90 new HashMap<DFAState, Set<Integer>>(); 91 92 /** Was a syntactic ambiguity resolved with predicates? Any DFA 93 * state that predicts more than one alternative, must be resolved 94 * with predicates or it should be reported to the user. 95 */ 96 protected Set<DFAState> statesResolvedWithSemanticPredicatesSet = new HashSet<DFAState>(); 97 98 /** Track the predicates for each alt per DFA state; 99 * more than one DFA state might have syntactically ambig alt prediction. 100 * Maps DFA state to another map, mapping alt number to a 101 * SemanticContext (pred(s) to execute to resolve syntactic ambiguity). 102 */ 103 protected Map<DFAState, Map<Integer,SemanticContext>> stateToAltSetWithSemanticPredicatesMap = 104 new HashMap<DFAState, Map<Integer,SemanticContext>>(); 105 106 /** Tracks alts insufficiently covered. 107 * For example, p1||true gets reduced to true and so leaves 108 * whole alt uncovered. This maps DFA state to the set of alts 109 */ 110 protected Map<DFAState,Map<Integer, Set<Token>>> stateToIncompletelyCoveredAltsMap = 111 new HashMap<DFAState,Map<Integer, Set<Token>>>(); 112 113 /** The set of states w/o emanating edges and w/o resolving sem preds. */ 114 protected Set<DFAState> danglingStates = new HashSet<DFAState>(); 115 116 /** The overall list of alts within the decision that have at least one 117 * conflicting input sequence. 118 */ 119 protected Set<Integer> altsWithProblem = new HashSet<Integer>(); 120 121 /** If decision with > 1 alt has recursion in > 1 alt, it's (likely) nonregular 122 * lookahead. The decision cannot be made with a DFA. 123 * the alts are stored in altsWithProblem. 124 */ 125 public boolean nonLLStarDecision = false; 126 127 /** Recursion is limited to a particular depth. If that limit is exceeded 128 * the proposed new NFAConfiguration is recorded for the associated DFA state. 129 */ 130 protected MultiMap<Integer, NFAConfiguration> stateToRecursionOverflowConfigurationsMap = 131 new MultiMap<Integer, NFAConfiguration>(); 132 /* 133 protected Map<Integer, List<NFAConfiguration>> stateToRecursionOverflowConfigurationsMap = 134 new HashMap<Integer, List<NFAConfiguration>>(); 135 */ 136 137 /** Left recursion discovered. The proposed new NFAConfiguration 138 * is recorded for the associated DFA state. 139 protected Map<Integer,List<NFAConfiguration>> stateToLeftRecursiveConfigurationsMap = 140 new HashMap<Integer,List<NFAConfiguration>>(); 141 */ 142 143 /** Did ANTLR have to terminate early on the analysis of this decision? */ 144 protected boolean timedOut = false; 145 146 /** Used to find paths through syntactically ambiguous DFA. If we've 147 * seen statement number before, what did we learn? 148 */ 149 protected Map<Integer, Integer> stateReachable; 150 151 public static final Integer REACHABLE_BUSY = Utils.integer(-1); 152 public static final Integer REACHABLE_NO = Utils.integer(0); 153 public static final Integer REACHABLE_YES = Utils.integer(1); 154 155 /** Used while finding a path through an NFA whose edge labels match 156 * an input sequence. Tracks the input position 157 * we were at the last time at this node. If same input position, then 158 * we'd have reached same state without consuming input...probably an 159 * infinite loop. Stop. Set<String>. The strings look like 160 * stateNumber_labelIndex. 161 */ 162 protected Set<String> statesVisitedAtInputDepth; 163 164 protected Set<Integer> statesVisitedDuringSampleSequence; 165 166 public static boolean verbose = false; 167 DecisionProbe(DFA dfa)168 public DecisionProbe(DFA dfa) { 169 this.dfa = dfa; 170 } 171 172 // I N F O R M A T I O N A B O U T D E C I S I O N 173 174 /** Return a string like "3:22: ( A {;} | B )" that describes this 175 * decision. 176 */ getDescription()177 public String getDescription() { 178 return dfa.getNFADecisionStartState().getDescription(); 179 } 180 isReduced()181 public boolean isReduced() { 182 return dfa.isReduced(); 183 } 184 isCyclic()185 public boolean isCyclic() { 186 return dfa.isCyclic(); 187 } 188 189 /** If no states are dead-ends, no alts are unreachable, there are 190 * no nondeterminisms unresolved by syn preds, all is ok with decision. 191 */ isDeterministic()192 public boolean isDeterministic() { 193 if ( danglingStates.size()==0 && 194 statesWithSyntacticallyAmbiguousAltsSet.size()==0 && 195 dfa.getUnreachableAlts().size()==0 ) 196 { 197 return true; 198 } 199 200 if ( statesWithSyntacticallyAmbiguousAltsSet.size()>0 ) { 201 Iterator it = 202 statesWithSyntacticallyAmbiguousAltsSet.iterator(); 203 while ( it.hasNext() ) { 204 DFAState d = (DFAState) it.next(); 205 if ( !statesResolvedWithSemanticPredicatesSet.contains(d) ) { 206 return false; 207 } 208 } 209 // no syntactically ambig alts were left unresolved by predicates 210 return true; 211 } 212 return false; 213 } 214 215 /** Did the analysis complete it's work? */ 216 // public boolean analysisTimedOut() { 217 // return timedOut; 218 // } 219 220 /** Took too long to analyze a DFA */ analysisOverflowed()221 public boolean analysisOverflowed() { 222 return stateToRecursionOverflowConfigurationsMap.size()>0; 223 } 224 225 /** Found recursion in > 1 alt */ isNonLLStarDecision()226 public boolean isNonLLStarDecision() { 227 return nonLLStarDecision; 228 } 229 230 /** How many states does the DFA predictor have? */ getNumberOfStates()231 public int getNumberOfStates() { 232 return dfa.getNumberOfStates(); 233 } 234 235 /** Get a list of all unreachable alternatives for this decision. There 236 * may be multiple alternatives with ambiguous input sequences, but this 237 * is the overall list of unreachable alternatives (either due to 238 * conflict resolution or alts w/o accept states). 239 */ getUnreachableAlts()240 public List<Integer> getUnreachableAlts() { 241 return dfa.getUnreachableAlts(); 242 } 243 244 /** return set of states w/o emanating edges and w/o resolving sem preds. 245 * These states come about because the analysis algorithm had to 246 * terminate early to avoid infinite recursion for example (due to 247 * left recursion perhaps). 248 */ getDanglingStates()249 public Set getDanglingStates() { 250 return danglingStates; 251 } 252 getNonDeterministicAlts()253 public Set getNonDeterministicAlts() { 254 return altsWithProblem; 255 } 256 257 /** Return the sorted list of alts that conflict within a single state. 258 * Note that predicates may resolve the conflict. 259 */ getNonDeterministicAltsForState(DFAState targetState)260 public List getNonDeterministicAltsForState(DFAState targetState) { 261 Set nondetAlts = targetState.getNonDeterministicAlts(); 262 if ( nondetAlts==null ) { 263 return null; 264 } 265 List sorted = new LinkedList(); 266 sorted.addAll(nondetAlts); 267 Collections.sort(sorted); // make sure it's 1, 2, ... 268 return sorted; 269 } 270 271 /** Return all DFA states in this DFA that have NFA configurations that 272 * conflict. You must report a problem for each state in this set 273 * because each state represents a different input sequence. 274 */ getDFAStatesWithSyntacticallyAmbiguousAlts()275 public Set getDFAStatesWithSyntacticallyAmbiguousAlts() { 276 return statesWithSyntacticallyAmbiguousAltsSet; 277 } 278 279 /** Which alts were specifically turned off to resolve nondeterminisms? 280 * This is different than the unreachable alts. Disabled doesn't mean that 281 * the alternative is totally unreachable necessarily, it just means 282 * that for this DFA state, that alt is disabled. There may be other 283 * accept states for that alt that make an alt reachable. 284 */ getDisabledAlternatives(DFAState d)285 public Set getDisabledAlternatives(DFAState d) { 286 return d.getDisabledAlternatives(); 287 } 288 289 /** If a recursion overflow is resolve with predicates, then we need 290 * to shut off the warning that would be generated. 291 */ removeRecursiveOverflowState(DFAState d)292 public void removeRecursiveOverflowState(DFAState d) { 293 Integer stateI = Utils.integer(d.stateNumber); 294 stateToRecursionOverflowConfigurationsMap.remove(stateI); 295 } 296 297 /** Return a List<Label> indicating an input sequence that can be matched 298 * from the start state of the DFA to the targetState (which is known 299 * to have a problem). 300 */ getSampleNonDeterministicInputSequence(DFAState targetState)301 public List<Label> getSampleNonDeterministicInputSequence(DFAState targetState) { 302 Set dfaStates = getDFAPathStatesToTarget(targetState); 303 statesVisitedDuringSampleSequence = new HashSet<Integer>(); 304 List<Label> labels = new ArrayList<Label>(); // may access ith element; use array 305 if ( dfa==null || dfa.startState==null ) { 306 return labels; 307 } 308 getSampleInputSequenceUsingStateSet(dfa.startState, 309 targetState, 310 dfaStates, 311 labels); 312 return labels; 313 } 314 315 /** Given List<Label>, return a String with a useful representation 316 * of the associated input string. One could show something different 317 * for lexers and parsers, for example. 318 */ getInputSequenceDisplay(List labels)319 public String getInputSequenceDisplay(List labels) { 320 Grammar g = dfa.nfa.grammar; 321 StringBuffer buf = new StringBuffer(); 322 for (Iterator it = labels.iterator(); it.hasNext();) { 323 Label label = (Label) it.next(); 324 buf.append(label.toString(g)); 325 if ( it.hasNext() && g.type!=Grammar.LEXER ) { 326 buf.append(' '); 327 } 328 } 329 return buf.toString(); 330 } 331 332 /** Given an alternative associated with a nondeterministic DFA state, 333 * find the path of NFA states associated with the labels sequence. 334 * Useful tracing where in the NFA, a single input sequence can be 335 * matched. For different alts, you should get different NFA paths. 336 * 337 * The first NFA state for all NFA paths will be the same: the starting 338 * NFA state of the first nondeterministic alt. Imagine (A|B|A|A): 339 * 340 * 5->9-A->o 341 * | 342 * 6->10-B->o 343 * | 344 * 7->11-A->o 345 * | 346 * 8->12-A->o 347 * 348 * There are 3 nondeterministic alts. The paths should be: 349 * 5 9 ... 350 * 5 6 7 11 ... 351 * 5 6 7 8 12 ... 352 * 353 * The NFA path matching the sample input sequence (labels) is computed 354 * using states 9, 11, and 12 rather than 5, 7, 8 because state 5, for 355 * example can get to all ambig paths. Must isolate for each alt (hence, 356 * the extra state beginning each alt in my NFA structures). Here, 357 * firstAlt=1. 358 */ getNFAPathStatesForAlt(int firstAlt, int alt, List labels)359 public List getNFAPathStatesForAlt(int firstAlt, 360 int alt, 361 List labels) 362 { 363 NFAState nfaStart = dfa.getNFADecisionStartState(); 364 List path = new LinkedList(); 365 // first add all NFA states leading up to altStart state 366 for (int a=firstAlt; a<=alt; a++) { 367 NFAState s = 368 dfa.nfa.grammar.getNFAStateForAltOfDecision(nfaStart,a); 369 path.add(s); 370 } 371 372 // add first state of actual alt 373 NFAState altStart = dfa.nfa.grammar.getNFAStateForAltOfDecision(nfaStart,alt); 374 NFAState isolatedAltStart = (NFAState)altStart.transition[0].target; 375 path.add(isolatedAltStart); 376 377 // add the actual path now 378 statesVisitedAtInputDepth = new HashSet(); 379 getNFAPath(isolatedAltStart, 380 0, 381 labels, 382 path); 383 return path; 384 } 385 386 /** Each state in the DFA represents a different input sequence for an 387 * alt of the decision. Given a DFA state, what is the semantic 388 * predicate context for a particular alt. 389 */ getSemanticContextForAlt(DFAState d, int alt)390 public SemanticContext getSemanticContextForAlt(DFAState d, int alt) { 391 Map altToPredMap = (Map)stateToAltSetWithSemanticPredicatesMap.get(d); 392 if ( altToPredMap==null ) { 393 return null; 394 } 395 return (SemanticContext)altToPredMap.get(Utils.integer(alt)); 396 } 397 398 /** At least one alt refs a sem or syn pred */ hasPredicate()399 public boolean hasPredicate() { 400 return stateToAltSetWithSemanticPredicatesMap.size()>0; 401 } 402 getNondeterministicStatesResolvedWithSemanticPredicate()403 public Set getNondeterministicStatesResolvedWithSemanticPredicate() { 404 return statesResolvedWithSemanticPredicatesSet; 405 } 406 407 /** Return a list of alts whose predicate context was insufficient to 408 * resolve a nondeterminism for state d. 409 */ getIncompletelyCoveredAlts(DFAState d)410 public Map<Integer, Set<Token>> getIncompletelyCoveredAlts(DFAState d) { 411 return stateToIncompletelyCoveredAltsMap.get(d); 412 } 413 issueWarnings()414 public void issueWarnings() { 415 // NONREGULAR DUE TO RECURSION > 1 ALTS 416 // Issue this before aborted analysis, which might also occur 417 // if we take too long to terminate 418 if ( nonLLStarDecision && !dfa.getAutoBacktrackMode() ) { 419 ErrorManager.nonLLStarDecision(this); 420 } 421 422 issueRecursionWarnings(); 423 424 // generate a separate message for each problem state in DFA 425 Set resolvedStates = getNondeterministicStatesResolvedWithSemanticPredicate(); 426 Set problemStates = getDFAStatesWithSyntacticallyAmbiguousAlts(); 427 if ( problemStates.size()>0 ) { 428 Iterator it = 429 problemStates.iterator(); 430 while ( it.hasNext() && !dfa.nfa.grammar.NFAToDFAConversionExternallyAborted() ) { 431 DFAState d = (DFAState) it.next(); 432 Map<Integer, Set<Token>> insufficientAltToLocations = getIncompletelyCoveredAlts(d); 433 if ( insufficientAltToLocations!=null && insufficientAltToLocations.size()>0 ) { 434 ErrorManager.insufficientPredicates(this,d,insufficientAltToLocations); 435 } 436 // don't report problem if resolved 437 if ( resolvedStates==null || !resolvedStates.contains(d) ) { 438 // first strip last alt from disableAlts if it's wildcard 439 // then don't print error if no more disable alts 440 Set disabledAlts = getDisabledAlternatives(d); 441 stripWildCardAlts(disabledAlts); 442 if ( disabledAlts.size()>0 ) { 443 // nondeterminism; same input predicts multiple alts. 444 // but don't emit error if greedy=true explicitly set 445 boolean explicitlyGreedy = false; 446 GrammarAST blockAST = 447 d.dfa.nfa.grammar.getDecisionBlockAST(d.dfa.decisionNumber); 448 if ( blockAST!=null ) { 449 String greedyS = (String)blockAST.getBlockOption("greedy"); 450 if ( greedyS!=null && greedyS.equals("true") ) explicitlyGreedy = true; 451 } 452 if ( !explicitlyGreedy) ErrorManager.nondeterminism(this,d); 453 } 454 } 455 } 456 } 457 458 Set danglingStates = getDanglingStates(); 459 if ( danglingStates.size()>0 ) { 460 //System.err.println("no emanating edges for states: "+danglingStates); 461 for (Iterator it = danglingStates.iterator(); it.hasNext();) { 462 DFAState d = (DFAState) it.next(); 463 ErrorManager.danglingState(this,d); 464 } 465 } 466 467 if ( !nonLLStarDecision ) { 468 List<Integer> unreachableAlts = dfa.getUnreachableAlts(); 469 if ( unreachableAlts!=null && unreachableAlts.size()>0 ) { 470 // give different msg if it's an empty Tokens rule from delegate 471 boolean isInheritedTokensRule = false; 472 if ( dfa.isTokensRuleDecision() ) { 473 for (Integer altI : unreachableAlts) { 474 GrammarAST decAST = dfa.getDecisionASTNode(); 475 GrammarAST altAST = (GrammarAST)decAST.getChild(altI-1); 476 GrammarAST delegatedTokensAlt = 477 (GrammarAST)altAST.getFirstChildWithType(ANTLRParser.DOT); 478 if ( delegatedTokensAlt !=null ) { 479 isInheritedTokensRule = true; 480 ErrorManager.grammarWarning(ErrorManager.MSG_IMPORTED_TOKENS_RULE_EMPTY, 481 dfa.nfa.grammar, 482 null, 483 dfa.nfa.grammar.name, 484 delegatedTokensAlt.getChild(0).getText()); 485 } 486 } 487 } 488 if ( isInheritedTokensRule ) { 489 } 490 else { 491 ErrorManager.unreachableAlts(this,unreachableAlts); 492 } 493 } 494 } 495 } 496 497 /** Get the last disabled alt number and check in the grammar to see 498 * if that alt is a simple wildcard. If so, treat like an else clause 499 * and don't emit the error. Strip out the last alt if it's wildcard. 500 */ stripWildCardAlts(Set disabledAlts)501 protected void stripWildCardAlts(Set disabledAlts) { 502 List sortedDisableAlts = new ArrayList(disabledAlts); 503 Collections.sort(sortedDisableAlts); 504 Integer lastAlt = 505 (Integer)sortedDisableAlts.get(sortedDisableAlts.size()-1); 506 GrammarAST blockAST = 507 dfa.nfa.grammar.getDecisionBlockAST(dfa.decisionNumber); 508 //System.out.println("block with error = "+blockAST.toStringTree()); 509 GrammarAST lastAltAST = null; 510 if ( blockAST.getChild(0).getType()==ANTLRParser.OPTIONS ) { 511 // if options, skip first child: ( options { ( = greedy false ) ) 512 lastAltAST = (GrammarAST)blockAST.getChild(lastAlt.intValue()); 513 } 514 else { 515 lastAltAST = (GrammarAST)blockAST.getChild(lastAlt.intValue()-1); 516 } 517 //System.out.println("last alt is "+lastAltAST.toStringTree()); 518 // if last alt looks like ( ALT . <end-of-alt> ) then wildcard 519 // Avoid looking at optional blocks etc... that have last alt 520 // as the EOB: 521 // ( BLOCK ( ALT 'else' statement <end-of-alt> ) <end-of-block> ) 522 if ( lastAltAST.getType()!=ANTLRParser.EOB && 523 lastAltAST.getChild(0).getType()== ANTLRParser.WILDCARD && 524 lastAltAST.getChild(1).getType()== ANTLRParser.EOA ) 525 { 526 //System.out.println("wildcard"); 527 disabledAlts.remove(lastAlt); 528 } 529 } 530 issueRecursionWarnings()531 protected void issueRecursionWarnings() { 532 // RECURSION OVERFLOW 533 Set dfaStatesWithRecursionProblems = 534 stateToRecursionOverflowConfigurationsMap.keySet(); 535 // now walk truly unique (unaliased) list of dfa states with inf recur 536 // Goal: create a map from alt to map<target,List<callsites>> 537 // Map<Map<String target, List<NFAState call sites>> 538 Map altToTargetToCallSitesMap = new HashMap(); 539 // track a single problem DFA state for each alt 540 Map altToDFAState = new HashMap(); 541 computeAltToProblemMaps(dfaStatesWithRecursionProblems, 542 stateToRecursionOverflowConfigurationsMap, 543 altToTargetToCallSitesMap, // output param 544 altToDFAState); // output param 545 546 // walk each alt with recursion overflow problems and generate error 547 Set alts = altToTargetToCallSitesMap.keySet(); 548 List sortedAlts = new ArrayList(alts); 549 Collections.sort(sortedAlts); 550 for (Iterator altsIt = sortedAlts.iterator(); altsIt.hasNext();) { 551 Integer altI = (Integer) altsIt.next(); 552 Map targetToCallSiteMap = 553 (Map)altToTargetToCallSitesMap.get(altI); 554 Set targetRules = targetToCallSiteMap.keySet(); 555 Collection callSiteStates = targetToCallSiteMap.values(); 556 DFAState sampleBadState = (DFAState)altToDFAState.get(altI); 557 ErrorManager.recursionOverflow(this, 558 sampleBadState, 559 altI.intValue(), 560 targetRules, 561 callSiteStates); 562 } 563 } 564 computeAltToProblemMaps(Set dfaStatesUnaliased, Map configurationsMap, Map altToTargetToCallSitesMap, Map altToDFAState)565 private void computeAltToProblemMaps(Set dfaStatesUnaliased, 566 Map configurationsMap, 567 Map altToTargetToCallSitesMap, 568 Map altToDFAState) 569 { 570 for (Iterator it = dfaStatesUnaliased.iterator(); it.hasNext();) { 571 Integer stateI = (Integer) it.next(); 572 // walk this DFA's config list 573 List configs = (List)configurationsMap.get(stateI); 574 for (int i = 0; i < configs.size(); i++) { 575 NFAConfiguration c = (NFAConfiguration) configs.get(i); 576 NFAState ruleInvocationState = dfa.nfa.getState(c.state); 577 Transition transition0 = ruleInvocationState.transition[0]; 578 RuleClosureTransition ref = (RuleClosureTransition)transition0; 579 String targetRule = ((NFAState) ref.target).enclosingRule.name; 580 Integer altI = Utils.integer(c.alt); 581 Map targetToCallSiteMap = 582 (Map)altToTargetToCallSitesMap.get(altI); 583 if ( targetToCallSiteMap==null ) { 584 targetToCallSiteMap = new HashMap(); 585 altToTargetToCallSitesMap.put(altI, targetToCallSiteMap); 586 } 587 Set callSites = 588 (HashSet)targetToCallSiteMap.get(targetRule); 589 if ( callSites==null ) { 590 callSites = new HashSet(); 591 targetToCallSiteMap.put(targetRule, callSites); 592 } 593 callSites.add(ruleInvocationState); 594 // track one problem DFA state per alt 595 if ( altToDFAState.get(altI)==null ) { 596 DFAState sampleBadState = dfa.getState(stateI.intValue()); 597 altToDFAState.put(altI, sampleBadState); 598 } 599 } 600 } 601 } 602 getUnaliasedDFAStateSet(Set dfaStatesWithRecursionProblems)603 private Set getUnaliasedDFAStateSet(Set dfaStatesWithRecursionProblems) { 604 Set dfaStatesUnaliased = new HashSet(); 605 for (Iterator it = dfaStatesWithRecursionProblems.iterator(); it.hasNext();) { 606 Integer stateI = (Integer) it.next(); 607 DFAState d = dfa.getState(stateI.intValue()); 608 dfaStatesUnaliased.add(Utils.integer(d.stateNumber)); 609 } 610 return dfaStatesUnaliased; 611 } 612 613 614 // T R A C K I N G M E T H O D S 615 616 /** Report the fact that DFA state d is not a state resolved with 617 * predicates and yet it has no emanating edges. Usually this 618 * is a result of the closure/reach operations being unable to proceed 619 */ reportDanglingState(DFAState d)620 public void reportDanglingState(DFAState d) { 621 danglingStates.add(d); 622 } 623 624 // public void reportAnalysisTimeout() { 625 // timedOut = true; 626 // dfa.nfa.grammar.setOfDFAWhoseAnalysisTimedOut.add(dfa); 627 // } 628 629 /** Report that at least 2 alts have recursive constructs. There is 630 * no way to build a DFA so we terminated. 631 */ reportNonLLStarDecision(DFA dfa)632 public void reportNonLLStarDecision(DFA dfa) { 633 /* 634 System.out.println("non-LL(*) DFA "+dfa.decisionNumber+", alts: "+ 635 dfa.recursiveAltSet.toList()); 636 */ 637 nonLLStarDecision = true; 638 dfa.nfa.grammar.numNonLLStar++; 639 altsWithProblem.addAll(dfa.recursiveAltSet.toList()); 640 } 641 reportRecursionOverflow(DFAState d, NFAConfiguration recursionNFAConfiguration)642 public void reportRecursionOverflow(DFAState d, 643 NFAConfiguration recursionNFAConfiguration) 644 { 645 // track the state number rather than the state as d will change 646 // out from underneath us; hash wouldn't return any value 647 648 // left-recursion is detected in start state. Since we can't 649 // call resolveNondeterminism() on the start state (it would 650 // not look k=1 to get min single token lookahead), we must 651 // prevent errors derived from this state. Avoid start state 652 if ( d.stateNumber > 0 ) { 653 Integer stateI = Utils.integer(d.stateNumber); 654 stateToRecursionOverflowConfigurationsMap.map(stateI, recursionNFAConfiguration); 655 } 656 } 657 reportNondeterminism(DFAState d, Set<Integer> nondeterministicAlts)658 public void reportNondeterminism(DFAState d, Set<Integer> nondeterministicAlts) { 659 altsWithProblem.addAll(nondeterministicAlts); // track overall list 660 statesWithSyntacticallyAmbiguousAltsSet.add(d); 661 dfa.nfa.grammar.setOfNondeterministicDecisionNumbers.add( 662 Utils.integer(dfa.getDecisionNumber()) 663 ); 664 } 665 666 /** Currently the analysis reports issues between token definitions, but 667 * we don't print out warnings in favor of just picking the first token 668 * definition found in the grammar ala lex/flex. 669 */ reportLexerRuleNondeterminism(DFAState d, Set<Integer> nondeterministicAlts)670 public void reportLexerRuleNondeterminism(DFAState d, Set<Integer> nondeterministicAlts) { 671 stateToSyntacticallyAmbiguousTokensRuleAltsMap.put(d,nondeterministicAlts); 672 } 673 reportNondeterminismResolvedWithSemanticPredicate(DFAState d)674 public void reportNondeterminismResolvedWithSemanticPredicate(DFAState d) { 675 // First, prevent a recursion warning on this state due to 676 // pred resolution 677 if ( d.abortedDueToRecursionOverflow ) { 678 d.dfa.probe.removeRecursiveOverflowState(d); 679 } 680 statesResolvedWithSemanticPredicatesSet.add(d); 681 //System.out.println("resolved with pred: "+d); 682 dfa.nfa.grammar.setOfNondeterministicDecisionNumbersResolvedWithPredicates.add( 683 Utils.integer(dfa.getDecisionNumber()) 684 ); 685 } 686 687 /** Report the list of predicates found for each alternative; copy 688 * the list because this set gets altered later by the method 689 * tryToResolveWithSemanticPredicates() while flagging NFA configurations 690 * in d as resolved. 691 */ reportAltPredicateContext(DFAState d, Map altPredicateContext)692 public void reportAltPredicateContext(DFAState d, Map altPredicateContext) { 693 Map copy = new HashMap(); 694 copy.putAll(altPredicateContext); 695 stateToAltSetWithSemanticPredicatesMap.put(d,copy); 696 } 697 reportIncompletelyCoveredAlts(DFAState d, Map<Integer, Set<Token>> altToLocationsReachableWithoutPredicate)698 public void reportIncompletelyCoveredAlts(DFAState d, 699 Map<Integer, Set<Token>> altToLocationsReachableWithoutPredicate) 700 { 701 stateToIncompletelyCoveredAltsMap.put(d, altToLocationsReachableWithoutPredicate); 702 } 703 704 // S U P P O R T 705 706 /** Given a start state and a target state, return true if start can reach 707 * target state. Also, compute the set of DFA states 708 * that are on a path from start to target; return in states parameter. 709 */ reachesState(DFAState startState, DFAState targetState, Set states)710 protected boolean reachesState(DFAState startState, 711 DFAState targetState, 712 Set states) { 713 if ( startState==targetState ) { 714 states.add(targetState); 715 //System.out.println("found target DFA state "+targetState.getStateNumber()); 716 stateReachable.put(startState.stateNumber, REACHABLE_YES); 717 return true; 718 } 719 720 DFAState s = startState; 721 // avoid infinite loops 722 stateReachable.put(s.stateNumber, REACHABLE_BUSY); 723 724 // look for a path to targetState among transitions for this state 725 // stop when you find the first one; I'm pretty sure there is 726 // at most one path to any DFA state with conflicting predictions 727 for (int i=0; i<s.getNumberOfTransitions(); i++) { 728 Transition t = s.transition(i); 729 DFAState edgeTarget = (DFAState)t.target; 730 Integer targetStatus = stateReachable.get(edgeTarget.stateNumber); 731 if ( targetStatus==REACHABLE_BUSY ) { // avoid cycles; they say nothing 732 continue; 733 } 734 if ( targetStatus==REACHABLE_YES ) { // return success! 735 stateReachable.put(s.stateNumber, REACHABLE_YES); 736 return true; 737 } 738 if ( targetStatus==REACHABLE_NO ) { // try another transition 739 continue; 740 } 741 // if null, target must be REACHABLE_UNKNOWN (i.e., unvisited) 742 if ( reachesState(edgeTarget, targetState, states) ) { 743 states.add(s); 744 stateReachable.put(s.stateNumber, REACHABLE_YES); 745 return true; 746 } 747 } 748 749 stateReachable.put(s.stateNumber, REACHABLE_NO); 750 return false; // no path to targetState found. 751 } 752 getDFAPathStatesToTarget(DFAState targetState)753 protected Set getDFAPathStatesToTarget(DFAState targetState) { 754 Set dfaStates = new HashSet(); 755 stateReachable = new HashMap(); 756 if ( dfa==null || dfa.startState==null ) { 757 return dfaStates; 758 } 759 boolean reaches = reachesState(dfa.startState, targetState, dfaStates); 760 return dfaStates; 761 } 762 763 /** Given a start state and a final state, find a list of edge labels 764 * between the two ignoring epsilon. Limit your scan to a set of states 765 * passed in. This is used to show a sample input sequence that is 766 * nondeterministic with respect to this decision. Return List<Label> as 767 * a parameter. The incoming states set must be all states that lead 768 * from startState to targetState and no others so this algorithm doesn't 769 * take a path that eventually leads to a state other than targetState. 770 * Don't follow loops, leading to short (possibly shortest) path. 771 */ getSampleInputSequenceUsingStateSet(State startState, State targetState, Set states, List<Label> labels)772 protected void getSampleInputSequenceUsingStateSet(State startState, 773 State targetState, 774 Set states, 775 List<Label> labels) 776 { 777 statesVisitedDuringSampleSequence.add(startState.stateNumber); 778 779 // pick the first edge in states as the one to traverse 780 for (int i=0; i<startState.getNumberOfTransitions(); i++) { 781 Transition t = startState.transition(i); 782 DFAState edgeTarget = (DFAState)t.target; 783 if ( states.contains(edgeTarget) && 784 !statesVisitedDuringSampleSequence.contains(edgeTarget.stateNumber) ) 785 { 786 labels.add(t.label); // traverse edge and track label 787 if ( edgeTarget!=targetState ) { 788 // get more labels if not at target 789 getSampleInputSequenceUsingStateSet(edgeTarget, 790 targetState, 791 states, 792 labels); 793 } 794 // done with this DFA state as we've found a good path to target 795 return; 796 } 797 } 798 labels.add(new Label(Label.EPSILON)); // indicate no input found 799 // this happens on a : {p1}? a | A ; 800 //ErrorManager.error(ErrorManager.MSG_CANNOT_COMPUTE_SAMPLE_INPUT_SEQ); 801 } 802 803 /** Given a sample input sequence, you usually would like to know the 804 * path taken through the NFA. Return the list of NFA states visited 805 * while matching a list of labels. This cannot use the usual 806 * interpreter, which does a deterministic walk. We need to be able to 807 * take paths that are turned off during nondeterminism resolution. So, 808 * just do a depth-first walk traversing edges labeled with the current 809 * label. Return true if a path was found emanating from state s. 810 */ getNFAPath(NFAState s, int labelIndex, List labels, List path)811 protected boolean getNFAPath(NFAState s, // starting where? 812 int labelIndex, // 0..labels.size()-1 813 List labels, // input sequence 814 List path) // output list of NFA states 815 { 816 // track a visit to state s at input index labelIndex if not seen 817 String thisStateKey = getStateLabelIndexKey(s.stateNumber,labelIndex); 818 if ( statesVisitedAtInputDepth.contains(thisStateKey) ) { 819 /* 820 System.out.println("### already visited "+s.stateNumber+" previously at index "+ 821 labelIndex); 822 */ 823 return false; 824 } 825 statesVisitedAtInputDepth.add(thisStateKey); 826 827 /* 828 System.out.println("enter state "+s.stateNumber+" visited states: "+ 829 statesVisitedAtInputDepth); 830 */ 831 832 // pick the first edge whose target is in states and whose 833 // label is labels[labelIndex] 834 for (int i=0; i<s.getNumberOfTransitions(); i++) { 835 Transition t = s.transition[i]; 836 NFAState edgeTarget = (NFAState)t.target; 837 Label label = (Label)labels.get(labelIndex); 838 /* 839 System.out.println(s.stateNumber+"-"+ 840 t.label.toString(dfa.nfa.grammar)+"->"+ 841 edgeTarget.stateNumber+" =="+ 842 label.toString(dfa.nfa.grammar)+"?"); 843 */ 844 if ( t.label.isEpsilon() || t.label.isSemanticPredicate() ) { 845 // nondeterministically backtrack down epsilon edges 846 path.add(edgeTarget); 847 boolean found = 848 getNFAPath(edgeTarget, labelIndex, labels, path); 849 if ( found ) { 850 statesVisitedAtInputDepth.remove(thisStateKey); 851 return true; // return to "calling" state 852 } 853 path.remove(path.size()-1); // remove; didn't work out 854 continue; // look at the next edge 855 } 856 if ( t.label.matches(label) ) { 857 path.add(edgeTarget); 858 /* 859 System.out.println("found label "+ 860 t.label.toString(dfa.nfa.grammar)+ 861 " at state "+s.stateNumber+"; labelIndex="+labelIndex); 862 */ 863 if ( labelIndex==labels.size()-1 ) { 864 // found last label; done! 865 statesVisitedAtInputDepth.remove(thisStateKey); 866 return true; 867 } 868 // otherwise try to match remaining input 869 boolean found = 870 getNFAPath(edgeTarget, labelIndex+1, labels, path); 871 if ( found ) { 872 statesVisitedAtInputDepth.remove(thisStateKey); 873 return true; 874 } 875 /* 876 System.out.println("backtrack; path from "+s.stateNumber+"->"+ 877 t.label.toString(dfa.nfa.grammar)+" didn't work"); 878 */ 879 path.remove(path.size()-1); // remove; didn't work out 880 continue; // keep looking for a path for labels 881 } 882 } 883 //System.out.println("no epsilon or matching edge; removing "+thisStateKey); 884 // no edge was found matching label; is ok, some state will have it 885 statesVisitedAtInputDepth.remove(thisStateKey); 886 return false; 887 } 888 getStateLabelIndexKey(int s, int i)889 protected String getStateLabelIndexKey(int s, int i) { 890 StringBuffer buf = new StringBuffer(); 891 buf.append(s); 892 buf.append('_'); 893 buf.append(i); 894 return buf.toString(); 895 } 896 897 /** From an alt number associated with artificial Tokens rule, return 898 * the name of the token that is associated with that alt. 899 */ getTokenNameForTokensRuleAlt(int alt)900 public String getTokenNameForTokensRuleAlt(int alt) { 901 NFAState decisionState = dfa.getNFADecisionStartState(); 902 NFAState altState = 903 dfa.nfa.grammar.getNFAStateForAltOfDecision(decisionState,alt); 904 NFAState decisionLeft = (NFAState)altState.transition[0].target; 905 RuleClosureTransition ruleCallEdge = 906 (RuleClosureTransition)decisionLeft.transition[0]; 907 NFAState ruleStartState = (NFAState)ruleCallEdge.target; 908 //System.out.println("alt = "+decisionLeft.getEnclosingRule()); 909 return ruleStartState.enclosingRule.name; 910 } 911 reset()912 public void reset() { 913 stateToRecursionOverflowConfigurationsMap.clear(); 914 } 915 } 916