blob: 84e13658a547ed097da7b0c2c5bd8dfc793f38bf [file] [log] [blame]
/*
* [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);
}
}