ANTLR4 generated incorrect code to access return value of recursive rule

Incorrect code was generated for $e.v in a rule like this one:

    e returns [int v] ::=
           INT       {$v = $INT.int;}
        | '(' e ')'  {$v = $e.v;}
        ;

After parsing "(99)" the result would have v == 0 instead of 99.
This commit is contained in:
Kurt Harriman 2016-11-16 22:38:11 -08:00
parent 3d8f81a63e
commit ab8d17472f
3 changed files with 46 additions and 16 deletions

View File

@ -30,6 +30,7 @@
package org.antlr.v4.test.runtime.java;
import org.antlr.v4.Tool;
import org.antlr.v4.analysis.AnalysisPipeline;
import org.antlr.v4.automata.ATNFactory;
import org.antlr.v4.automata.ATNPrinter;
import org.antlr.v4.automata.LexerATNFactory;
@ -913,6 +914,9 @@ public class BaseJavaTest implements RuntimeTestSupport {
if ( g.isLexer() ) factory = new LexerATNFactory((LexerGrammar)g);
g.atn = factory.createATN();
AnalysisPipeline anal = new AnalysisPipeline(g);
anal.process();
CodeGenerator gen = new CodeGenerator(g);
ST outputFileST = gen.generateParser(false);
String output = outputFileST.render();

View File

@ -176,6 +176,45 @@ public class TestActionTranslation extends BaseJavaTest {
testActions(attributeTemplate, "inline", action, expected);
}
/**
* Regression test for issue #1295
* $e.v yields incorrect value 0 in "e returns [int v] : '1' {$v = 1;} | '(' e ')' {$v = $e.v;} ;"
* https://github.com/antlr/antlr4/issues/1295
*/
@Test public void testRuleRefsRecursive() throws Exception {
String recursiveTemplate =
"recursiveTemplate(inline) ::= <<\n" +
"parser grammar A;\n"+
"e returns [int v]\n" +
" : INT {$v = $INT.int;}\n" +
" | '(' e ')' {\n" +
" #inline#<inline>#end-inline#\n" +
" }\n" +
" ;\n" +
">>";
String leftRecursiveTemplate =
"recursiveTemplate(inline) ::= <<\n" +
"parser grammar A;\n"+
"e returns [int v]\n" +
" : a=e op=('*'|'/') b=e {$v = eval($a.v, $op.type, $b.v);}\n" +
" | INT {$v = $INT.int;}\n" +
" | '(' e ')' {\n" +
" #inline#<inline>#end-inline#\n" +
" }\n" +
" ;\n" +
">>";
// ref to value returned from recursive call to rule
String action = "$v = $e.v;";
String expected = "((EContext)_localctx).v = ((EContext)_localctx).e.v;";
testActions(recursiveTemplate, "inline", action, expected);
testActions(leftRecursiveTemplate, "inline", action, expected);
// ref to predefined attribute obtained from recursive call to rule
action = "$v = $e.text.length();";
expected = "((EContext)_localctx).v = (((EContext)_localctx).e!=null?_input.getText(((EContext)_localctx).e.start,((EContext)_localctx).e.stop):null).length();";
testActions(recursiveTemplate, "inline", action, expected);
testActions(leftRecursiveTemplate, "inline", action, expected);
}
@Test public void testRefToTextAttributeForCurrentRule() throws Exception {
String action = "$ctx.text; $text";

View File

@ -214,23 +214,10 @@ public class ActionTranslator implements ActionSplitterListener {
switch ( a.dict.type ) {
case ARG: chunks.add(new ArgRef(nodeContext,y.getText())); break; // has to be current rule
case RET:
if ( factory.getCurrentRuleFunction()!=null &&
factory.getCurrentRuleFunction().name.equals(x.getText()) )
{
chunks.add(new RetValueRef(rf.ruleCtx, y.getText())); break;
}
else {
chunks.add(new QRetValueRef(nodeContext, getRuleLabel(x.getText()), y.getText())); break;
}
chunks.add(new QRetValueRef(nodeContext, getRuleLabel(x.getText()), y.getText()));
break;
case PREDEFINED_RULE:
if ( factory.getCurrentRuleFunction()!=null &&
factory.getCurrentRuleFunction().name.equals(x.getText()) )
{
chunks.add(getRulePropertyRef(y));
}
else {
chunks.add(getRulePropertyRef(x, y));
}
chunks.add(getRulePropertyRef(x, y));
break;
case TOKEN:
chunks.add(getTokenPropertyRef(x, y));