antlr4 puppet-style declarative grammar issue - antlr4

I'm trying to write a grammer for a puppet-styled declarative language. I have my basic grammar, and a JUnit test trying to implement a Listener approach to loading it. The JUnit test captures the first resource but not the second resource, its almost like the resources rule is never getting evaluated. I added logging statements to my Loader, and resources never gets entered/exited.
grammar Bosse;
resources : resource+;
resource : ID LBRACE STRING COLON attributes RBRACE ;
attributes : keyvalue ( COMMA keyvalue )* COMMA? ;
keyvalue : ID ASSIGN expr ;
expr : STRING
| INT
| FLOAT
;
ID : [a-z]+ ;
STRING : SQUOTE (SQESC|.)*? SQUOTE;
SQESC : '\\\'' | '\\\\' ;
INT : DIGIT+ ;
FLOAT : DIGIT+ '.' DIGIT*
| '.' DIGIT+
;
ASSIGN : '=>' ;
LBRACE : '{' ;
RBRACE : '}' ;
fragment
SQUOTE : '\'' ;
COLON : ':' ;
COMMA : ',' ;
WS : (' '|'\n'|'\t'|'\r')+ -> skip ;
fragment
DIGIT : [0-9] ;
My BaseListener implementation:
public class BosseLoader extends BosseBaseListener {
private List<Resource> resources = new ArrayList<Resource>();
private String resourceType;
private String resourceTitle;
private Map<String,Object> attributes = new HashMap<String,Object>();
private String parseString(String str) {
str = str.substring(1, str.length()-1);
str.replaceAll("\\\\(.)", "\1");
return str;
}
public List<Resource> getResources() {
return resources;
}
#Override
public void enterResources(ResourcesContext ctx) {
log.finest("entered "+ctx);
}
#Override
public void exitResources(ResourcesContext ctx) {
log.finest("entered "+ctx);
}
#Override
public void enterResource(ResourceContext ctx) {
log.finest("entered "+ctx);
resourceType = ctx.ID().getText();
resourceTitle = parseString(ctx.STRING().getText());
}
#Override
public void exitResource(ResourceContext ctx) {
log.finest("entered "+ctx);
Resource r = new Resource() {
public String getTitle() {
return resourceTitle;
}
public String toString() {
String type = resourceType.substring(0,1).toUpperCase()+resourceType.substring(1);
return type+"["+resourceTitle+"]";
}
};
resources.add(r);
System.out.println(r);
super.exitResource(ctx);
}
#Override
public void enterAttributes(AttributesContext ctx) {
log.finest("entered "+ctx);
attributes = new HashMap<String,Object>();
}
#Override
public void exitKeyvalue(KeyvalueContext ctx) {
log.finest("entered "+ctx);
attributes.put(ctx.ID().getText(),ctx.expr().getText());
}
}
My JUnit test
#Test
public void testSyntaxDouble() {
System.out.println("testSyntaxDouble()");
String data = "user { 'ruckc' : ensure => 'present' }\n"+
"user { 'cruck' : ensure => 'present' }\n";
BosseLoader loader = new BosseLoader();
BosseLexer lexer = new BosseLexer(new ANTLRInputStream(data));
CommonTokenStream tokens = new CommonTokenStream(lexer);
BosseParser parser = new BosseParser(tokens);
ParseTree tree = parser.resource();
ParseTreeWalker walker = new ParseTreeWalker();
walker.walk(loader, tree);
assertEquals(2,loader.getResources().size());
Resource r = loader.getResources().get(0);
assertEquals("ruckc", r.getTitle());
assertEquals("User[ruckc]", r.toString());
r = loader.getResources().get(1);
assertEquals("cruck", r.getTitle());
assertEquals("User[cruck]", r.toString());
}
My test output:
testSyntaxDouble()
FINEST: AM org.cruck.bosse.language.BosseLoader enterResource entered []
FINEST: AM org.cruck.bosse.language.BosseLoader enterAttributes entered [19]
FINEST: AM org.cruck.bosse.language.BosseLoader exitKeyvalue entered [22 19]
FINEST: AM org.cruck.bosse.language.BosseLoader exitResource entered []
User[ruckc]
Failed tests:
testSyntaxDouble(org.cruck.bosse.language.BosseLanguageTest): expected:<2> but was:<1>

Related

