Fixes #822, Fixes #855. We didn't check for left-recursive rules that didn't follow pattern that would lead to error. added unit tests. Passes all tests.
This commit is contained in:
parent
f645e175ad
commit
b5907498de
|
@ -93,7 +93,12 @@ public class LeftRecursiveRuleTransformer {
|
||||||
if ( !Grammar.isTokenName(r.name) ) {
|
if ( !Grammar.isTokenName(r.name) ) {
|
||||||
if ( LeftRecursiveRuleAnalyzer.hasImmediateRecursiveRuleRefs(r.ast, r.name) ) {
|
if ( LeftRecursiveRuleAnalyzer.hasImmediateRecursiveRuleRefs(r.ast, r.name) ) {
|
||||||
boolean fitsPattern = translateLeftRecursiveRule(ast, (LeftRecursiveRule)r, language);
|
boolean fitsPattern = translateLeftRecursiveRule(ast, (LeftRecursiveRule)r, language);
|
||||||
if ( fitsPattern ) leftRecursiveRuleNames.add(r.name);
|
if ( fitsPattern ) {
|
||||||
|
leftRecursiveRuleNames.add(r.name);
|
||||||
|
}
|
||||||
|
else { // better given an error that non-conforming left-recursion exists
|
||||||
|
tool.errMgr.grammarError(ErrorType.NONCONFORMING_LR_RULE, g.fileName, ((GrammarAST)r.ast.getChild(0)).token, r.name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,6 +79,7 @@ package org.antlr.v4.parse;
|
||||||
import org.antlr.v4.Tool;
|
import org.antlr.v4.Tool;
|
||||||
import org.antlr.v4.tool.*;
|
import org.antlr.v4.tool.*;
|
||||||
import org.antlr.v4.tool.ast.*;
|
import org.antlr.v4.tool.ast.*;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,8 +106,12 @@ public void visit(GrammarAST t, String ruleName) {
|
||||||
Method m = getClass().getMethod(ruleName);
|
Method m = getClass().getMethod(ruleName);
|
||||||
m.invoke(this);
|
m.invoke(this);
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Throwable e) {
|
||||||
ErrorManager errMgr = getErrorManager();
|
ErrorManager errMgr = getErrorManager();
|
||||||
|
if ( e instanceof InvocationTargetException ) {
|
||||||
|
e = e.getCause();
|
||||||
|
}
|
||||||
|
//e.printStackTrace(System.err);
|
||||||
if ( errMgr==null ) {
|
if ( errMgr==null ) {
|
||||||
System.err.println("can't find rule "+ruleName+
|
System.err.println("can't find rule "+ruleName+
|
||||||
" or tree structure error: "+t.toStringTree()
|
" or tree structure error: "+t.toStringTree()
|
||||||
|
|
|
@ -85,14 +85,15 @@ public class SemanticPipeline {
|
||||||
BasicSemanticChecks basics = new BasicSemanticChecks(g, ruleCollector);
|
BasicSemanticChecks basics = new BasicSemanticChecks(g, ruleCollector);
|
||||||
basics.process();
|
basics.process();
|
||||||
|
|
||||||
// don't continue if we get errors in this basic check
|
|
||||||
//if ( false ) return;
|
|
||||||
|
|
||||||
// TRANSFORM LEFT-RECURSIVE RULES
|
// TRANSFORM LEFT-RECURSIVE RULES
|
||||||
|
int prevErrors = g.tool.errMgr.getNumErrors();
|
||||||
LeftRecursiveRuleTransformer lrtrans =
|
LeftRecursiveRuleTransformer lrtrans =
|
||||||
new LeftRecursiveRuleTransformer(g.ast, ruleCollector.rules.values(), g);
|
new LeftRecursiveRuleTransformer(g.ast, ruleCollector.rules.values(), g);
|
||||||
lrtrans.translateLeftRecursiveRules();
|
lrtrans.translateLeftRecursiveRules();
|
||||||
|
|
||||||
|
// don't continue if we got errors during left-recursion elimination
|
||||||
|
if ( g.tool.errMgr.getNumErrors()>prevErrors ) return;
|
||||||
|
|
||||||
// STORE RULES IN GRAMMAR
|
// STORE RULES IN GRAMMAR
|
||||||
for (Rule r : ruleCollector.rules.values()) {
|
for (Rule r : ruleCollector.rules.values()) {
|
||||||
g.defineRule(r);
|
g.defineRule(r);
|
||||||
|
|
|
@ -956,6 +956,8 @@ public enum ErrorType {
|
||||||
*/
|
*/
|
||||||
CHANNELS_BLOCK_IN_COMBINED_GRAMMAR(164, "custom channels are not supported in combined grammars", ErrorSeverity.ERROR),
|
CHANNELS_BLOCK_IN_COMBINED_GRAMMAR(164, "custom channels are not supported in combined grammars", ErrorSeverity.ERROR),
|
||||||
|
|
||||||
|
NONCONFORMING_LR_RULE(165, "rule <arg> is left recursive but doesn't conform to a pattern ANTLR can handle", ErrorSeverity.ERROR),
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Backward incompatibility errors
|
* Backward incompatibility errors
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -34,7 +34,7 @@ import org.antlr.v4.tool.ErrorType;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
/** */
|
/** */
|
||||||
public class TestLeftRecursion extends BaseTest {
|
public class TestLeftRecursionToolIssues extends BaseTest {
|
||||||
protected boolean debug = false;
|
protected boolean debug = false;
|
||||||
|
|
||||||
@Test public void testCheckForNonLeftRecursiveRule() throws Exception {
|
@Test public void testCheckForNonLeftRecursiveRule() throws Exception {
|
||||||
|
@ -64,4 +64,60 @@ public class TestLeftRecursion extends BaseTest {
|
||||||
testErrors(new String[] { grammar, expected }, false);
|
testErrors(new String[] { grammar, expected }, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Reproduces https://github.com/antlr/antlr4/issues/855 */
|
||||||
|
@Test public void testLeftRecursiveRuleRefWithArg() throws Exception {
|
||||||
|
String grammar =
|
||||||
|
"grammar T;\n" +
|
||||||
|
"statement\n" +
|
||||||
|
"locals[Scope scope]\n" +
|
||||||
|
" : expressionA[$scope] ';'\n" +
|
||||||
|
" ;\n" +
|
||||||
|
"expressionA[Scope scope]\n" +
|
||||||
|
" : atom[$scope]\n" +
|
||||||
|
" | expressionA[$scope] '[' expressionA[$scope] ']'\n" +
|
||||||
|
" ;\n" +
|
||||||
|
"atom[Scope scope]\n" +
|
||||||
|
" : 'dummy'\n" +
|
||||||
|
" ;\n";
|
||||||
|
String expected =
|
||||||
|
"error(" + ErrorType.NONCONFORMING_LR_RULE.code + "): T.g4:6:0: rule expressionA is left recursive but doesn't conform to a pattern ANTLR can handle\n";
|
||||||
|
testErrors(new String[]{grammar, expected}, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Reproduces https://github.com/antlr/antlr4/issues/855 */
|
||||||
|
@Test public void testLeftRecursiveRuleRefWithArg2() throws Exception {
|
||||||
|
String grammar =
|
||||||
|
"grammar T;\n" +
|
||||||
|
"a[int i] : 'x'\n" +
|
||||||
|
" | a[3] 'y'\n" +
|
||||||
|
" ;";
|
||||||
|
String expected =
|
||||||
|
"error(" + ErrorType.NONCONFORMING_LR_RULE.code + "): T.g4:2:0: rule a is left recursive but doesn't conform to a pattern ANTLR can handle\n";
|
||||||
|
testErrors(new String[]{grammar, expected}, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Reproduces https://github.com/antlr/antlr4/issues/855 */
|
||||||
|
@Test public void testLeftRecursiveRuleRefWithArg3() throws Exception {
|
||||||
|
String grammar =
|
||||||
|
"grammar T;\n" +
|
||||||
|
"a : 'x'\n" +
|
||||||
|
" | a[3] 'y'\n" +
|
||||||
|
" ;";
|
||||||
|
String expected =
|
||||||
|
"error(" + ErrorType.NONCONFORMING_LR_RULE.code + "): T.g4:2:0: rule a is left recursive but doesn't conform to a pattern ANTLR can handle\n";
|
||||||
|
testErrors(new String[]{grammar, expected}, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Reproduces https://github.com/antlr/antlr4/issues/822 */
|
||||||
|
@Test public void testIsolatedLeftRecursiveRuleRef() throws Exception {
|
||||||
|
String grammar =
|
||||||
|
"grammar T;\n" +
|
||||||
|
"a : a | b ;\n" +
|
||||||
|
"b : 'B' ;\n";
|
||||||
|
String expected =
|
||||||
|
"error(" + ErrorType.NONCONFORMING_LR_RULE.code + "): T.g4:2:0: rule a is left recursive but doesn't conform to a pattern ANTLR can handle\n";
|
||||||
|
testErrors(new String[]{grammar, expected}, false);
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue