/* * [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.tool.ErrorManager; import org.antlr.tool.GrammarAST; import org.antlr.tool.Rule; /** A state within an NFA. At most 2 transitions emanate from any NFA state. */ public class NFAState extends State { // I need to distinguish between NFA decision states for (...)* and (...)+ // during NFA interpretation. public static final int LOOPBACK = 1; public static final int BLOCK_START = 2; public static final int OPTIONAL_BLOCK_START = 3; public static final int BYPASS = 4; public static final int RIGHT_EDGE_OF_BLOCK = 5; public static final int MAX_TRANSITIONS = 2; /** How many transitions; 0, 1, or 2 transitions */ int numTransitions = 0; public Transition[] transition = new Transition[MAX_TRANSITIONS]; /** For o-A->o type NFA tranitions, record the label that leads to this * state. Useful for creating rich error messages when we find * insufficiently (with preds) covered states. */ public Label incidentEdgeLabel; /** Which NFA are we in? */ public NFA nfa = null; /** What's its decision number from 1..n? */ protected int decisionNumber = 0; /** Subrules (...)* and (...)+ have more than one decision point in * the NFA created for them. They both have a loop-exit-or-stay-in * decision node (the loop back node). They both have a normal * alternative block decision node at the left edge. The (...)* is * worse as it even has a bypass decision (2 alts: stay in or bypass) * node at the extreme left edge. This is not how they get generated * in code as a while-loop or whatever deals nicely with either. For * error messages (where I need to print the nondeterministic alts) * and for interpretation, I need to use the single DFA that is created * (for efficiency) but interpret the results differently depending * on which of the 2 or 3 decision states uses the DFA. For example, * the DFA will always report alt n+1 as the exit branch for n real * alts, so I need to translate that depending on the decision state. * * If decisionNumber>0 then this var tells you what kind of decision * state it is. */ public int decisionStateType; /** What rule do we live in? */ public Rule enclosingRule; /** During debugging and for nondeterminism warnings, it's useful * to know what relationship this node has to the original grammar. * For example, "start of alt 1 of rule a". */ protected String description; /** Associate this NFAState with the corresponding GrammarAST node * from which this node was created. This is useful not only for * associating the eventual lookahead DFA with the associated * Grammar position, but also for providing users with * nondeterminism warnings. Mainly used by decision states to * report line:col info. Could also be used to track line:col * for elements such as token refs. */ public GrammarAST associatedASTNode; /** Is this state the sole target of an EOT transition? */ protected boolean EOTTargetState = false; /** Jean Bovet needs in the GUI to know which state pairs correspond * to the start/stop of a block. */ public int endOfBlockStateNumber = State.INVALID_STATE_NUMBER; public NFAState(NFA nfa) { this.nfa = nfa; } public int getNumberOfTransitions() { return numTransitions; } public void addTransition(Transition e) { if ( e==null ) { throw new IllegalArgumentException("You can't add a null transition"); } if ( numTransitions>transition.length ) { throw new IllegalArgumentException("You can only have "+transition.length+" transitions"); } if ( e!=null ) { transition[numTransitions] = e; numTransitions++; // Set the "back pointer" of the target state so that it // knows about the label of the incoming edge. Label label = e.label; if ( label.isAtom() || label.isSet() ) { if ( ((NFAState)e.target).incidentEdgeLabel!=null ) { ErrorManager.internalError("Clobbered incident edge"); } ((NFAState)e.target).incidentEdgeLabel = e.label; } } } /** Used during optimization to reset a state to have the (single) * transition another state has. */ public void setTransition0(Transition e) { if ( e==null ) { throw new IllegalArgumentException("You can't use a solitary null transition"); } transition[0] = e; transition[1] = null; numTransitions = 1; } public Transition transition(int i) { return transition[i]; } /** The DFA decision for this NFA decision state always has * an exit path for loops as n+1 for n alts in the loop. * That is really useful for displaying nondeterministic alts * and so on, but for walking the NFA to get a sequence of edge * labels or for actually parsing, we need to get the real alt * number. The real alt number for exiting a loop is always 1 * as transition 0 points at the exit branch (we compute DFAs * always for loops at the loopback state). * * For walking/parsing the loopback state: * 1 2 3 display alt (for human consumption) * 2 3 1 walk alt * * For walking the block start: * 1 2 3 display alt * 1 2 3 * * For walking the bypass state of a (...)* loop: * 1 2 3 display alt * 1 1 2 all block alts map to entering loop exit means take bypass * * Non loop EBNF do not need to be translated; they are ignored by * this method as decisionStateType==0. * * Return same alt if we can't translate. */ public int translateDisplayAltToWalkAlt(int displayAlt) { NFAState nfaStart = this; if ( decisionNumber==0 || decisionStateType==0 ) { return displayAlt; } int walkAlt = 0; // find the NFA loopback state associated with this DFA // and count number of alts (all alt numbers are computed // based upon the loopback's NFA state. /* DFA dfa = nfa.grammar.getLookaheadDFA(decisionNumber); if ( dfa==null ) { ErrorManager.internalError("can't get DFA for decision "+decisionNumber); } */ int nAlts = nfa.grammar.getNumberOfAltsForDecisionNFA(nfaStart); switch ( nfaStart.decisionStateType ) { case LOOPBACK : walkAlt = displayAlt % nAlts + 1; // rotate right mod 1..3 break; case BLOCK_START : case OPTIONAL_BLOCK_START : walkAlt = displayAlt; // identity transformation break; case BYPASS : if ( displayAlt == nAlts ) { walkAlt = 2; // bypass } else { walkAlt = 1; // any non exit branch alt predicts entering } break; } return walkAlt; } // Setter/Getters /** What AST node is associated with this NFAState? When you * set the AST node, I set the node to point back to this NFA state. */ public void setDecisionASTNode(GrammarAST decisionASTNode) { decisionASTNode.setNFAStartState(this); this.associatedASTNode = decisionASTNode; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public int getDecisionNumber() { return decisionNumber; } public void setDecisionNumber(int decisionNumber) { this.decisionNumber = decisionNumber; } public boolean isEOTTargetState() { return EOTTargetState; } public void setEOTTargetState(boolean eot) { EOTTargetState = eot; } public boolean isDecisionState() { return decisionStateType>0; } public String toString() { return String.valueOf(stateNumber); } }