rm'd epsilon transition in ATN after rule refs.

[git-p4: depot-paths = "//depot/code/antlr4/main/": change = 9518]
This commit is contained in:
parrt 2011-12-03 14:52:24 -08:00
parent 2b8a821c21
commit 80fd90d363
4 changed files with 54 additions and 32 deletions

View File

@ -38,7 +38,7 @@ public class RuleTransition extends Transition {
/** What node to begin computations following ref to rule */
@NotNull
public final ATNState followState;
public ATNState followState;
public RuleTransition(@NotNull RuleStartState ruleStart,
int ruleIndex,

View File

@ -1,8 +1,4 @@
grammar T;
s : b ';' | b '.' ;
b : a ;
a : {false}? ID {System.out.println("alt 1");}
| {true}? ID {System.out.println("alt 2");}
;ID : 'a'..'z'+ ;
INT : '0'..'9'+;
WS : (' '|'\n') {skip();} ;
s : b c ;
b : A ;
c : C ;

View File

@ -58,11 +58,26 @@ import java.util.List;
* No side-effects. It builds an ATN object and returns it.
*/
public class ParserATNFactory implements ATNFactory {
class TailEpsilonRemover extends ATNVisitor {
@Override
public void visitState(ATNState p) {
if ( p.getClass() == ATNState.class && p.getNumberOfTransitions()==1 ) {
ATNState q = p.transition(0).target;
/** Add follow links from rule stop states to every following state for rule invocations.
* Must do after construction since we optimize away some epsilon transitions.
*/
class FollowLinkAdder extends ATNVisitor {
@Override
public void visitState(ATNState p) {
if ( p.getClass() == ATNState.class && p.getNumberOfTransitions()==1 &&
p.transition(0) instanceof RuleTransition )
{
RuleTransition rt = (RuleTransition) p.transition(0);
addFollowLink(rt.ruleIndex, rt.followState);
}
}
}
class TailEpsilonRemover extends ATNVisitor {
@Override
public void visitState(ATNState p) {
if ( p.getClass() == ATNState.class && p.getNumberOfTransitions()==1 ) {
ATNState q = p.transition(0).target;
if ( q.getClass() == ATNState.class ) {
// we have p-x->q for x in {rule, action, pred, token, ...}
// if edge out of q is single epsilon to block end
@ -96,11 +111,12 @@ public class ParserATNFactory implements ATNFactory {
public ATN createATN() {
_createATN(g.rules.values());
atn.maxTokenType = g.getMaxTokenType();
addRuleFollowLinks();
addEOFTransitionToStartRules();
return atn;
}
public void _createATN(Collection<Rule> rules) {
public void _createATN(Collection<Rule> rules) {
createRuleStartAndStopATNStates();
GrammarASTAdaptor adaptor = new GrammarASTAdaptor();
@ -205,8 +221,8 @@ public class ParserATNFactory implements ATNFactory {
*/
public Handle ruleRef(GrammarAST node) {
Handle h = _ruleRef(node);
Rule r = g.getRule(node.getText());
addFollowLink(r, h.right);
// Rule r = g.getRule(node.getText());
// addFollowLink(r, h.right);
return h;
}
@ -222,9 +238,9 @@ public class ParserATNFactory implements ATNFactory {
return new Handle(left, right);
}
public void addFollowLink(Rule r, ATNState right) {
public void addFollowLink(int ruleIndex, ATNState right) {
// add follow edge from end of invoked rule
RuleStopState stop = atn.ruleToStopState[r.index];
RuleStopState stop = atn.ruleToStopState[ruleIndex];
epsilon(stop, right);
}
@ -349,13 +365,16 @@ public class ParserATNFactory implements ATNFactory {
Handle el = els.get(i);
// if el is of form o-x->o for x in {rule, action, pred, token, ...}
// and not last in alt
if ( el.left.getClass() == ATNState.class &&
Transition tr = null;
if ( el.left.getNumberOfTransitions()==1 ) tr = el.left.transition(0);
boolean isRuleTrans = tr instanceof RuleTransition;
if ( el.left.getClass() == ATNState.class &&
el.right.getClass() == ATNState.class &&
el.left.getNumberOfTransitions()==1 &&
el.left.transition(0).target == el.right)
tr!=null && (isRuleTrans || tr.target == el.right) )
{
// we can avoid epsilon edge to next el
el.left.transition(0).target = els.get(i+1).left;
if ( isRuleTrans ) ((RuleTransition)tr).followState = els.get(i+1).left;
else tr.target = els.get(i+1).left;
atn.removeState(el.right); // we skipped over this state
}
else { // need epsilon if previous block's right end node is complicated
@ -501,6 +520,14 @@ public class ParserATNFactory implements ATNFactory {
}
}
public void addRuleFollowLinks() {
FollowLinkAdder flinker = new FollowLinkAdder();
for (Rule r : g.rules.values()) {
ATNState start = atn.ruleToStartState[r.index];
flinker.visit(start);
}
}
/** add an EOF transition to any rule end ATNState that points to nothing
* (i.e., for all those rules not invoked by another rule). These
* are start symbols then.

View File

@ -184,17 +184,16 @@ public class TestATNConstruction extends BaseTest {
"b : B ;");
String expecting =
"RuleStart_a_0->s4\n" +
"s4-b->RuleStart_b_2\n" +
"s5->s6\n" +
"s6-A->s7\n" +
"s7->RuleStop_a_1\n" +
"RuleStop_a_1-EOF->s10\n";
"s4-b->RuleStart_b_2\n" +
"s6-A->s7\n" +
"s7->RuleStop_a_1\n" +
"RuleStop_a_1-EOF->s10\n";
checkRuleATN(g, "a", expecting);
expecting =
"RuleStart_b_2->s8\n" +
"s8-B->s9\n" +
"s9->RuleStop_b_3\n" +
"RuleStop_b_3->s5\n";
"RuleStop_b_3->s6\n";
checkRuleATN(g, "b", expecting);
}
@ -206,10 +205,10 @@ public class TestATNConstruction extends BaseTest {
"c : b C;");
String expecting =
"RuleStart_b_2->s10\n" +
"s10-B->s11\n" +
"s11->RuleStop_b_3\n" +
"RuleStop_b_3->s7\n" +
"RuleStop_b_3->s13\n";
"s10-B->s11\n" +
"s11->RuleStop_b_3\n" +
"RuleStop_b_3->s8\n" +
"RuleStop_b_3->s14\n";
checkRuleATN(g, "b", expecting);
}