/*
* Copyright (c) 2012-2017 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.misc.AbstractEqualityComparator;
import org.antlr.v4.runtime.misc.Array2DHashSet;
import org.antlr.v4.runtime.misc.DoubleKeyMap;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
/**
* Specialized {@link Set}{@code <}{@link ATNConfig}{@code >} that can track
* info about the set, with support for combining similar configurations using a
* graph-structured stack.
*/
public class ATNConfigSet implements Set<ATNConfig> {
The reason that we need this is because we don't want the hash map to use the standard hash code and equals. We need all configurations with the same (s,i,_,semctx)
to be equal. Unfortunately, this key effectively doubles the number of objects associated with ATNConfigs. The other solution is to use a hash table that lets us specify the equals/hashcode operation. /**
* The reason that we need this is because we don't want the hash map to use
* the standard hash code and equals. We need all configurations with the same
* {@code (s,i,_,semctx)} to be equal. Unfortunately, this key effectively doubles
* the number of objects associated with ATNConfigs. The other solution is to
* use a hash table that lets us specify the equals/hashcode operation.
*/
public static class ConfigHashSet extends AbstractConfigHashSet {
public ConfigHashSet() {
super(ConfigEqualityComparator.INSTANCE);
}
}
public static final class ConfigEqualityComparator extends AbstractEqualityComparator<ATNConfig> {
public static final ConfigEqualityComparator INSTANCE = new ConfigEqualityComparator();
private ConfigEqualityComparator() {
}
@Override
public int hashCode(ATNConfig o) {
int hashCode = 7;
hashCode = 31 * hashCode + o.state.stateNumber;
hashCode = 31 * hashCode + o.alt;
hashCode = 31 * hashCode + o.semanticContext.hashCode();
return hashCode;
}
@Override
public boolean equals(ATNConfig a, ATNConfig b) {
if ( a==b ) return true;
if ( a==null || b==null ) return false;
return a.state.stateNumber==b.state.stateNumber
&& a.alt==b.alt
&& a.semanticContext.equals(b.semanticContext);
}
}
Indicates that the set of configurations is read-only. Do not
allow any code to manipulate the set; DFA states will point at
the sets and they must not change. This does not protect the other
fields; in particular, conflictingAlts is set after
we've made this readonly.
/** Indicates that the set of configurations is read-only. Do not
* allow any code to manipulate the set; DFA states will point at
* the sets and they must not change. This does not protect the other
* fields; in particular, conflictingAlts is set after
* we've made this readonly.
*/
protected boolean readonly = false;
All configs but hashed by (s, i, _, pi) not including context. Wiped out
when we go readonly as this set becomes a DFA state.
/**
* All configs but hashed by (s, i, _, pi) not including context. Wiped out
* when we go readonly as this set becomes a DFA state.
*/
public AbstractConfigHashSet configLookup;
Track the elements as they are added to the set; supports get(i) /** Track the elements as they are added to the set; supports get(i) */
public final ArrayList<ATNConfig> configs = new ArrayList<ATNConfig>(7);
// TODO: these fields make me pretty uncomfortable but nice to pack up info together, saves recomputation
// TODO: can we track conflicts as they are added to save scanning configs later?
public int uniqueAlt;
Currently this is only used when we detect SLL conflict; this does
not necessarily represent the ambiguous alternatives. In fact,
I should also point out that this seems to include predicated alternatives
that have predicates that evaluate to false. Computed in computeTargetState().
/** Currently this is only used when we detect SLL conflict; this does
* not necessarily represent the ambiguous alternatives. In fact,
* I should also point out that this seems to include predicated alternatives
* that have predicates that evaluate to false. Computed in computeTargetState().
*/
protected BitSet conflictingAlts;
// Used in parser and lexer. In lexer, it indicates we hit a pred
// while computing a closure operation. Don't make a DFA state from this.
public boolean hasSemanticContext;
public boolean dipsIntoOuterContext;
Indicates that this configuration set is part of a full context
LL prediction. It will be used to determine how to merge $. With SLL
it's a wildcard whereas it is not for LL context merge.
/** Indicates that this configuration set is part of a full context
* LL prediction. It will be used to determine how to merge $. With SLL
* it's a wildcard whereas it is not for LL context merge.
*/
public final boolean fullCtx;
private int cachedHashCode = -1;
public ATNConfigSet(boolean fullCtx) {
configLookup = new ConfigHashSet();
this.fullCtx = fullCtx;
}
public ATNConfigSet() { this(true); }
public ATNConfigSet(ATNConfigSet old) {
this(old.fullCtx);
addAll(old);
this.uniqueAlt = old.uniqueAlt;
this.conflictingAlts = old.conflictingAlts;
this.hasSemanticContext = old.hasSemanticContext;
this.dipsIntoOuterContext = old.dipsIntoOuterContext;
}
@Override
public boolean add(ATNConfig config) {
return add(config, null);
}
Adding a new config means merging contexts with existing configs for (s, i, pi, _)
, where s
is the ATNConfig.state
, i
is the ATNConfig.alt
, and pi
is the ATNConfig.semanticContext
. We use (s,i,pi)
as key. This method updates dipsIntoOuterContext
and hasSemanticContext
when necessary.
/**
* Adding a new config means merging contexts with existing configs for
* {@code (s, i, pi, _)}, where {@code s} is the
* {@link ATNConfig#state}, {@code i} is the {@link ATNConfig#alt}, and
* {@code pi} is the {@link ATNConfig#semanticContext}. We use
* {@code (s,i,pi)} as key.
*
* <p>This method updates {@link #dipsIntoOuterContext} and
* {@link #hasSemanticContext} when necessary.</p>
*/
public boolean add(
ATNConfig config,
DoubleKeyMap<PredictionContext,PredictionContext,PredictionContext> mergeCache)
{
if ( readonly ) throw new IllegalStateException("This set is readonly");
if ( config.semanticContext!=SemanticContext.NONE ) {
hasSemanticContext = true;
}
if (config.getOuterContextDepth() > 0) {
dipsIntoOuterContext = true;
}
ATNConfig existing = configLookup.getOrAdd(config);
if ( existing==config ) { // we added this new one
cachedHashCode = -1;
configs.add(config); // track order here
return true;
}
// a previous (s,i,pi,_), merge with it and save result
boolean rootIsWildcard = !fullCtx;
PredictionContext merged =
PredictionContext.merge(existing.context, config.context, rootIsWildcard, mergeCache);
// no need to check for existing.context, config.context in cache
// since only way to create new graphs is "call rule" and here. We
// cache at both places.
existing.reachesIntoOuterContext =
Math.max(existing.reachesIntoOuterContext, config.reachesIntoOuterContext);
// make sure to preserve the precedence filter suppression during the merge
if (config.isPrecedenceFilterSuppressed()) {
existing.setPrecedenceFilterSuppressed(true);
}
existing.context = merged; // replace context; no need to alt mapping
return true;
}
Return a List holding list of configs /** Return a List holding list of configs */
public List<ATNConfig> elements() { return configs; }
public Set<ATNState> getStates() {
Set<ATNState> states = new HashSet<ATNState>();
for (ATNConfig c : configs) {
states.add(c.state);
}
return states;
}
Gets the complete set of represented alternatives for the configuration
set.
Returns: the set of represented alternatives in this configuration set Since: 4.3
/**
* Gets the complete set of represented alternatives for the configuration
* set.
*
* @return the set of represented alternatives in this configuration set
*
* @since 4.3
*/
public BitSet getAlts() {
BitSet alts = new BitSet();
for (ATNConfig config : configs) {
alts.set(config.alt);
}
return alts;
}
public List<SemanticContext> getPredicates() {
List<SemanticContext> preds = new ArrayList<SemanticContext>();
for (ATNConfig c : configs) {
if ( c.semanticContext!=SemanticContext.NONE ) {
preds.add(c.semanticContext);
}
}
return preds;
}
public ATNConfig get(int i) { return configs.get(i); }
public void optimizeConfigs(ATNSimulator interpreter) {
if ( readonly ) throw new IllegalStateException("This set is readonly");
if ( configLookup.isEmpty() ) return;
for (ATNConfig config : configs) {
// int before = PredictionContext.getAllContextNodes(config.context).size();
config.context = interpreter.getCachedContext(config.context);
// int after = PredictionContext.getAllContextNodes(config.context).size();
// System.out.println("configs "+before+"->"+after);
}
}
@Override
public boolean addAll(Collection<? extends ATNConfig> coll) {
for (ATNConfig c : coll) add(c);
return false;
}
@Override
public boolean equals(Object o) {
if (o == this) {
return true;
}
else if (!(o instanceof ATNConfigSet)) {
return false;
}
// System.out.print("equals " + this + ", " + o+" = ");
ATNConfigSet other = (ATNConfigSet)o;
boolean same = configs!=null &&
configs.equals(other.configs) && // includes stack context
this.fullCtx == other.fullCtx &&
this.uniqueAlt == other.uniqueAlt &&
this.conflictingAlts == other.conflictingAlts &&
this.hasSemanticContext == other.hasSemanticContext &&
this.dipsIntoOuterContext == other.dipsIntoOuterContext;
// System.out.println(same);
return same;
}
@Override
public int hashCode() {
if (isReadonly()) {
if (cachedHashCode == -1) {
cachedHashCode = configs.hashCode();
}
return cachedHashCode;
}
return configs.hashCode();
}
@Override
public int size() {
return configs.size();
}
@Override
public boolean isEmpty() {
return configs.isEmpty();
}
@Override
public boolean contains(Object o) {
if (configLookup == null) {
throw new UnsupportedOperationException("This method is not implemented for readonly sets.");
}
return configLookup.contains(o);
}
public boolean containsFast(ATNConfig obj) {
if (configLookup == null) {
throw new UnsupportedOperationException("This method is not implemented for readonly sets.");
}
return configLookup.containsFast(obj);
}
@Override
public Iterator<ATNConfig> iterator() {
return configs.iterator();
}
@Override
public void clear() {
if ( readonly ) throw new IllegalStateException("This set is readonly");
configs.clear();
cachedHashCode = -1;
configLookup.clear();
}
public boolean isReadonly() {
return readonly;
}
public void setReadonly(boolean readonly) {
this.readonly = readonly;
configLookup = null; // can't mod, no need for lookup cache
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder();
buf.append(elements().toString());
if ( hasSemanticContext ) buf.append(",hasSemanticContext=").append(hasSemanticContext);
if ( uniqueAlt!=ATN.INVALID_ALT_NUMBER ) buf.append(",uniqueAlt=").append(uniqueAlt);
if ( conflictingAlts!=null ) buf.append(",conflictingAlts=").append(conflictingAlts);
if ( dipsIntoOuterContext ) buf.append(",dipsIntoOuterContext");
return buf.toString();
}
// satisfy interface
@Override
public ATNConfig[] toArray() {
return configLookup.toArray();
}
@Override
public <T> T[] toArray(T[] a) {
return configLookup.toArray(a);
}
@Override
public boolean remove(Object o) {
throw new UnsupportedOperationException();
}
@Override
public boolean containsAll(Collection<?> c) {
throw new UnsupportedOperationException();
}
@Override
public boolean retainAll(Collection<?> c) {
throw new UnsupportedOperationException();
}
@Override
public boolean removeAll(Collection<?> c) {
throw new UnsupportedOperationException();
}
public static abstract class AbstractConfigHashSet extends Array2DHashSet<ATNConfig> {
public AbstractConfigHashSet(AbstractEqualityComparator<? super ATNConfig> comparator) {
this(comparator, 16, 2);
}
public AbstractConfigHashSet(AbstractEqualityComparator<? super ATNConfig> comparator, int initialCapacity, int initialBucketCapacity) {
super(comparator, initialCapacity, initialBucketCapacity);
}
@Override
protected final ATNConfig asElementType(Object o) {
if (!(o instanceof ATNConfig)) {
return null;
}
return (ATNConfig)o;
}
@Override
protected final ATNConfig[][] createBuckets(int capacity) {
return new ATNConfig[capacity][];
}
@Override
protected final ATNConfig[] createBucket(int capacity) {
return new ATNConfig[capacity];
}
}
}