Antlr4 语言翻译 - 将模板逻辑与访问者分开 class?
Antlr4 language translation - separating template logic from visitor class?
我正在考虑实用地将大量相对简单的 TSQL 代码转换为 Groovy 代码。当然有很多原因,但驱动原因只是看看是否可以完成,并在此过程中了解 compilers/grammers/ 等
Antlr4 似乎是解决这个问题的理想工具(Java 是一个加号)。
标记/解析 TSQL(使用语法文件),并使用生成的 Listener/Visitor 读取树非常简单。
我知道我可以在继承的访问者中创建 Groovy 代码的字符串表示,但是将匹配的 Groovy 标记值与我的 TSQLVisitor 耦合似乎不是最干净的解决方案.
此处最佳做法是什么?通常在 Antlr4 中将一种语言映射到另一种语言 ?
我正在考虑的事情:
- 使用 StringTemplate,并在 STG 文件中定义我的 groovy 代码
(我的 TSQLVisitor 会使用这些模板和 return 完整的字符串
Groovy 代码的表示)。
- 切换到Antlr3
支持在Grammar文件中直接添加StringTemplate逻辑
最佳做法取决于您的 objective。如果转换不得引入或必须最小化任何增加的技术包袱或性能或维护开销,则由 Ira 的评论控制。
然而,如果性能和维护不是本质问题,则转换在语义上接近 1:1,并且您有能力在目标环境中添加 运行 时间支持代码,然后 Antlr4 样式转换成为可能。当然,源语言和目标语言之间的语义差异越大,它就变得越困难——目标 运行 时间支持库的大小和复杂性会适得其反。而且,只需要一个根深蒂固的差异就可以推动对像 Ira 开发的分析工具的需求。
假设已开发出足够的 Groovy 库,目标代码的生成将减少到接近访问者 onEntry 和 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();
}
}
我正在考虑实用地将大量相对简单的 TSQL 代码转换为 Groovy 代码。当然有很多原因,但驱动原因只是看看是否可以完成,并在此过程中了解 compilers/grammers/ 等
Antlr4 似乎是解决这个问题的理想工具(Java 是一个加号)。
标记/解析 TSQL(使用语法文件),并使用生成的 Listener/Visitor 读取树非常简单。
我知道我可以在继承的访问者中创建 Groovy 代码的字符串表示,但是将匹配的 Groovy 标记值与我的 TSQLVisitor 耦合似乎不是最干净的解决方案.
此处最佳做法是什么?通常在 Antlr4 中将一种语言映射到另一种语言 ?
我正在考虑的事情:
- 使用 StringTemplate,并在 STG 文件中定义我的 groovy 代码 (我的 TSQLVisitor 会使用这些模板和 return 完整的字符串 Groovy 代码的表示)。
- 切换到Antlr3 支持在Grammar文件中直接添加StringTemplate逻辑
最佳做法取决于您的 objective。如果转换不得引入或必须最小化任何增加的技术包袱或性能或维护开销,则由 Ira 的评论控制。
然而,如果性能和维护不是本质问题,则转换在语义上接近 1:1,并且您有能力在目标环境中添加 运行 时间支持代码,然后 Antlr4 样式转换成为可能。当然,源语言和目标语言之间的语义差异越大,它就变得越困难——目标 运行 时间支持库的大小和复杂性会适得其反。而且,只需要一个根深蒂固的差异就可以推动对像 Ira 开发的分析工具的需求。
假设已开发出足够的 Groovy 库,目标代码的生成将减少到接近访问者 onEntry 和 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();
}
}