Antlr4语言翻译-从访问者类中分离模板逻辑?
我正在研究实用地将大量相对简单的TSQL代码转换为Groovy代码。当然有很多原因,但主要原因只是看看是否可以做到,并在此过程中学习编译器/语法等 Antlr4似乎是解决这个问题的理想工具(Java是一个加号) 标记化/解析TSQL(使用语法文件),并使用生成的侦听器/访问者读取树非常简单 我知道我可以在继承的访问者中创建Groovy代码的字符串表示,但是将匹配的Groovy令牌值与我的TSQLVisitor耦合似乎不是最干净的解决方案 这里的最佳做法是什么?通常用于在Antlr4中将一种语言映射到另一种语言? 我正在考虑的事情:Antlr4语言翻译-从访问者类中分离模板逻辑?,antlr,antlr4,stringtemplate,Antlr,Antlr4,Stringtemplate,我正在研究实用地将大量相对简单的TSQL代码转换为Groovy代码。当然有很多原因,但主要原因只是看看是否可以做到,并在此过程中学习编译器/语法等 Antlr4似乎是解决这个问题的理想工具(Java是一个加号) 标记化/解析TSQL(使用语法文件),并使用生成的侦听器/访问者读取树非常简单 我知道我可以在继承的访问者中创建Groovy代码的字符串表示,但是将匹配的Groovy令牌值与我的TSQLVisitor耦合似乎不是最干净的解决方案 这里的最佳做法是什么?通常用于在Antlr4中将一种语言映
最佳实践取决于您的目标。如果转换不能引入或必须尽量减少任何增加的技术负担或性能或维护费用,则Ira的评论控制 但是,如果性能和维护不是关键问题,转换在语义上接近1:1,并且您能够在目标环境中添加运行时支持代码,那么Antlr4样式的转换就成为可能。当然,源语言和目标语言之间的语义差异越大,它就变得越困难——目标运行时支持库的大小和复杂性会适得其反。而且,只需要一个深层次的差异就可以驱动对像Ira这样的分析工具的需求 假设已经开发了足够的Groovy库,那么目标代码的生成量将减少到访问者onetry和onExit例程所要求的一行代码。通过抽象渲染,可以在一定程度上减少耦合:
public class Render {
private static final String templateDir = "some/path/to/templates";
private STGroupFile blocksGroup;
private STGroupFile stmtGroup;
public Render() {
blocksGroup = new STGroupFile(Strings.concatAsClassPath(templateDir, "Blocks.stg"));
stmtGroup = new STGroupFile(Strings.concatAsClassPath(templateDir, "Statements.stg"));
}
public String gen(GenType type, String name) {
return gen(type, name, null);
}
/**
* type is an enum, identifying the group template
* name is the template name within the group
* varMap contains the named values to be passed to the template
*/
public String gen(GenType type, String name, Map<String, Object> varMap) {
Log.debug(this, name);
STGroupFile stf = null;
switch (type) {
case BLOCK:
stf = blocksGroup;
break;
case STMT:
stf = stmtGroup;
break;
}
ST st = stf.getInstanceOf(name);
if (varMap != null) {
for (String varName : varMap.keySet()) {
try {
st.add(varName, varMap.get(varName));
} catch (NullPointerException e) {
Log.error(this, "Error adding attribute: " + name + ":" + varName + " [" + e.getMessage() + "]");
}
}
}
return st.render();
}
}
公共类渲染{
私有静态最终字符串templateDir=“some/path/to/templates”;
私有STGroupFile块组;
私有STGroupFile stmtGroup;
公共渲染(){
blocksGroup=newstgroupfile(Strings.concatAsClassPath(templateDir,“Blocks.stg”);
stmtGroup=newstgroupfile(Strings.concatAsClassPath(templateDir,“Statements.stg”);
}
公共字符串生成器(GenType类型,字符串名称){
返回gen(类型、名称、空);
}
/**
*类型是一个枚举,用于标识组模板
*name是组中的模板名称
*varMap包含要传递给模板的命名值
*/
公共字符串生成器(GenType类型、字符串名称、Map varMap){
Log.debug(这个,名称);
STGroupFile stf=null;
开关(类型){
案例块:
stf=区块组;
打破
案例STMT:
stf=stmtGroup;
打破
}
ST=stf.getInstanceOf(名称);
if(varMap!=null){
for(字符串varName:varMap.keySet()){
试一试{
st.add(varName,varMap.get(varName));
}捕获(NullPointerException e){
Log.error(这是“添加属性时出错:“+name+”:“+varName+”[“+e.getMessage()+”]);
}
}
}
返回st.render();
}
}
您想了解最佳实践吗?还是使用ANTLR的最佳实践?我认为这两个都是非常不同的。两者都是,当然,如果Antlr4是用于此工作的错误工具的话。我想这就是你想要的:关于链接的一些好的高级背景信息——包括我正在研究的东西被称为“transpiler”,但我更多的是寻找技术最佳实践——也就是说,映射是在哪里定义的。另外,由于这更像是一个宠物项目,我更喜欢使用开源工具。最佳实践是“不要只使用解析器生成器”。不幸的是,这大部分都是开源的。您可以获得的唯一开源的“不仅仅是解析器生成器”是TXL、Stratego/MPL。为陡峭的学习曲线做好准备。(仅仅一个解析器生成器所带来的学习曲线更为陡峭;它包括发现为什么这还不够,但首先要弄清楚如何使用它来完成这项工作。)@GRosenburg说得对:如果你的语言能够很好地映射到目标,而你不关心代码效率,您可以执行“动态”(或在树上行走)代码生成。问题是你,翻译设计师,必须事先决定这是否属实。如果你决定这样做,而且你是对的,那么这种翻译是实用的。真正的问题是你在打赌你是对的。如果你被证明是错的,你的整个翻译器实现必须被替换,这是一个相当高的成本。所以,你可以打赌和祈祷。或者,使用坚实的基础。@GRosenburg提出了一个关键点:通过在目标环境中构建额外的基础设施来模拟原始langauge语义,可以缩小源语言和目标语言之间的语义差距。(如果你用这种方法走得够远的话,你最终会建立一个相当于原始语言的解释器)。IMHO,这是将无法克服的语义鸿沟最小化的唯一好方法。