Create an object from Abstract type class in Xamarin.iOS

In my Xamarin.iOS binding project, I have a generated class called LSMAUsecase.g.cs. following is the content of that class.
namespace SightCallBinding {
[Protocol (Name = "LSMAUsecase", WrapperType = typeof (LSMAUsecaseWrapper))]
[ProtocolMember (IsRequired = true, IsProperty = true, IsStatic = false, Name = "Name", Selector = "name", PropertyType = typeof (string), GetterSelector = "name", ArgumentSemantic = ArgumentSemantic.None)]
public interface ILSMAUsecase : INativeObject, IDisposable
{
[Preserve (Conditional = true)]
string Name {
[Export ("name")]
get;
}
}
internal sealed class LSMAUsecaseWrapper : BaseWrapper, ILSMAUsecase {
[Preserve (Conditional = true)]
public LSMAUsecaseWrapper (IntPtr handle, bool owns)
: base (handle, owns)
{
}
[BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)]
public string Name {
[Export ("name")]
get {
return NSString.FromHandle (global::ApiDefinitions.Messaging.IntPtr_objc_msgSend (this.Handle, Selector.GetHandle ("name")));
}
}
}
}
namespace SightCallBinding {
[Protocol()]
[Register("LSMAUsecase", false)]
[Model]
public unsafe abstract partial class LSMAUsecase : NSObject, ILSMAUsecase {
[BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)]
[EditorBrowsable (EditorBrowsableState.Advanced)]
[Export ("init")]
protected LSMAUsecase () : base (NSObjectFlag.Empty)
{
IsDirectBinding = false;
InitializeHandle (global::ApiDefinitions.Messaging.IntPtr_objc_msgSendSuper (this.SuperHandle, global::ObjCRuntime.Selector.GetHandle ("init")), "init");
}
[BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)]
[EditorBrowsable (EditorBrowsableState.Advanced)]
protected LSMAUsecase (NSObjectFlag t) : base (t)
{
IsDirectBinding = false;
}
[BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)]
[EditorBrowsable (EditorBrowsableState.Advanced)]
protected internal LSMAUsecase (IntPtr handle) : base (handle)
{
IsDirectBinding = false;
}
[BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)]
public abstract string Name {
[Export ("name")]
get;
}
} /* class LSMAUsecase */
}
I want to pass LSMaUsecase object to a method. But I cannot create any object from this since it is an abstract class. And I cannot even cast into LSMAUsecase type. It says, invalid cast. Is there any way to resolve this How can I pass this type of object. following is the related method in the interface.
[BindingImpl(BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)]
[Export("sendInvitationForUsecasePhone:toPhone:withReference:andNotify:")]
[Preserve(Conditional = true)]
void SendInvitationForUsecasePhone(LSMAUsecase usecase, string phoneNumber, string reference, [BlockProxy(typeof(NIDActionArity2V0))] Action<LSMAPincodeStatus_t, NSString> block);
UPDATE
This is my extended class
public class Usecase : LSMAUsecase
{
public override string Name { get; }
public Usecase()
: base()
{
}
}
Then I am getting this exception since I don't have any value for "Name"(Name is get only).
Foundation.MonoTouchException: Objective-C exception thrown. Name:
NSInvalidArgumentException Reason: -[LSMAHandler
sendInvitationForUsecasePhone:toPhone:withReference:andNotify:]:
unrecognized selector sent to instance 0x282458780

Dataset not showing any columns

