fix LL1 analyzer

This commit is contained in:
parrt 2016-12-09 18:07:47 -08:00
parent 3a6ffafec3
commit 0eb27279de
4 changed files with 54 additions and 15 deletions

View File

@ -150,7 +150,8 @@ public class LL1Analyzer {
if (ctx == null) {
look.add(Token.EPSILON);
return;
} else if (ctx.isEmpty() && addEOF) {
}
else if (ctx.isEmpty() && addEOF) {
look.add(Token.EOF);
return;
}
@ -160,26 +161,26 @@ public class LL1Analyzer {
if ( ctx==null ) {
look.add(Token.EPSILON);
return;
} else if (ctx.isEmpty() && addEOF) {
}
else if (ctx.isEmpty() && addEOF) {
look.add(Token.EOF);
return;
}
if ( ctx != PredictionContext.EMPTY ) {
// run thru all possible stack tops in ctx
for (int i = 0; i < ctx.size(); i++) {
ATNState returnState = atn.states.get(ctx.getReturnState(i));
// System.out.println("popping back to "+retState);
boolean removed = calledRuleStack.get(returnState.ruleIndex);
try {
calledRuleStack.clear(returnState.ruleIndex);
boolean removed = calledRuleStack.get(s.ruleIndex);
try {
calledRuleStack.clear(s.ruleIndex);
for (int i = 0; i < ctx.size(); i++) {
ATNState returnState = atn.states.get(ctx.getReturnState(i));
// System.out.println("popping back to "+retState);
_LOOK(returnState, stopState, ctx.getParent(i), look, lookBusy, calledRuleStack, seeThruPreds, addEOF);
}
finally {
if (removed) {
calledRuleStack.set(returnState.ruleIndex);
}
}
finally {
if (removed) {
calledRuleStack.set(s.ruleIndex);
}
}
return;

View File

@ -317,6 +317,40 @@ public class TestToolSyntaxErrors extends BaseJavaToolTest {
super.testErrors(pair, true);
}
// Test for https://github.com/antlr/antlr4/issues/1203
@Test public void testEpsilonNestedClosureAnalysis() {
String grammar =
"grammar T;\n"+
"s : (a a)* ;\n"+
"a : 'foo'* ;\n";
String expected =
"error(" + ErrorType.EPSILON_CLOSURE.code + "): T.g4:2:0: rule s contains a closure with at least one alternative that can match an empty string\n";
String[] pair = new String[] {
grammar,
expected
};
super.testErrors(pair, true);
}
// Test for https://github.com/antlr/antlr4/issues/1203
@Test public void testEpsilonOptionalAndClosureAnalysis() {
String grammar =
"grammar T;\n"+
"s : (a a)? ;\n"+
"a : 'foo'* ;\n";
String expected =
"warning(" + ErrorType.EPSILON_OPTIONAL.code + "): T.g4:2:0: rule s contains an optional block with at least one alternative that can match an empty string\n";
String[] pair = new String[] {
grammar,
expected
};
super.testErrors(pair, true);
}
@Test public void testEpsilonOptionalAnalysis() {
String grammar =
"grammar A;\n"

View File

@ -34,7 +34,8 @@ public class AnalysisPipeline {
if (g.isLexer()) {
processLexer();
} else {
}
else {
// BUILD DFA FOR EACH DECISION
processParser();
}

View File

@ -113,7 +113,10 @@ public class ParserATNFactory implements ATNFactory {
for (Triple<Rule, ATNState, ATNState> pair : preventEpsilonClosureBlocks) {
LL1Analyzer analyzer = new LL1Analyzer(atn);
if (analyzer.LOOK(pair.b, pair.c, null).contains(org.antlr.v4.runtime.Token.EPSILON)) {
ATNState blkStart = pair.b;
ATNState blkStop = pair.c;
IntervalSet lookahead = analyzer.LOOK(blkStart, blkStop, null);
if ( lookahead.contains(org.antlr.v4.runtime.Token.EPSILON)) {
ErrorType errorType = pair.a instanceof LeftRecursiveRule ? ErrorType.EPSILON_LR_FOLLOW : ErrorType.EPSILON_CLOSURE;
g.tool.errMgr.grammarError(errorType, g.fileName, ((GrammarAST)pair.a.ast.getChild(0)).getToken(), pair.a.name);
}