org.antlr.v4.runtime.atn.ATN Maven / Gradle / Ivy
/*
* Copyright (c) 2012 The ANTLR Project. All rights reserved.
* Use of this file is governed by the BSD-3-Clause license that
* can be found in the LICENSE.txt file in the project root.
*/
package org.antlr.v4.runtime.atn;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.RuleContext;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.dfa.DFA;
import org.antlr.v4.runtime.misc.Args;
import org.antlr.v4.runtime.misc.IntervalSet;
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.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/** */
public class ATN {
public static final int INVALID_ALT_NUMBER = 0;
@NotNull
public final List states = new ArrayList();
/** Each subrule/rule is a decision point and we must track them so we
* can go back later and build DFA predictors for them. This includes
* all the rules, subrules, optional blocks, ()+, ()* etc...
*/
@NotNull
public final List decisionToState = new ArrayList();
/**
* Maps from rule index to starting state number.
*/
public RuleStartState[] ruleToStartState;
/**
* Maps from rule index to stop state number.
*/
public RuleStopState[] ruleToStopState;
@NotNull
public final Map modeNameToStartState =
new LinkedHashMap();
/**
* The type of the ATN.
*/
public final ATNType grammarType;
/**
* The maximum value for any symbol recognized by a transition in the ATN.
*/
public final int maxTokenType;
private boolean hasUnicodeSMPTransitions;
/**
* For lexer ATNs, this maps the rule index to the resulting token type.
* For parser ATNs, this maps the rule index to the generated bypass token
* type if the
* {@link ATNDeserializationOptions#isGenerateRuleBypassTransitions}
* deserialization option was specified; otherwise, this is {@code null}.
*/
public int[] ruleToTokenType;
/**
* For lexer ATNs, this is an array of {@link LexerAction} objects which may
* be referenced by action transitions in the ATN.
*/
public LexerAction[] lexerActions;
@NotNull
public final List modeToStartState = new ArrayList();
private final ConcurrentMap contextCache =
new ConcurrentHashMap();
@NotNull
public DFA[] decisionToDFA = new DFA[0];
@NotNull
public DFA[] modeToDFA = new DFA[0];
protected final ConcurrentMap LL1Table = new ConcurrentHashMap();
/** Used for runtime deserialization of ATNs from strings */
public ATN(@NotNull ATNType grammarType, int maxTokenType) {
this.grammarType = grammarType;
this.maxTokenType = maxTokenType;
}
public final void clearDFA() {
decisionToDFA = new DFA[decisionToState.size()];
for (int i = 0; i < decisionToDFA.length; i++) {
decisionToDFA[i] = new DFA(decisionToState.get(i), i);
}
modeToDFA = new DFA[modeToStartState.size()];
for (int i = 0; i < modeToDFA.length; i++) {
modeToDFA[i] = new DFA(modeToStartState.get(i));
}
contextCache.clear();
LL1Table.clear();
}
public int getContextCacheSize() {
return contextCache.size();
}
public PredictionContext getCachedContext(PredictionContext context) {
return PredictionContext.getCachedContext(context, contextCache, new PredictionContext.IdentityHashMap());
}
public final DFA[] getDecisionToDFA() {
assert decisionToDFA != null && decisionToDFA.length == decisionToState.size();
return decisionToDFA;
}
/** Compute the set of valid tokens that can occur starting in state {@code s}.
* If {@code ctx} is {@link PredictionContext#EMPTY_LOCAL}, the set of tokens will not include what can follow
* the rule surrounding {@code s}. In other words, the set will be
* restricted to tokens reachable staying within {@code s}'s rule.
*/
@NotNull
public IntervalSet nextTokens(ATNState s, @NotNull PredictionContext ctx) {
Args.notNull("ctx", ctx);
LL1Analyzer anal = new LL1Analyzer(this);
IntervalSet next = anal.LOOK(s, ctx);
return next;
}
/**
* Compute the set of valid tokens that can occur starting in {@code s} and
* staying in same rule. {@link Token#EPSILON} is in set if we reach end of
* rule.
*/
@NotNull
public IntervalSet nextTokens(@NotNull ATNState s) {
if ( s.nextTokenWithinRule != null ) return s.nextTokenWithinRule;
s.nextTokenWithinRule = nextTokens(s, PredictionContext.EMPTY_LOCAL);
s.nextTokenWithinRule.setReadonly(true);
return s.nextTokenWithinRule;
}
public void addState(@Nullable ATNState state) {
if (state != null) {
state.atn = this;
state.stateNumber = states.size();
}
states.add(state);
}
public void removeState(@NotNull ATNState state) {
states.set(state.stateNumber, null); // just free mem, don't shift states in list
}
public void defineMode(@NotNull String name, @NotNull TokensStartState s) {
modeNameToStartState.put(name, s);
modeToStartState.add(s);
modeToDFA = Arrays.copyOf(modeToDFA, modeToStartState.size());
modeToDFA[modeToDFA.length - 1] = new DFA(s);
defineDecisionState(s);
}
public int defineDecisionState(@NotNull DecisionState s) {
decisionToState.add(s);
s.decision = decisionToState.size()-1;
decisionToDFA = Arrays.copyOf(decisionToDFA, decisionToState.size());
decisionToDFA[decisionToDFA.length - 1] = new DFA(s, s.decision);
return s.decision;
}
public DecisionState getDecisionState(int decision) {
if ( !decisionToState.isEmpty() ) {
return decisionToState.get(decision);
}
return null;
}
public int getNumberOfDecisions() {
return decisionToState.size();
}
/**
* Computes the set of input symbols which could follow ATN state number
* {@code stateNumber} in the specified full {@code context}. This method
* considers the complete parser context, but does not evaluate semantic
* predicates (i.e. all predicates encountered during the calculation are
* assumed true). If a path in the ATN exists from the starting state to the
* {@link RuleStopState} of the outermost context without matching any
* symbols, {@link Token#EOF} is added to the returned set.
*
* If {@code context} is {@code null}, it is treated as
* {@link ParserRuleContext#EMPTY}.
*
* Note that this does NOT give you the set of all tokens that could
* appear at a given token position in the input phrase. In other words, it
* does not answer:
*
* "Given a specific partial input phrase, return the set of all
* tokens that can follow the last token in the input phrase."
*
* The big difference is that with just the input, the parser could land
* right in the middle of a lookahead decision. Getting all
* possible tokens given a partial input stream is a separate
* computation. See https://github.com/antlr/antlr4/issues/1428
*
* For this function, we are specifying an ATN state and call stack to
* compute what token(s) can come next and specifically: outside of a
* lookahead decision. That is what you want for error reporting and
* recovery upon parse error.
*
* @param stateNumber the ATN state number
* @param context the full parse context
* @return The set of potentially valid input symbols which could follow the
* specified state in the specified context.
* @throws IllegalArgumentException if the ATN does not contain a state with
* number {@code stateNumber}
*/
@NotNull
public IntervalSet getExpectedTokens(int stateNumber, @Nullable RuleContext context) {
if (stateNumber < 0 || stateNumber >= states.size()) {
throw new IllegalArgumentException("Invalid state number.");
}
RuleContext ctx = context;
ATNState s = states.get(stateNumber);
IntervalSet following = nextTokens(s);
if (!following.contains(Token.EPSILON)) {
return following;
}
IntervalSet expected = new IntervalSet();
expected.addAll(following);
expected.remove(Token.EPSILON);
while (ctx != null && ctx.invokingState >= 0 && following.contains(Token.EPSILON)) {
ATNState invokingState = states.get(ctx.invokingState);
RuleTransition rt = (RuleTransition)invokingState.transition(0);
following = nextTokens(rt.followState);
expected.addAll(following);
expected.remove(Token.EPSILON);
ctx = ctx.parent;
}
if (following.contains(Token.EPSILON)) {
expected.add(Token.EOF);
}
return expected;
}
public boolean hasUnicodeSMPTransitions() {
return hasUnicodeSMPTransitions;
}
public void setHasUnicodeSMPTransitions(boolean value) {
hasUnicodeSMPTransitions = value;
}
}