protected bool CanDropLoopEntryEdgeInLeftRecursiveRule(ATNConfig config)
{
ATNState p = config.state;
// First check to see if we are in StarLoopEntryState generated during
// left-recursion elimination. For efficiency, also check if
// the context has an empty stack case. If so, it would mean
// global FOLLOW so we can't perform optimization
if (p.StateType != StateType.StarLoopEntry ||
!((StarLoopEntryState)p).isPrecedenceDecision || // Are we the special loop entry/exit state?
config.context.IsEmpty || // If SLL wildcard
config.context.HasEmptyPath)
{
return false;
}
// Require all return states to return back to the same rule
// that p is in.
int numCtxs = config.context.Size;
for (int i = 0; i < numCtxs; i++)
{ // for each stack context
ATNState returnState = atn.states[config.context.GetReturnState(i)];
if (returnState.ruleIndex != p.ruleIndex) return false;
}
BlockStartState decisionStartState = (BlockStartState)p.Transition(0).target;
int blockEndStateNum = decisionStartState.endState.stateNumber;
BlockEndState blockEndState = (BlockEndState)atn.states[blockEndStateNum];
// Verify that the top of each stack context leads to loop entry/exit
// state through epsilon edges and w/o leaving rule.
for (int i = 0; i < numCtxs; i++)
{ // for each stack context
int returnStateNumber = config.context.GetReturnState(i);
ATNState returnState = atn.states[returnStateNumber];
// all states must have single outgoing epsilon edge
if (returnState.NumberOfTransitions != 1 ||
!returnState.Transition(0).IsEpsilon)
{
return false;
}
// Look for prefix op case like 'not expr', (' type ')' expr
ATNState returnStateTarget = returnState.Transition(0).target;
if (returnState.StateType == StateType.BlockEnd && returnStateTarget == p)
{
continue;
}
// Look for 'expr op expr' or case where expr's return state is block end
// of (...)* internal block; the block end points to loop back
// which points to p but we don't need to check that
if (returnState == blockEndState)
{
continue;
}
// Look for ternary expr ? expr : expr. The return state points at block end,
// which points at loop entry state
if (returnStateTarget == blockEndState)
{
continue;
}
// Look for complex prefix 'between expr and expr' case where 2nd expr's
// return state points at block end state of (...)* internal block
if (returnStateTarget.StateType == StateType.BlockEnd &&
returnStateTarget.NumberOfTransitions == 1 &&
returnStateTarget.Transition(0).IsEpsilon &&
returnStateTarget.Transition(0).target == p)
{
continue;
}
// anything else ain't conforming
return false;
}
return true;
}