forked from jasder/antlr
Consistent handling of element options applied to alternatives (fixes #542)
This commit is contained in:
parent
8f08802716
commit
6eeafacb88
|
@ -285,6 +285,12 @@ public class LeftRecursiveRuleAnalyzer extends LeftRecursiveRuleWalker {
|
||||||
GrammarAST alt = (GrammarAST)blk.getChildren().get(i);
|
GrammarAST alt = (GrammarAST)blk.getChildren().get(i);
|
||||||
Tree first = alt.getChild(0);
|
Tree first = alt.getChild(0);
|
||||||
if ( first==null ) continue;
|
if ( first==null ) continue;
|
||||||
|
if (first.getType() == ELEMENT_OPTIONS) {
|
||||||
|
first = alt.getChild(1);
|
||||||
|
if (first == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
if ( first.getType()==RULE_REF && first.getText().equals(ruleName) ) return true;
|
if ( first.getType()==RULE_REF && first.getText().equals(ruleName) ) return true;
|
||||||
Tree rref = first.getChild(1);
|
Tree rref = first.getChild(1);
|
||||||
if ( rref!=null && rref.getType()==RULE_REF && rref.getText().equals(ruleName) ) return true;
|
if ( rref!=null && rref.getType()==RULE_REF && rref.getText().equals(ruleName) ) return true;
|
||||||
|
|
|
@ -769,8 +769,8 @@ public class ParserATNFactory implements ATNFactory {
|
||||||
for (Object alt : block.getChildren()) {
|
for (Object alt : block.getChildren()) {
|
||||||
if ( !(alt instanceof AltAST) ) continue;
|
if ( !(alt instanceof AltAST) ) continue;
|
||||||
AltAST altAST = (AltAST)alt;
|
AltAST altAST = (AltAST)alt;
|
||||||
if ( altAST.getChildCount()==1 ) {
|
if ( altAST.getChildCount()==1 || (altAST.getChildCount() == 2 && altAST.getChild(0).getType() == ANTLRParser.ELEMENT_OPTIONS) ) {
|
||||||
Tree e = altAST.getChild(0);
|
Tree e = altAST.getChild(altAST.getChildCount() - 1);
|
||||||
if ( e.getType()==ANTLRParser.WILDCARD ) {
|
if ( e.getType()==ANTLRParser.WILDCARD ) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,9 +99,9 @@ alt[boolean outerMost] returns [CodeBlockForAlt altCodeBlock, List<SrcOp> ops]
|
||||||
$altCodeBlock.ops = $ops = elems;
|
$altCodeBlock.ops = $ops = elems;
|
||||||
controller.setCurrentBlock($altCodeBlock);
|
controller.setCurrentBlock($altCodeBlock);
|
||||||
}
|
}
|
||||||
^( ALT ( element {if ($element.omos!=null) elems.addAll($element.omos);} )+ )
|
^( ALT elementOptions? ( element {if ($element.omos!=null) elems.addAll($element.omos);} )+ )
|
||||||
|
|
||||||
| ^(ALT EPSILON)
|
| ^(ALT elementOptions? EPSILON)
|
||||||
{$altCodeBlock = controller.epsilon(controller.getCurrentOuterMostAlt(), outerMost);}
|
{$altCodeBlock = controller.epsilon(controller.getCurrentOuterMostAlt(), outerMost);}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
|
@ -658,10 +658,11 @@ alternative
|
||||||
paraphrases.pop();
|
paraphrases.pop();
|
||||||
Grammar.setNodeOptions($tree, $o.tree);
|
Grammar.setNodeOptions($tree, $o.tree);
|
||||||
}
|
}
|
||||||
: o=elementOptions?
|
: o=elementOptions?
|
||||||
e+=element+ -> ^(ALT<AltAST> elementOptions? $e+)
|
( e+=element+ -> ^(ALT<AltAST> elementOptions? $e+)
|
||||||
| -> ^(ALT<AltAST> EPSILON) // empty alt
|
| -> ^(ALT<AltAST> elementOptions? EPSILON) // empty alt
|
||||||
;
|
)
|
||||||
|
;
|
||||||
|
|
||||||
element
|
element
|
||||||
@init {
|
@init {
|
||||||
|
|
|
@ -104,8 +104,8 @@ alternative returns [ATNFactory.Handle p]
|
||||||
@init {List<ATNFactory.Handle> els = new ArrayList<ATNFactory.Handle>();}
|
@init {List<ATNFactory.Handle> els = new ArrayList<ATNFactory.Handle>();}
|
||||||
: ^(LEXER_ALT_ACTION a=alternative lexerCommands)
|
: ^(LEXER_ALT_ACTION a=alternative lexerCommands)
|
||||||
{$p = factory.lexerAltCommands($a.p,$lexerCommands.p);}
|
{$p = factory.lexerAltCommands($a.p,$lexerCommands.p);}
|
||||||
| ^(ALT EPSILON) {$p = factory.epsilon($EPSILON);}
|
| ^(ALT elementOptions? EPSILON) {$p = factory.epsilon($EPSILON);}
|
||||||
| ^(ALT (e=element {els.add($e.p);})+) {$p = factory.alt(els);}
|
| ^(ALT elementOptions? (e=element {els.add($e.p);})+) {$p = factory.alt(els);}
|
||||||
;
|
;
|
||||||
|
|
||||||
lexerCommands returns [ATNFactory.Handle p]
|
lexerCommands returns [ATNFactory.Handle p]
|
||||||
|
@ -200,3 +200,15 @@ terminal returns [ATNFactory.Handle p]
|
||||||
| ^(TOKEN_REF .) {$p = factory.tokenRef((TerminalAST)$start);}
|
| ^(TOKEN_REF .) {$p = factory.tokenRef((TerminalAST)$start);}
|
||||||
| TOKEN_REF {$p = factory.tokenRef((TerminalAST)$start);}
|
| TOKEN_REF {$p = factory.tokenRef((TerminalAST)$start);}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
elementOptions
|
||||||
|
: ^(ELEMENT_OPTIONS elementOption*)
|
||||||
|
;
|
||||||
|
|
||||||
|
elementOption
|
||||||
|
: ID
|
||||||
|
| ^(ASSIGN ID ID)
|
||||||
|
| ^(ASSIGN ID STRING_LITERAL)
|
||||||
|
| ^(ASSIGN ID ACTION)
|
||||||
|
| ^(ASSIGN ID INT)
|
||||||
|
;
|
||||||
|
|
|
@ -95,10 +95,10 @@ boolean inLexer = Grammar.isTokenName(currentRuleName);
|
||||||
GrammarTransformPipeline.setGrammarPtr(g, $tree);
|
GrammarTransformPipeline.setGrammarPtr(g, $tree);
|
||||||
}
|
}
|
||||||
: {inContext("RULE")}? // top-level: rule block and > 1 alt
|
: {inContext("RULE")}? // top-level: rule block and > 1 alt
|
||||||
^(BLOCK ^(alt=ALT {((AltAST)$alt).altLabel==null}? setElement[inLexer]) ( ^(ALT setElement[inLexer]) )+)
|
^(BLOCK ^(alt=ALT elementOptions? {((AltAST)$alt).altLabel==null}? setElement[inLexer]) ( ^(ALT elementOptions? setElement[inLexer]) )+)
|
||||||
-> ^(BLOCK<BlockAST>[$BLOCK.token] ^(ALT<AltAST>[$BLOCK.token,"ALT"] ^(SET[$BLOCK.token, "SET"] setElement+)))
|
-> ^(BLOCK<BlockAST>[$BLOCK.token] ^(ALT<AltAST>[$BLOCK.token,"ALT"] ^(SET[$BLOCK.token, "SET"] setElement+)))
|
||||||
| {!inContext("RULE")}? // if not rule block and > 1 alt
|
| {!inContext("RULE")}? // if not rule block and > 1 alt
|
||||||
^(BLOCK ^(ALT setElement[inLexer]) ( ^(ALT setElement[inLexer]) )+)
|
^(BLOCK ^(ALT elementOptions? setElement[inLexer]) ( ^(ALT elementOptions? setElement[inLexer]) )+)
|
||||||
-> ^(SET[$BLOCK.token, "SET"] setElement+)
|
-> ^(SET[$BLOCK.token, "SET"] setElement+)
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
|
@ -767,7 +767,7 @@ alternative
|
||||||
exitAlternative((AltAST)$start);
|
exitAlternative((AltAST)$start);
|
||||||
}
|
}
|
||||||
: ^(ALT elementOptions? element+)
|
: ^(ALT elementOptions? element+)
|
||||||
| ^(ALT EPSILON)
|
| ^(ALT elementOptions? EPSILON)
|
||||||
;
|
;
|
||||||
|
|
||||||
lexerCommand
|
lexerCommand
|
||||||
|
|
|
@ -139,7 +139,7 @@ suffix
|
||||||
;
|
;
|
||||||
|
|
||||||
nonLeftRecur
|
nonLeftRecur
|
||||||
: ^(ALT element+) // no assoc for these; ignore if <assoc=...> present
|
: ^(ALT elementOptions? element+)
|
||||||
;
|
;
|
||||||
|
|
||||||
recurse
|
recurse
|
||||||
|
|
|
@ -152,6 +152,36 @@ public class TestLeftRecursion extends BaseTest {
|
||||||
runTests(grammar, tests, "s");
|
runTests(grammar, tests, "s");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a regression test for antlr/antlr4#542 "First alternative cannot
|
||||||
|
* be right-associative".
|
||||||
|
* https://github.com/antlr/antlr4/issues/542
|
||||||
|
*/
|
||||||
|
@Test public void testTernaryExprExplicitAssociativity() throws Exception {
|
||||||
|
String grammar =
|
||||||
|
"grammar T;\n" +
|
||||||
|
"s @after {System.out.println($ctx.toStringTree(this));} : e EOF ;\n" + // must indicate EOF can follow or 'a<EOF>' won't match
|
||||||
|
"e :<assoc=right> e '*' e" +
|
||||||
|
" |<assoc=right> e '+' e" +
|
||||||
|
" |<assoc=right> e '?' e ':' e" +
|
||||||
|
" |<assoc=right> e '=' e" +
|
||||||
|
" | ID" +
|
||||||
|
" ;\n" +
|
||||||
|
"ID : 'a'..'z'+ ;\n" +
|
||||||
|
"WS : (' '|'\\n') -> skip ;\n";
|
||||||
|
String[] tests = {
|
||||||
|
"a", "(s (e a) <EOF>)",
|
||||||
|
"a+b", "(s (e (e a) + (e b)) <EOF>)",
|
||||||
|
"a*b", "(s (e (e a) * (e b)) <EOF>)",
|
||||||
|
"a?b:c", "(s (e (e a) ? (e b) : (e c)) <EOF>)",
|
||||||
|
"a=b=c", "(s (e (e a) = (e (e b) = (e c))) <EOF>)",
|
||||||
|
"a?b+c:d", "(s (e (e a) ? (e (e b) + (e c)) : (e d)) <EOF>)",
|
||||||
|
"a?b=c:d", "(s (e (e a) ? (e (e b) = (e c)) : (e d)) <EOF>)",
|
||||||
|
"a? b?c:d : e", "(s (e (e a) ? (e (e b) ? (e c) : (e d)) : (e e)) <EOF>)",
|
||||||
|
"a?b: c?d:e", "(s (e (e a) ? (e b) : (e (e c) ? (e d) : (e e))) <EOF>)",
|
||||||
|
};
|
||||||
|
runTests(grammar, tests, "s");
|
||||||
|
}
|
||||||
|
|
||||||
@Test public void testExpressions() throws Exception {
|
@Test public void testExpressions() throws Exception {
|
||||||
String grammar =
|
String grammar =
|
||||||
|
|
Loading…
Reference in New Issue