'bug or feature? enter method of listener on labeled rule
After playing a bit with parse listeners I found a behaviour I didn't expect. My question to you is, am I wrong with my expectation and is this behaviour wanted or is it a bug? If the behaviour is wanted, please explain it.
Here the sample grammar:
grammar Labeled;
file: stmt;
stmt: stmt '+' stmt # Add
| stmt '*' stmt # Mult
| FLOAT # Value
| INTEGER # Value
;
FLOAT: '-'? DIGIT* '.' DIGIT+;
INTEGER: '-'? DIGIT+;
COMMENT: (COMMENT_LINE | COMMENT_BLOCK) -> skip;
WS: [ \t\r\n] -> skip;
fragment
DIGIT: [0-9];
COMMENT_LINE: '//' ~'\n'*;
COMMENT_BLOCK: '/*' .*? '*/';`
Here the sample listener:
import org.antlr.v4.runtime.misc.NotNull;
import java.util.HashMap;
import java.util.Map;
public class TestListener extends LabeledBaseListener {
public static final String ALL_KEY = "All";
public static final String MULT_KEY = "Mult";
public static final String ADD_KEY = "Add";
public static final String VALUE_KEY = "Value";
public static final String FILE_KEY = "File";
public Map<String, Integer> enterValues = new HashMap<>();
public Map<String, Integer> exitValues = new HashMap<>();
@Override
public void enterMult(@NotNull LabeledParser.MultContext ctx) {
addEnter(ALL_KEY);
addEnter(MULT_KEY);
}
@Override
public void exitMult(@NotNull LabeledParser.MultContext ctx) {
addExit(ALL_KEY);
addExit(MULT_KEY);
}
@Override
public void enterValue(@NotNull LabeledParser.ValueContext ctx) {
addEnter(ALL_KEY);
addEnter(VALUE_KEY);
}
@Override
public void exitValue(@NotNull LabeledParser.ValueContext ctx) {
addExit(ALL_KEY);
addExit(VALUE_KEY);
}
@Override
public void enterFile(@NotNull LabeledParser.FileContext ctx) {
addEnter(ALL_KEY);
addEnter(FILE_KEY);
}
@Override
public void exitFile(@NotNull LabeledParser.FileContext ctx) {
addExit(ALL_KEY);
addExit(FILE_KEY);
}
@Override
public void enterAdd(@NotNull LabeledParser.AddContext ctx) {
addEnter(ALL_KEY);
addEnter(ADD_KEY);
}
@Override
public void exitAdd(@NotNull LabeledParser.AddContext ctx) {
addExit(ALL_KEY);
addExit(ADD_KEY);
}
// region map helper
private static void addValue(Map<String, Integer> valueMap, String name) {
if(valueMap.containsKey(name)) {
valueMap.put(name, valueMap.get(name) + 1);
} else {
valueMap.put(name, 1);
}
}
private void addEnter(String name) {
addValue(enterValues, name);
}
private void addExit(String name) {
addValue(exitValues, name);
}
// endregion
}
The main class:
import org.antlr.v4.runtime.ANTLRFileStream;
import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CommonTokenStream;
import java.io.File;
import java.io.IOException;
import java.util.Map;
public class Main {
public static void main(String[] args) throws IOException {
String filePath = args[0];
ANTLRInputStream input = new ANTLRFileStream(filePath);
LabeledLexer lexer = new LabeledLexer(input);
CommonTokenStream token = new CommonTokenStream(lexer);
LabeledParser parser = new LabeledParser(token);
TestListener testListener = new TestListener();
parser.addParseListener(testListener);
parser.file();
System.out.println("Enter Values:");
System.out.println(getMapString(testListener.enterValues));
System.out.println("Exit Values:");
System.out.println(getMapString(testListener.exitValues));
System.out.println("End");
}
private static String getMapString(Map<?, ?> map) {
StringBuffer buffer = new StringBuffer();
for(Map.Entry<?, ?> curEntry: map.entrySet()) {
buffer.append("Key: " + curEntry.getKey() + "\tValue: " + curEntry.getValue() + "\n");
}
String result = buffer.toString();
return result;
}
}
Now when I execute with a file with content:
-4 + 8
The output will be:
Enter Values:
Key: File Value: 1
Key: Add Value: 1
Key: All Value: 2
Exit Values:
Key: Value Value: 2
Key: File Value: 1
Key: Add Value: 1
Key: All Value: 4
End
But I expect this output:
Enter Values:
Key: Value Value: 2
Key: File Value: 1
Key: Add Value: 1
Key: All Value: 4
Exit Values:
Key: Value Value: 2
Key: File Value: 1
Key: Add Value: 1
Key: All Value: 4
End
So as you can see the enterValue() method is never called. After some more tests it seams that the enterXXX() method is not be called if there is only ONE Token/Rule in the rule's alternative.
Thanks in advance!
Solution 1:[1]
Yes, this behavior is expected (or at a minimum, is allowed). The behavior itself as well as the rationale for it are included in the documentation for the addParseListener
method you used:
Solution 2:[2]
To make this work the way you expect it to, use the ParseTreeWalker directly. Something like:
ParseTree tree = parser.json();
jsonListener listener = new jsonListener();
ParseTreeWalker walker = new ParseTreeWalker();
walker.walk(listener, tree);
or in your case:
LabeledParser parser = new LabeledParser(token);
ParseTree tree = parser.file();
TestListener testListener = new TestListener();
ParseTreeWalker walker = new ParseTreeWalker();
walker.walk(testListener, tree);
This will result in enter methods being called for labeled rule alternatives.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Sam Harwell |
Solution 2 | Chris |