Merge pull request #873 from parrt/fix-nonconforming-lr-rules
Check for nonconforming left-recursive rules.
This commit is contained in:
commit
f491a1a84f
|
@ -93,7 +93,12 @@ public class LeftRecursiveRuleTransformer {
|
|||
if ( !Grammar.isTokenName(r.name) ) {
|
||||
if ( LeftRecursiveRuleAnalyzer.hasImmediateRecursiveRuleRefs(r.ast, r.name) ) {
|
||||
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.ast.*;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
}
|
||||
|
||||
|
@ -105,8 +106,12 @@ public void visit(GrammarAST t, String ruleName) {
|
|||
Method m = getClass().getMethod(ruleName);
|
||||
m.invoke(this);
|
||||
}
|
||||
catch (Exception e) {
|
||||
catch (Throwable e) {
|
||||
ErrorManager errMgr = getErrorManager();
|
||||
if ( e instanceof InvocationTargetException ) {
|
||||
e = e.getCause();
|
||||
}
|
||||
//e.printStackTrace(System.err);
|
||||
if ( errMgr==null ) {
|
||||
System.err.println("can't find rule "+ruleName+
|
||||
" or tree structure error: "+t.toStringTree()
|
||||
|
|
|
@ -85,14 +85,15 @@ public class SemanticPipeline {
|
|||
BasicSemanticChecks basics = new BasicSemanticChecks(g, ruleCollector);
|
||||
basics.process();
|
||||
|
||||
// don't continue if we get errors in this basic check
|
||||
//if ( false ) return;
|
||||
|
||||
// TRANSFORM LEFT-RECURSIVE RULES
|
||||
int prevErrors = g.tool.errMgr.getNumErrors();
|
||||
LeftRecursiveRuleTransformer lrtrans =
|
||||
new LeftRecursiveRuleTransformer(g.ast, ruleCollector.rules.values(), g);
|
||||
lrtrans.translateLeftRecursiveRules();
|
||||
|
||||
// don't continue if we got errors during left-recursion elimination
|
||||
if ( g.tool.errMgr.getNumErrors()>prevErrors ) return;
|
||||
|
||||
// STORE RULES IN GRAMMAR
|
||||
for (Rule r : ruleCollector.rules.values()) {
|
||||
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),
|
||||
|
||||
NONCONFORMING_LR_RULE(165, "rule <arg> is left recursive but doesn't conform to a pattern ANTLR can handle", ErrorSeverity.ERROR),
|
||||
|
||||
/*
|
||||
* Backward incompatibility errors
|
||||
*/
|
||||
|
|
|
@ -34,7 +34,7 @@ import org.antlr.v4.tool.ErrorType;
|
|||
import org.junit.Test;
|
||||
|
||||
/** */
|
||||
public class TestLeftRecursion extends BaseTest {
|
||||
public class TestLeftRecursionToolIssues extends BaseTest {
|
||||
protected boolean debug = false;
|
||||
|
||||
@Test public void testCheckForNonLeftRecursiveRule() throws Exception {
|
||||
|
@ -64,4 +64,60 @@ public class TestLeftRecursion extends BaseTest {
|
|||
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