protected virtual ATNConfigSet ComputeReachSet(ATNConfigSet closure, int t, bool fullCtx)
{
if (debug)
Console.WriteLine("in computeReachSet, starting closure: " + closure);
if (mergeCache == null)
{
mergeCache = new MergeCache();
}
ATNConfigSet intermediate = new ATNConfigSet(fullCtx);
/* Configurations already in a rule stop state indicate reaching the end
* of the decision rule (local context) or end of the start rule (full
* context). Once reached, these configurations are never updated by a
* closure operation, so they are handled separately for the performance
* advantage of having a smaller intermediate set when calling closure.
*
* For full-context reach operations, separate handling is required to
* ensure that the alternative matching the longest overall sequence is
* chosen when multiple such configurations can match the input.
*/
List<ATNConfig> skippedStopStates = null;
// First figure out where we can reach on input t
foreach (ATNConfig c in closure.configs)
{
if (debug) Console.WriteLine("testing " + GetTokenName(t) + " at " + c.ToString());
if (c.state is RuleStopState)
{
if (fullCtx || t == IntStreamConstants.EOF)
{
if (skippedStopStates == null)
{
skippedStopStates = new List<ATNConfig>();
}
skippedStopStates.Add(c);
}
continue;
}
int n = c.state.NumberOfTransitions;
for (int ti = 0; ti < n; ti++)
{ // for each transition
Transition trans = c.state.Transition(ti);
ATNState target = GetReachableTarget(trans, t);
if (target != null)
{
intermediate.Add(new ATNConfig(c, target), mergeCache);
}
}
}
// Now figure out where the reach operation can take us...
ATNConfigSet reach = null;
/* This block optimizes the reach operation for intermediate sets which
* trivially indicate a termination state for the overall
* adaptivePredict operation.
*
* The conditions assume that intermediate
* contains all configurations relevant to the reach set, but this
* condition is not true when one or more configurations have been
* withheld in skippedStopStates, or when the current symbol is EOF.
*/
if (skippedStopStates == null && t != TokenConstants.EOF)
{
if (intermediate.Count == 1)
{
// Don't pursue the closure if there is just one state.
// It can only have one alternative; just add to result
// Also don't pursue the closure if there is unique alternative
// among the configurations.
reach = intermediate;
}
else if (GetUniqueAlt(intermediate) != ATN.INVALID_ALT_NUMBER)
{
// Also don't pursue the closure if there is unique alternative
// among the configurations.
reach = intermediate;
}
}
/* If the reach set could not be trivially determined, perform a closure
* operation on the intermediate set to compute its initial value.
*/
if (reach == null)
{
reach = new ATNConfigSet(fullCtx);
HashSet<ATNConfig> closureBusy = new HashSet<ATNConfig>();
bool treatEofAsEpsilon = t == TokenConstants.EOF;
foreach (ATNConfig c in intermediate.configs)
{
Closure(c, reach, closureBusy, false, fullCtx, treatEofAsEpsilon);
}
}
if (t == IntStreamConstants.EOF)
{
/* After consuming EOF no additional input is possible, so we are
* only interested in configurations which reached the end of the
* decision rule (local context) or end of the start rule (full
* context). Update reach to contain only these configurations. This
* handles both explicit EOF transitions in the grammar and implicit
* EOF transitions following the end of the decision or start rule.
*
* When reach==intermediate, no closure operation was performed. In
* this case, removeAllConfigsNotInRuleStopState needs to check for
* reachable rule stop states as well as configurations already in
* a rule stop state.
*
* This is handled before the configurations in skippedStopStates,
* because any configurations potentially added from that list are
* already guaranteed to meet this condition whether or not it's
* required.
*/
reach = RemoveAllConfigsNotInRuleStopState(reach, reach == intermediate);
}
/* If skippedStopStates is not null, then it contains at least one
* configuration. For full-context reach operations, these
* configurations reached the end of the start rule, in which case we
* only add them back to reach if no configuration during the current
* closure operation reached such a state. This ensures adaptivePredict
* chooses an alternative matching the longest overall sequence when
* multiple alternatives are viable.
*/
if (skippedStopStates != null && (!fullCtx || !PredictionMode.HasConfigInRuleStopState(reach.configs)))
{
foreach (ATNConfig c in skippedStopStates)
{
reach.Add(c, mergeCache);
}
}
if (reach.Empty)
return null;
return reach;
}