I am new to spark and trying to learn it. I am trying to create a Dataset from a textFile using a class. When i do a dataset.show(), it shows all blank and columns length shows 0.
Code:
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.Function;
import org.apache.spark.sql.Dataset;
import org.apache.spark.sql.Row;
import org.apache.spark.sql.SparkSession;
public class DatasetExample {
public static void main(String[] args) {
test(fileName);
}
static final String fileName = "inputFile";
static SparkConf conf = new SparkConf().setMaster("local").setAppName("Test");
static JavaSparkContext sc = new JavaSparkContext(conf);
static SparkSession session = SparkSession.builder().config(conf).getOrCreate();
private static void test(String fileName){
JavaRDD<Input> rdd = sc.textFile(fileName).map(new Function<String, Input>() {
#Override
public Input call(String s) throws Exception {
String[] str = s.split(",");
System.out.println(str[0] + " and " + str[1] + " and " + str[2]);
return new Input(str[0], str[1], Integer.parseInt(str[2]));
}
});
Dataset<Row> dataSet = session.createDataFrame(rdd, Input.class);
dataSet.show();
System.out.println("Column length is: " + dataSet.columns().length);
}
static class Input{
String key;
String value;
int number;
Input(String key, String value, int number){
this.key = key;
this.value = value;
this.number = number;
}
}
}
Output shown is:
foo and A and 1
foo and A and 2
foo and A and 1
foo and B and 2
foo and B and 1
bar and C and 2
bar and D and 3
dek and X and 3
max and X and 3
eer and P and 3
++
||
++
||
||
||
||
||
||
||
||
||
||
++
Column length is: 0
I do not want to explicitly define schema but I want it to take schema from class structure. What I might be missing?
From JavaBeans Wiki Definition:
In computing based on the Java Platform, JavaBeans are classes that
encapsulate many objects into a single object (the bean). They are
serializable, have a zero-argument constructor, and allow access to
properties using getter and setter methods
So, make it public and generate getter/setter:
public static class Input {
String key;
String value;
int number;
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
public Input(String key, String value, int number) {
this.key = key;
this.value = value;
this.number = number;
}
}
and you will have output.

get vs getProperty in groovy

