我已经阅读很多试图找到一种方法来干净地使用ANTLR的树语法中的列表。这是我尝试过的和他们的结果(我真的希望我错过了一些微不足道的事情)......
使用+ =语法
program returns [someInterface result]
: m+=method* EOF {result = new SomeClass(m);};
method returns [SomeMethod result] : <definition here>
这失败了......
规则'+ ='不允许使用列表标签 没有输出选项
如果我将输出设置为“AST”或“template”(唯一选项),则生成的类的方法签名会发生变化。也就是说,m
不是由SomeMethod列表,而是分别是节点或模板列表。如果有办法使这种方法有效,我愿意接受建议。
使用规则范围
program returns [CompilesToJavaByteCode result]
scope {
List<SomeMethod> methods;
}
@init {
$program::methods = new ArrayList<SomeMethod>();
}
: (m=method {$program::methods.add(m);})*
EOF {result = new SomeClass($program::methods);};
这似乎有效,但我承认我还没有使用嵌套/递归的情况进行测试。
最终目标
我想构建一组代表我的语言的类(Class,Method,Variable,Statement,ect),这样我就可以在生成编译代码之前做一些静态分析和优化。为此,我需要能够使用列表。我希望+ =语法“正常工作”,但我可能会遗漏一些东西。第二种方法有效,但看起来过于冗长和不优雅。
问题
在ANTLR的树语法中使用列表传递给我的具体类的原因是什么?
答案 0 :(得分:6)
您可以从示例中删除范围,并使用局部变量完成所有操作。
program returns [CompilesToJavaByteCode result]
@init {
List<SomeMethod> methods = new ArrayList<SomeMethod>();
}
: (m=method { methods.add($m.result); })* EOF
{ $result = new SomeClass(methods); };
这就是我们在工作中对此案例所做的工作。另一个选择是让你的方法规则处理它:
program returns [CompilesToJavaByteCode result]
@init {
List<SomeMethod> methods = new ArrayList<SomeMethod>();
}
: method[methods]* EOF { $result = new SomeClass(methods); };
method [List<SomeMethod> methods]
: ...
{ methods.add(new SomeMethod(...); };
我真的不喜欢第二个选项,因为方法规则可能不应该关心它的结果是做什么的。但是您可以想象一个结构,其中顶部规则创建ClassBeingCompiled
,其余代码使用.addMethod()
逐步填充它。