blob: 30796fefe12949b2213b5a16ac6d05bca0cec650 [file] [log] [blame] [edit]
/* 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.Text;
using Antlr4.Runtime.Misc;
using Antlr4.Runtime.Sharpen;
namespace Antlr4.Runtime.Atn
{
public abstract class PredictionContext
{
public static readonly int EMPTY_RETURN_STATE = int.MaxValue;
private static readonly int INITIAL_HASH = 1;
protected internal static int CalculateEmptyHashCode()
{
int hash = MurmurHash.Initialize(INITIAL_HASH);
hash = MurmurHash.Finish(hash, 0);
return hash;
}
protected internal static int CalculateHashCode(PredictionContext parent, int returnState)
{
int hash = MurmurHash.Initialize(INITIAL_HASH);
hash = MurmurHash.Update(hash, parent);
hash = MurmurHash.Update(hash, returnState);
hash = MurmurHash.Finish(hash, 2);
return hash;
}
protected internal static int CalculateHashCode(PredictionContext[] parents, int[] returnStates)
{
int hash = MurmurHash.Initialize(INITIAL_HASH);
foreach (PredictionContext parent in parents)
{
hash = MurmurHash.Update(hash, parent);
}
foreach (int returnState in returnStates)
{
hash = MurmurHash.Update(hash, returnState);
}
hash = MurmurHash.Finish(hash, 2 * parents.Length);
return hash;
}
private readonly int cachedHashCode;
protected internal PredictionContext(int cachedHashCode)
{
this.cachedHashCode = cachedHashCode;
}
public static PredictionContext FromRuleContext(ATN atn, RuleContext outerContext)
{
if (outerContext == null)
outerContext = ParserRuleContext.EMPTY;
if (outerContext.Parent == null || outerContext == ParserRuleContext.EMPTY)
return EmptyPredictionContext.Instance;
PredictionContext parent = PredictionContext.FromRuleContext(atn, outerContext.Parent);
ATNState state = atn.states[outerContext.invokingState];
RuleTransition transition = (RuleTransition)state.Transition(0);
return parent.GetChild(transition.followState.stateNumber);
}
public abstract int Size
{
get;
}
public abstract PredictionContext GetParent(int index);
public abstract int GetReturnState(int index);
public virtual bool IsEmpty
{
get
{
return this == EmptyPredictionContext.Instance;
}
}
public virtual bool HasEmptyPath
{
get
{
return GetReturnState(Size - 1) == EMPTY_RETURN_STATE;
}
}
public sealed override int GetHashCode()
{
return cachedHashCode;
}
internal static PredictionContext Merge(PredictionContext a, PredictionContext b, bool rootIsWildcard, MergeCache mergeCache)
{
if (a == b || a.Equals(b))
{
return a;
}
if (a is SingletonPredictionContext && b is SingletonPredictionContext)
{
return MergeSingletons((SingletonPredictionContext)a,
(SingletonPredictionContext)b,
rootIsWildcard, mergeCache);
}
// At least one of a or b is array
// If one is $ and rootIsWildcard, return $ as * wildcard
if (rootIsWildcard)
{
if (a is EmptyPredictionContext)
return a;
if (b is EmptyPredictionContext)
return b;
}
// convert singleton so both are arrays to normalize
if (a is SingletonPredictionContext)
{
a = new ArrayPredictionContext((SingletonPredictionContext)a);
}
if (b is SingletonPredictionContext)
{
b = new ArrayPredictionContext((SingletonPredictionContext)b);
}
return MergeArrays((ArrayPredictionContext)a, (ArrayPredictionContext)b,
rootIsWildcard, mergeCache);
}
public static PredictionContext MergeSingletons(
SingletonPredictionContext a,
SingletonPredictionContext b,
bool rootIsWildcard,
MergeCache mergeCache)
{
if (mergeCache != null)
{
PredictionContext previous = mergeCache.Get(a, b);
if (previous != null) return previous;
previous = mergeCache.Get(b, a);
if (previous != null) return previous;
}
PredictionContext rootMerge = MergeRoot(a, b, rootIsWildcard);
if (rootMerge != null)
{
if (mergeCache != null) mergeCache.Put(a, b, rootMerge);
return rootMerge;
}
if (a.returnState == b.returnState)
{ // a == b
PredictionContext parent = Merge(a.parent, b.parent, rootIsWildcard, mergeCache);
// if parent is same as existing a or b parent or reduced to a parent, return it
if (parent == a.parent) return a; // ax + bx = ax, if a=b
if (parent == b.parent) return b; // ax + bx = bx, if a=b
// else: ax + ay = a'[x,y]
// merge parents x and y, giving array node with x,y then remainders
// of those graphs. dup a, a' points at merged array
// new joined parent so create new singleton pointing to it, a'
PredictionContext a_ = SingletonPredictionContext.Create(parent, a.returnState);
if (mergeCache != null) mergeCache.Put(a, b, a_);
return a_;
}
else { // a != b payloads differ
// see if we can collapse parents due to $+x parents if local ctx
int[] payloads = new int[2];
PredictionContext[] parents = new PredictionContext[2];
PredictionContext pc;
PredictionContext singleParent = null;
if (a == b || (a.parent != null && a.parent.Equals(b.parent)))
{ // ax + bx = [a,b]x
singleParent = a.parent;
}
if (singleParent != null)
{ // parents are same
// sort payloads and use same parent
if (a.returnState > b.returnState)
{
payloads[0] = b.returnState;
payloads[1] = a.returnState;
}
else {
payloads[0] = a.returnState;
payloads[1] = b.returnState;
}
parents[0] = singleParent;
parents[1] = singleParent;
pc = new ArrayPredictionContext(parents, payloads);
if (mergeCache != null)
mergeCache.Put(a, b, pc);
return pc;
}
// parents differ and can't merge them. Just pack together
// into array; can't merge.
// ax + by = [ax,by]
// sort by payload
if (a.returnState > b.returnState)
{
payloads[0] = b.returnState;
payloads[1] = a.returnState;
parents[0] = b.parent;
parents[1] = a.parent;
}
else {
payloads[0] = a.returnState;
payloads[1] = b.returnState;
parents[0] = a.parent;
parents[1] = b.parent;
}
pc = new ArrayPredictionContext(parents, payloads);
if (mergeCache != null)
mergeCache.Put(a, b, pc);
return pc;
}
}
public static PredictionContext MergeArrays(
ArrayPredictionContext a,
ArrayPredictionContext b,
bool rootIsWildcard,
MergeCache mergeCache)
{
if (mergeCache != null)
{
PredictionContext previous = mergeCache.Get(a, b);
if (previous != null) {
if ( ParserATNSimulator.trace_atn_sim ) Console.WriteLine("mergeArrays a="+a+",b="+b+" -> previous");
return previous;
}
previous = mergeCache.Get(b, a);
if (previous != null) {
if ( ParserATNSimulator.trace_atn_sim ) Console.WriteLine("mergeArrays a="+a+",b="+b+" -> previous");
return previous;
}
}
// merge sorted payloads a + b => M
int i = 0; // walks a
int j = 0; // walks b
int k = 0; // walks target M array
int[] mergedReturnStates =
new int[a.returnStates.Length + b.returnStates.Length];
PredictionContext[] mergedParents =
new PredictionContext[a.returnStates.Length + b.returnStates.Length];
// walk and merge to yield mergedParents, mergedReturnStates
while (i < a.returnStates.Length && j < b.returnStates.Length)
{
PredictionContext a_parent = a.parents[i];
PredictionContext b_parent = b.parents[j];
if (a.returnStates[i] == b.returnStates[j])
{
// same payload (stack tops are equal), must yield merged singleton
int payload = a.returnStates[i];
// $+$ = $
bool both_dollar = payload == EMPTY_RETURN_STATE &&
a_parent == null && b_parent == null;
bool ax_ax = (a_parent != null && b_parent != null) &&
a_parent.Equals(b_parent); // ax+ax -> ax
if (both_dollar || ax_ax ) {
mergedParents[k] = a_parent; // choose left
mergedReturnStates[k] = payload;
}
else { // ax+ay -> a'[x,y]
PredictionContext mergedParent =
Merge(a_parent, b_parent, rootIsWildcard, mergeCache);
mergedParents[k] = mergedParent;
mergedReturnStates[k] = payload;
}
i++; // hop over left one as usual
j++; // but also skip one in right side since we merge
}
else if (a.returnStates[i] < b.returnStates[j])
{ // copy a[i] to M
mergedParents[k] = a_parent;
mergedReturnStates[k] = a.returnStates[i];
i++;
}
else { // b > a, copy b[j] to M
mergedParents[k] = b_parent;
mergedReturnStates[k] = b.returnStates[j];
j++;
}
k++;
}
// copy over any payloads remaining in either array
if (i < a.returnStates.Length)
{
for (int p = i; p < a.returnStates.Length; p++)
{
mergedParents[k] = a.parents[p];
mergedReturnStates[k] = a.returnStates[p];
k++;
}
}
else {
for (int p = j; p < b.returnStates.Length; p++)
{
mergedParents[k] = b.parents[p];
mergedReturnStates[k] = b.returnStates[p];
k++;
}
}
// trim merged if we combined a few that had same stack tops
if (k < mergedParents.Length)
{ // write index < last position; trim
if (k == 1)
{ // for just one merged element, return singleton top
PredictionContext a_ = SingletonPredictionContext.Create(mergedParents[0], mergedReturnStates[0]);
if (mergeCache != null) mergeCache.Put(a, b, a_);
return a_;
}
mergedParents = Arrays.CopyOf(mergedParents, k);
mergedReturnStates = Arrays.CopyOf(mergedReturnStates, k);
}
PredictionContext M = new ArrayPredictionContext(mergedParents, mergedReturnStates);
// if we created same array as a or b, return that instead
// TODO: track whether this is possible above during merge sort for speed
if (M.Equals(a))
{
if (mergeCache != null)
mergeCache.Put(a, b, a);
if ( ParserATNSimulator.trace_atn_sim ) Console.WriteLine("mergeArrays a="+a+",b="+b+" -> a");
return a;
}
if (M.Equals(b))
{
if (mergeCache != null)
mergeCache.Put(a, b, b);
if ( ParserATNSimulator.trace_atn_sim ) Console.WriteLine("mergeArrays a="+a+",b="+b+" -> b");
return b;
}
CombineCommonParents(mergedParents);
if ( ParserATNSimulator.trace_atn_sim ) Console.WriteLine("mergeArrays a="+a+",b="+b+" -> "+M);
if (mergeCache != null)
mergeCache.Put(a, b, M);
return M;
}
protected static void CombineCommonParents(PredictionContext[] parents)
{
Dictionary<PredictionContext, PredictionContext> uniqueParents = new Dictionary<PredictionContext, PredictionContext>();
for (int p = 0; p < parents.Length; p++)
{
PredictionContext parent = parents[p];
if (parent!=null && !uniqueParents.ContainsKey(parent))
{ // don't replace
uniqueParents.Put(parent, parent);
}
}
for (int p = 0; p < parents.Length; p++)
{
PredictionContext parent = parents[p];
if (parent!=null)
parents[p] = uniqueParents.Get(parent);
}
}
public static PredictionContext MergeRoot(SingletonPredictionContext a,
SingletonPredictionContext b,
bool rootIsWildcard)
{
if (rootIsWildcard)
{
if (a == EmptyPredictionContext.Instance)
return EmptyPredictionContext.Instance; // * + b = *
if (b == EmptyPredictionContext.Instance)
return EmptyPredictionContext.Instance; // a + * = *
}
else {
if (a == EmptyPredictionContext.Instance && b == EmptyPredictionContext.Instance) return EmptyPredictionContext.Instance; // $ + $ = $
if (a == EmptyPredictionContext.Instance)
{ // $ + x = [$,x]
int[] payloads = { b.returnState, EMPTY_RETURN_STATE };
PredictionContext[] parents = { b.parent, null };
PredictionContext joined =
new ArrayPredictionContext(parents, payloads);
return joined;
}
if (b == EmptyPredictionContext.Instance)
{ // x + $ = [$,x] ($ is always first if present)
int[] payloads = { a.returnState, EMPTY_RETURN_STATE };
PredictionContext[] parents = { a.parent, null };
PredictionContext joined =
new ArrayPredictionContext(parents, payloads);
return joined;
}
}
return null;
}
public static PredictionContext GetCachedContext(PredictionContext context, PredictionContextCache contextCache, PredictionContext.IdentityHashMap visited)
{
if (context.IsEmpty)
{
return context;
}
PredictionContext existing = visited.Get(context);
if (existing != null)
{
return existing;
}
existing = contextCache.Get(context);
if (existing != null)
{
visited.Put(context, existing);
return existing;
}
bool changed = false;
PredictionContext[] parents = new PredictionContext[context.Size];
for (int i = 0; i < parents.Length; i++)
{
PredictionContext parent = GetCachedContext(context.GetParent(i), contextCache, visited);
if (changed || parent != context.GetParent(i))
{
if (!changed)
{
parents = new PredictionContext[context.Size];
for (int j = 0; j < context.Size; j++)
{
parents[j] = context.GetParent(j);
}
changed = true;
}
parents[i] = parent;
}
}
if (!changed)
{
contextCache.Add(context);
visited.Put(context, context);
return context;
}
PredictionContext updated;
if (parents.Length == 0)
{
updated = EmptyPredictionContext.Instance;
}
else if (parents.Length == 1)
{
updated = SingletonPredictionContext.Create(parents[0], context.GetReturnState(0));
}
else {
ArrayPredictionContext arrayPredictionContext = (ArrayPredictionContext)context;
updated = new ArrayPredictionContext(parents, arrayPredictionContext.returnStates);
}
contextCache.Add(updated);
visited.Put(updated, updated);
visited.Put(context, updated);
return updated;
}
public virtual PredictionContext GetChild(int returnState)
{
return new SingletonPredictionContext(this, returnState);
}
public virtual string[] ToStrings(IRecognizer recognizer, int currentState)
{
return ToStrings(recognizer, EmptyPredictionContext.Instance, currentState);
}
public virtual string[] ToStrings(IRecognizer recognizer, PredictionContext stop, int currentState)
{
List<string> result = new List<string>();
for (int perm = 0; ; perm++)
{
int offset = 0;
bool last = true;
PredictionContext p = this;
int stateNumber = currentState;
StringBuilder localBuffer = new StringBuilder();
localBuffer.Append("[");
while (!p.IsEmpty && p != stop)
{
int index = 0;
if (p.Size > 0)
{
int bits = 1;
while ((1 << bits) < p.Size)
{
bits++;
}
int mask = (1 << bits) - 1;
index = (perm >> offset) & mask;
last &= index >= p.Size - 1;
if (index >= p.Size)
{
goto outer_continue;
}
offset += bits;
}
if (recognizer != null)
{
if (localBuffer.Length > 1)
{
// first char is '[', if more than that this isn't the first rule
localBuffer.Append(' ');
}
ATN atn = recognizer.Atn;
ATNState s = atn.states[stateNumber];
string ruleName = recognizer.RuleNames[s.ruleIndex];
localBuffer.Append(ruleName);
}
else
{
if (p.GetReturnState(index) != EMPTY_RETURN_STATE)
{
if (!p.IsEmpty)
{
if (localBuffer.Length > 1)
{
// first char is '[', if more than that this isn't the first rule
localBuffer.Append(' ');
}
localBuffer.Append(p.GetReturnState(index));
}
}
}
stateNumber = p.GetReturnState(index);
p = p.GetParent(index);
}
localBuffer.Append("]");
result.Add(localBuffer.ToString());
if (last)
{
break;
}
outer_continue:;
}
return result.ToArray();
}
public sealed class IdentityHashMap : Dictionary<PredictionContext, PredictionContext>
{
public IdentityHashMap()
: base(PredictionContext.IdentityEqualityComparator.Instance)
{
}
}
public sealed class IdentityEqualityComparator : EqualityComparer<PredictionContext>
{
public static readonly PredictionContext.IdentityEqualityComparator Instance = new PredictionContext.IdentityEqualityComparator();
private IdentityEqualityComparator()
{
}
public override int GetHashCode(PredictionContext obj)
{
return obj.GetHashCode();
}
public override bool Equals(PredictionContext a, PredictionContext b)
{
return a == b;
}
}
}
}