It surprise me!
According to the document of groovy, groovy may use "getProperty" method to get the property of a object. So when I want to change the behavier of getting property on the special object, I use a category class to override the "getProperty" method. However, it does not work.
At last, I found groovy framework use the "get" method in the category class to get property, even if the object is not a map.
My question is that is it a bug or groovy just work like that.
This is the category class.
class DynaBeanExtension {
public static void setProperty(DynaBean bean, String propertyName, def newValue) {
try {
PropertyUtilsBean pu = null;
if (bean instanceof CustomWrapDynaBean) {
pu = bean.propertyUtilsBean;
}
if (pu != null) {
pu.setProperty(bean, propertyName, newValue);
} else {
PropertyUtils.setProperty(bean, propertyName, newValue);
}
} catch (IllegalArgumentException ex) {
bean.propertyMissing(propertyName, newValue);
}
}
public static def getProperty(DynaBean bean, String propertyName) {
try {
PropertyUtilsBean pu = null;
if (bean instanceof CustomWrapDynaBean) {
pu = bean.propertyUtilsBean;
}
if (pu != null) {
return pu.getProperty(bean, propertyName);
} else {
return PropertyUtils.getProperty(bean, propertyName);
}
} catch (IllegalArgumentException ex) {
return bean.propertyMissing(propertyName);
}
}
public static def get(DynaBean bean, String propertyName) {
try {
PropertyUtilsBean pu = null;
if (bean instanceof CustomWrapDynaBean) {
pu = bean.propertyUtilsBean;
}
if (pu != null) {
return pu.getProperty(bean, propertyName);
} else {
return PropertyUtils.getProperty(bean, propertyName);
}
} catch (IllegalArgumentException ex) {
return bean.propertyMissing(propertyName);
}
}
This is the test code:
public static class TestSubClass {
private final int e = 3, f = 4;
private final Map<String, Object> m = new HashMap<>();
public int getE() {
return e;
}
public int getF() {
return f;
}
public Map<String, Object> getM() {
return m;
}
#Override
public String toString() {
return "TestSubClass{" + "e=" + e + ", f=" + f + ", m=" + m + '}';
}
}
public static class TestClass {
private final int a = 1;
private final TestSubClass b = new TestSubClass();
public int getA() {
return a;
}
public TestSubClass getB() {
return b;
}
#Override
public String toString() {
return "TestClass{" + "a=" + a + ", b=" + b + '}';
}
}
Map<String, String> pMap = new HashMap<>();
pMap.put("b.e", "c");
PropertyUtilsBean pu = new PropertyUtilsBean();
pu.setResolver(new ExResolver(pMap));
TestClass testObj = new TestClass();
DynaBean bean = new CustomWrapDynaBean(testObj, pu);
int c = use(DynaBeanExtension) {
bean.c;
}
This is the code of ExResolver:
public class ExResolver implements Resolver {
private static final char NESTED = '.';
private static final char MAPPED_START = '(';
private static final char MAPPED_END = ')';
private static final char INDEXED_START = '[';
private static final char INDEXED_END = ']';
private final Resolver resolver;
private final Map<String, String> pMap;
public ExResolver(Map<String, String> pMap) {
this(new DefaultResolver(), pMap);
}
public ExResolver(Resolver resolver, Map<String, String> pMap) {
this.resolver = resolver;
this.pMap = new HashMap<>(pMap);
}
private String resolveExpr(String expression) {
for (Map.Entry<String, String> entry : pMap.entrySet()) {
if (expression.startsWith(entry.getValue())) {
String to = entry.getValue();
if (expression.length() == entry.getValue().length()) {
return entry.getKey();
} else {
int toTest = expression.codePointAt(to.length());
if (toTest == NESTED || toTest == MAPPED_START || toTest == INDEXED_START) {
return entry.getKey() + expression.substring(to.length(), expression.length());
} else {
return expression;
}
}
}
}
return expression;
}
#Override
public int getIndex(String expression) {
expression = resolveExpr(expression);
return resolver.getIndex(expression);
}
#Override
public String getKey(String expression) {
expression = resolveExpr(expression);
return resolver.getKey(expression);
}
#Override
public String getProperty(String expression) {
expression = resolveExpr(expression);
return resolver.getProperty(expression);
}
#Override
public boolean hasNested(String expression) {
expression = resolveExpr(expression);
return resolver.hasNested(expression);
}
#Override
public boolean isIndexed(String expression) {
expression = resolveExpr(expression);
return resolver.isIndexed(expression);
}
#Override
public boolean isMapped(String expression) {
expression = resolveExpr(expression);
return resolver.isMapped(expression);
}
#Override
public String next(String expression) {
expression = resolveExpr(expression);
return resolver.next(expression);
}
#Override
public String remove(String expression) {
expression = resolveExpr(expression);
return resolver.remove(expression);
}
}
"get" is invoked, not "getProperty"
What's more, in the real situation DynaBeanExtension is compiled with groovy. The construction of bean is compiled with java. Then by using binding, I put it into the test code which is a runtime script executed by java code.
This happens in the compilation itself. Let's look at a simpler example.
class Main {
static void main(def args) {
Foo foo = new Foo()
foo.str = ""
foo.str
}
}
For Groovy classes
class Foo {
String str
}
If you decompile the Main class, you'll see it is
public class Main implements GroovyObject {
public Main() {
Main this;
CallSite[] arrayOfCallSite = $getCallSiteArray();
MetaClass localMetaClass = $getStaticMetaClass();
this.metaClass = localMetaClass;
}
public static void main(String... args) {
CallSite[] arrayOfCallSite = $getCallSiteArray();
Foo foo = (Foo)ScriptBytecodeAdapter.castToType(arrayOfCallSite[0].callConstructor(Foo.class), Foo.class);
String str = "";
ScriptBytecodeAdapter.setGroovyObjectProperty(str, Main.class, foo, (String)"str");
arrayOfCallSite[1].callGroovyObjectGetProperty(foo);
}
}
A .[property] = call gets compiled to a ScriptBytecodeAdapter.setGroovyObjectProperty, that in turn calls the chain MetaClassImpl.setProperty > MetaMethod.doMethodInvoke > CachedMethod.invoke > java.lang.reflect.Method.invoke > [setter]
And a .[property] call gets compiled to a arrayOfCallSite[1].callGroovyObjectGetProperty, that in turn calls the chain
AbstractCallSite.callGroovyObjectGetProperty > GetEffectivePogoPropertySite.getProperty > MethodMetaProperty$GetBeanMethodMetaProperty.getProperty > MetaMethod.doMethodInvoke > CachedMethod.invoke > java.lang.reflect.Method.invoke > [getter]
For Java classes
If you use a Java version of the class being called, like this
public class Foo {
private String str;
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
}
The same Main decompiles to
public class Main implements GroovyObject {
public Main() {
Main this;
CallSite[] arrayOfCallSite = $getCallSiteArray();
MetaClass localMetaClass = $getStaticMetaClass();
this.metaClass = localMetaClass;
}
public static void main(String... args) {
CallSite[] arrayOfCallSite = $getCallSiteArray();
Foo foo = (Foo)ScriptBytecodeAdapter.castToType(arrayOfCallSite[0].callConstructor(Foo.class), Foo.class);
String str = "";
ScriptBytecodeAdapter.setProperty(str, null, foo, (String)"str");
arrayOfCallSite[1].callGetProperty(foo);
}
}
A .[property] = call gets compiled to a ScriptBytecodeAdapter.setProperty, that in turn calls the chain [Class].setProperty > InvokerHelper.setProperty -> MetaClassImpl.setProperty > MetaMethod.doMethodInvoke > CachedMethod.invoke > java.lang.reflect.Method.invoke > [setter]
And a .[property] call gets compiled to a arrayOfCallSite[1].callGroovyObjectGetProperty, that in turn calls the chain
AbstractCallSite.callGetProperty > GetEffectivePojoPropertySite.getProperty > MethodMetaProperty$GetBeanMethodMetaProperty.getProperty > MetaMethod.doMethodInvoke > CachedMethod.invoke > java.lang.reflect.Method.invoke > [getter]
To correct your code
As you can see from these dispatch chains, you've overridden the getter correctly (since it happens in the class itself), but if you want to override getProperty or setProperty, you have to do this in metaClass, and not the class itself. The behavior you're seeing is expected. This code demonstrates how to override each
class Foo {
String bar
}
// override using setter in category
#Category(Foo)
class FooCategory {
public String getBar() {
println "in getter"
}
public void setBar(String bar) {
println "in setter"
}
}
use (FooCategory) {
Foo foo = new Foo()
foo.bar = ""
foo.bar
}
// override using metaClass
Foo.metaClass.getProperty { String pname ->
println "in getProperty"
}
Foo.metaClass.setProperty { String pname, Object pValue ->
println "in setProperty"
}
Foo foo = new Foo()
foo.bar = ""
foo.bar
outputs
in setter
in getter
in setProperty
in getProperty
And because the getProperty/setProperty call makes the dispatch (eventually) to the getter/setter, you can prevent the getter/setter from being called at all, like this
class Foo {
String bar
}
Foo.metaClass.getProperty { String pname ->
println "in getProperty"
}
Foo.metaClass.setProperty { String pname, Object pValue ->
println "in setProperty"
}
#Category(Foo)
class FooCategory {
String getBar() {
println "in getter"
}
void setBar(String bar) {
println "in setter"
}
}
use (FooCategory) {
Foo foo = new Foo()
foo.bar = "hi foo1"
foo.bar
}
outputs
in setProperty
in getProperty

Drools : How to get String in List<String>

I want to get String in List< String > but did not work.
Drools Rule
import schedule.Schedule
import schedule.Control_Exam_List
rule "People"
salience 5
when
$controlExamList : Control_Exam_List( ) from accumulate( $scheduleCheck : Schedule( $scheduleCECheck1 : control1 , $scheduleCECheck2 : control2 , $scheduleCECheck3 : control3 ) ,
init( Control_Exam_List CEL = new Control_Exam_List(); ),
action( CEL.addData($scheduleCECheck1); CEL.addData($scheduleCECheck2); CEL.addData($scheduleCECheck3); ),
result( CEL ) )
then
System.out.println("Test1: "+$controlExamList);
end
The result return list but I get all string from this list.
Result : Still list< String >
Test1: schedule.Control_Exam_List#22916a
Control_Exam_List Class : List< String >
import java.util.ArrayList;
import java.util.List;
public class Control_Exam_List {
private List<String> code = new ArrayList<String>();
public void addData(String code){
if(this.code.contains(code) != true && !code.equals(""))
this.code.add(code);
}
public List<String> getCode() {
return code;
}
}
Schedule Class : Accumulate from this class
public class Schedule {
private String control1 = "", control2 = "", control3 = "";
public String getControl1() {
return control1;
}
public String getControl2() {
return control2;
}
public String getControl3() {
return control3;
}
public void setControlExam1(String ce_code) {
this.control1 = ce_code;
}
public void setControlExam2(String ce_code) {
this.control2 = ce_code;
}
public void setControlExam3(String ce_code) {
this.control3 = ce_code;
}
}
This is almost as you'd do it Java, with $controlExamList being bound to a Control_Exam_List object; although, in Drools, you may have to cast:
then
for( Object obj: $controlExamList.getCode() ){
System.out.println( (String)obj );
}
end
But you can also try:
then
for( String str: $controlExamList.getCode() ){
System.out.println( str );
}
end

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!
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:
Parser.addParseListener(ParseTreeListener)
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.

Resources