/*
* [The "BSD license"]
* Copyright (c) 2012 Terence Parr
* Copyright (c) 2012 Sam Harwell
* 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.v4.runtime.dfa;
import org.antlr.v4.runtime.Parser;
import org.antlr.v4.runtime.Vocabulary;
import org.antlr.v4.runtime.VocabularyImpl;
import org.antlr.v4.runtime.atn.ATNConfigSet;
import org.antlr.v4.runtime.atn.DecisionState;
import org.antlr.v4.runtime.misc.NotNull;
import org.antlr.v4.runtime.misc.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class DFA {
/** A set of all DFA states. Use {@link Map} so we can get old state back
* ({@link Set} only allows you to see if it's there).
*/
@NotNull
public final Map<DFAState, DFAState> states = new HashMap<DFAState, DFAState>();
@Nullable
public volatile DFAState s0;
public final int decision;
/** From which ATN state did we create this DFA? */
@NotNull
public final DecisionState atnStartState;
/**
* {@code true} if this DFA is for a precedence decision; otherwise,
* {@code false}. This is the backing field for {@link #isPrecedenceDfa},
* {@link #setPrecedenceDfa}.
*/
private volatile boolean precedenceDfa;
public DFA(@NotNull DecisionState atnStartState) {
this(atnStartState, 0);
}
public DFA(@NotNull DecisionState atnStartState, int decision) {
this.atnStartState = atnStartState;
this.decision = decision;
}
/**
* Gets whether this DFA is a precedence DFA. Precedence DFAs use a special
* start state {@link #s0} which is not stored in {@link #states}. The
* {@link DFAState#edges} array for this start state contains outgoing edges
* supplying individual start states corresponding to specific precedence
* values.
*
* @return {@code true} if this is a precedence DFA; otherwise,
* {@code false}.
* @see Parser#getPrecedence()
*/
public final boolean isPrecedenceDfa() {
return precedenceDfa;
}
/**
* Get the start state for a specific precedence value.
*
* @param precedence The current precedence.
* @return The start state corresponding to the specified precedence, or
* {@code null} if no start state exists for the specified precedence.
*
* @throws IllegalStateException if this is not a precedence DFA.
* @see #isPrecedenceDfa()
*/
@SuppressWarnings("null")
public final DFAState getPrecedenceStartState(int precedence) {
if (!isPrecedenceDfa()) {
throw new IllegalStateException("Only precedence DFAs may contain a precedence start state.");
}
// s0.edges is never null for a precedence DFA
if (precedence < 0 || precedence >= s0.edges.length) {
return null;
}
return s0.edges[precedence];
}
/**
* Set the start state for a specific precedence value.
*
* @param precedence The current precedence.
* @param startState The start state corresponding to the specified
* precedence.
*
* @throws IllegalStateException if this is not a precedence DFA.
* @see #isPrecedenceDfa()
*/
@SuppressWarnings({"SynchronizeOnNonFinalField", "null"})
public final void setPrecedenceStartState(int precedence, DFAState startState) {
if (!isPrecedenceDfa()) {
throw new IllegalStateException("Only precedence DFAs may contain a precedence start state.");
}
if (precedence < 0) {
return;
}
// synchronization on s0 here is ok. when the DFA is turned into a
// precedence DFA, s0 will be initialized once and not updated again
synchronized (s0) {
// s0.edges is never null for a precedence DFA
if (precedence >= s0.edges.length) {
s0.edges = Arrays.copyOf(s0.edges, precedence + 1);
}
s0.edges[precedence] = startState;
}
}
/**
* Sets whether this is a precedence DFA. If the specified value differs
* from the current DFA configuration, the following actions are taken;
* otherwise no changes are made to the current DFA.
*
* <ul>
* <li>The {@link #states} map is cleared</li>
* <li>If {@code precedenceDfa} is {@code false}, the initial state
* {@link #s0} is set to {@code null}; otherwise, it is initialized to a new
* {@link DFAState} with an empty outgoing {@link DFAState#edges} array to
* store the start states for individual precedence values.</li>
* <li>The {@link #precedenceDfa} field is updated</li>
* </ul>
*
* @param precedenceDfa {@code true} if this is a precedence DFA; otherwise,
* {@code false}
*/
public final synchronized void setPrecedenceDfa(boolean precedenceDfa) {
if (this.precedenceDfa != precedenceDfa) {
this.states.clear();
if (precedenceDfa) {
DFAState precedenceState = new DFAState(new ATNConfigSet());
precedenceState.edges = new DFAState[0];
precedenceState.isAcceptState = false;
precedenceState.requiresFullContext = false;
this.s0 = precedenceState;
}
else {
this.s0 = null;
}
this.precedenceDfa = precedenceDfa;
}
}
/**
* Return a list of all states in this DFA, ordered by state number.
*/
@NotNull
public List<DFAState> getStates() {
List<DFAState> result = new ArrayList<DFAState>(states.keySet());
Collections.sort(result, new Comparator<DFAState>() {
@Override
public int compare(DFAState o1, DFAState o2) {
return o1.stateNumber - o2.stateNumber;
}
});
return result;
}
@Override
public String toString() { return toString(VocabularyImpl.EMPTY_VOCABULARY); }
/**
* @deprecated Use {@link #toString(Vocabulary)} instead.
*/
@Deprecated
public String toString(@Nullable String[] tokenNames) {
if ( s0==null ) return "";
DFASerializer serializer = new DFASerializer(this,tokenNames);
return serializer.toString();
}
public String toString(@NotNull Vocabulary vocabulary) {
if (s0 == null) {
return "";
}
DFASerializer serializer = new DFASerializer(this, vocabulary);
return serializer.toString();
}
public String toLexerString() {
if ( s0==null ) return "";
DFASerializer serializer = new LexerDFASerializer(this);
return serializer.toString();
}
}