Repository URL to install this package:
|
Version:
2023.12.1 ▾
|
/* 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.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Antlr4.Runtime.Misc;
using Antlr4.Runtime.Sharpen;
namespace Antlr4.Runtime.Atn
{
public class ATNConfigSet
{
/** 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 bool 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.
*/
public ConfigHashSet configLookup;
/** Track the elements as they are added to the set; supports get(i) */
public 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().
*/
public 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 bool hasSemanticContext;
public bool 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.
*/
public readonly bool fullCtx;
private int cachedHashCode = -1;
public ATNConfigSet(bool fullCtx)
{
configLookup = new ConfigHashSet();
this.fullCtx = fullCtx;
}
public ATNConfigSet()
: this(true)
{
}
public ATNConfigSet(ATNConfigSet old)
: this(old.fullCtx)
{
AddAll(old.configs);
this.uniqueAlt = old.uniqueAlt;
this.conflictingAlts = old.conflictingAlts;
this.hasSemanticContext = old.hasSemanticContext;
this.dipsIntoOuterContext = old.dipsIntoOuterContext;
}
public bool Add(ATNConfig config)
{
return Add(config, null);
}
/**
* 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 bool Add(ATNConfig config, MergeCache mergeCache)
{
if (readOnly)
throw new Exception("This set is readonly");
if (config.semanticContext != SemanticContext.Empty.Instance)
{
hasSemanticContext = true;
}
if (config.OuterContextDepth > 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
bool 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 */
public List<ATNConfig> Elements
{
get
{
return configs;
}
}
public HashSet<ATNState> GetStates()
{
HashSet<ATNState> states = new HashSet<ATNState>();
foreach (ATNConfig c in configs)
{
states.Add(c.state);
}
return states;
}
/**
* 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();
foreach (ATNConfig config in configs)
{
alts.Set(config.alt);
}
return alts;
}
public List<SemanticContext> GetPredicates()
{
List<SemanticContext> preds = new List<SemanticContext>();
foreach (ATNConfig c in configs)
{
if (c.semanticContext != SemanticContext.Empty.Instance)
{
preds.Add(c.semanticContext);
}
}
return preds;
}
public ATNConfig Get(int i) { return configs[i]; }
public void OptimizeConfigs(ATNSimulator interpreter)
{
if (readOnly)
throw new Exception("This set is readonly");
if (configLookup.Count == 0)
return;
foreach (ATNConfig config in 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);
}
}
public bool AddAll(ICollection<ATNConfig> coll)
{
foreach (ATNConfig c in coll) Add(c);
return false;
}
public override bool Equals(Object o)
{
if (o == this)
{
return true;
}
else if (!(o is ATNConfigSet))
{
return false;
}
// System.out.print("equals " + this + ", " + o+" = ");
ATNConfigSet other = (ATNConfigSet)o;
bool 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;
}
public override int GetHashCode()
{
if (IsReadOnly)
{
if (cachedHashCode == -1)
{
cachedHashCode = configs.GetHashCode();
}
return cachedHashCode;
}
return configs.GetHashCode();
}
public int Count
{
get
{
return configs.Count;
}
}
public bool Empty
{
get
{
return configs.Count == 0;
}
}
public bool Contains(Object o)
{
if (configLookup == null)
{
throw new Exception("This method is not implemented for readonly sets.");
}
return configLookup.ContainsKey((ATNConfig)o);
}
public void Clear()
{
if (readOnly)
throw new Exception("This set is readonly");
configs.Clear();
cachedHashCode = -1;
configLookup.Clear();
}
public bool IsReadOnly
{
get
{
return readOnly;
}
set
{
this.readOnly = value;
configLookup = null; // can't mod, no need for lookup cache
}
}
public override String ToString()
{
StringBuilder buf = new StringBuilder();
buf.Append('[');
List<ATNConfig> cfgs = Elements;
if (cfgs.Count > 0)
{
foreach (ATNConfig c in cfgs)
{
buf.Append(c.ToString());
buf.Append(", ");
}
buf.Length = buf.Length - 2;
}
buf.Append(']');
if (hasSemanticContext)
buf.Append(",hasSemanticContext=")
.Append(hasSemanticContext.ToString().ToLower());
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();
}
}
public class OrderedATNConfigSet : ATNConfigSet
{
public OrderedATNConfigSet()
{
this.configLookup = new LexerConfigHashSet();
}
public class LexerConfigHashSet : ConfigHashSet
{
public LexerConfigHashSet()
: base(new ObjectEqualityComparator())
{
}
}
}
public class ObjectEqualityComparator : IEqualityComparer<ATNConfig>
{
public int GetHashCode(ATNConfig o)
{
if (o == null)
return 0;
else
return o.GetHashCode();
}
public bool Equals(ATNConfig a, ATNConfig b)
{
if (a == b) return true;
if (a == null || b == null) return false;
return a.Equals(b);
}
}
/**
* 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 class ConfigHashSet : Dictionary<ATNConfig, ATNConfig>
{
public ConfigHashSet(IEqualityComparer<ATNConfig> comparer)
: base(comparer)
{
}
public ConfigHashSet()
: base(new ConfigEqualityComparator())
{
}
public ATNConfig GetOrAdd(ATNConfig config)
{
ATNConfig existing;
if (this.TryGetValue(config, out existing))
return existing;
else
{
this.Put(config, config);
return config;
}
}
}
public class ConfigEqualityComparator : IEqualityComparer<ATNConfig>
{
public int GetHashCode(ATNConfig o)
{
int hashCode = 7;
hashCode = 31 * hashCode + o.state.stateNumber;
hashCode = 31 * hashCode + o.alt;
hashCode = 31 * hashCode + o.semanticContext.GetHashCode();
return hashCode;
}
public bool 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);
}
}
}