您正在尝试做的事情称为程序转换,即从另一个程序自动生成一个程序。您所做的“错误”是假设解析器就是您所需要的,并且发现它不是并且您必须填补空白。
可以很好地做到这一点的工具有解析器(构建 AST),意味着修改 AST(过程和模式定向),以及将(修改后的)AST 转换回合法源代码的漂亮打印机。您似乎在为 ANTLR 不附带漂亮打印机这一事实而苦苦挣扎。这不是其哲学的一部分;ANTLR 是一个(精细的)解析器生成器。其他答案建议使用 ANTLR 的“字符串模板”,它们本身不是漂亮的打印机,但可以用来实现一个,但代价是实现一个。这比看起来更难做到;请参阅我关于将 AST 编译回源代码的答案。
这里真正的问题是广为流传但错误的假设,即“如果我有一个解析器,我就可以很好地构建复杂的程序分析和转换工具”。请参阅我关于解析后的生活的文章对此进行了长时间的讨论;基本上,您需要更多“只是”解析器的工具来执行此操作,除非您想自己重建大部分基础架构而不是继续您的任务。实际程序转换系统的其他有用特征包括典型的源到源转换,这大大简化了在树中查找和替换复杂模式的问题。
例如,如果您具有源到源转换功能(我们的工具DMS Software Reengineering Toolkit,您将能够使用这些 DMS 转换编写部分示例代码更改:
domain ECMAScript.
tag replace; -- says this is a special kind of temporary tree
rule barize(function_name:IDENTIFIER,list:expression_list,b:body):
expression->expression
= " \function_name ( '[' \list ']' ) "
-> "\function_name( \firstarg\(\function_name\), \replace\(\list\))";
rule replace_unit_list(s:character_literal):
expression_list -> expression_list
replace(s) -> compute_index_for(s);
rule replace_long_list(s:character_list, list:expression_list):
expression_list -> expression_list
"\replace\(\s\,\list)-> "compute_index_for\(\s\),\list";
使用规则外部“元”程序“first_arg”(知道如何在给定标识符“foo”的情况下计算“bar”[我猜你想这样做),以及给定字符串文字的“compute_index_for”,知道什么整数来替换它。
单独的重写规则具有参数列表“(....)”,其中表示子树的插槽被命名,左侧充当匹配模式,右侧充当替换,两者通常在元引号中 引用“将重写规则语言文本与目标语言(例如 JavaScript)文本分开。元引号内有很多元转义 **,表示特殊的重写规则语言项。通常这些是参数名称,代表任何类型的参数表示的名称树,或表示外部元过程调用(例如 first_arg;您会注意到它的参数列表( , )是元引用的!),或者最后是一个“标记”,例如“替换”,这是一种特殊的树,代表了未来进行更多转换的意图。
这组特定的规则通过用 barized 版本替换候选函数调用来工作,并带有额外的意图“替换”来转换列表。其他两个转换通过一次处理一个列表的元素来转换“替换”来实现意图,并将替换进一步推到列表的下方,直到它最终掉到最后并完成替换。(这是循环的转换等价物)。
您的具体示例可能会有所不同,因为您确实对细节并不精确。
应用这些规则来修改解析树后,DMS 可以简单地漂亮打印结果(某些配置中的默认行为是“解析到 AST,应用规则直到耗尽,漂亮打印 AST”,因为这很方便)。
你可以在(高中)代数作为DMS域看到“定义语言”、“定义重写规则”、“应用规则和漂亮打印”的完整过程。
其他程序转换系统包括TXL和Stratego。我们将 DMS 想象成这些的工业级版本,我们在其中构建了所有基础设施,包括许多标准语言解析器和漂亮的打印机。