gpt4 book ai didi

parsing - 使用ANTLR分析和修改源代码;我做错了吗?

转载 作者:行者123 更新时间:2023-12-04 10:27:30 25 4
gpt4 key购买 nike

我正在编写一个程序,我需要解析 JavaScript 源文件、提取一些事实并插入/替换部分代码。给定以下代码,我需要做的各种事情的简化描述是:

foo(['a', 'b', 'c']);

摘录 'a' , 'b' , 和 'c'并将代码重写为:
foo('bar', [0, 1, 2]);

我正在使用 ANTLR 来满足我的解析需求,生成 C# 3 代码。其他人已经贡献了 JavaScript 语法。源代码的解析正在工作。

我遇到的问题是弄清楚如何真正正确地分析和修改源文件。我在实际解决问题时尝试采用的每种方法都将我带入了死胡同。我不禁认为我没有按预期使用该工具,或者在处理 AST 时我只是一个新手。

我的第一种方法是使用 TokenRewriteStream 进行解析并实现 EnterRule_*我感兴趣的规则的部分方法。虽然这似乎使修改 token 流变得非常容易,但我的分析没有足够的上下文信息。似乎我只能访问一个扁平的 token 流,这并不能告诉我关于整个代码结构的足够信息。例如,检测 foo 是否函数被调用,仅仅查看第一个标记是行不通的,因为这也会错误地匹配:
a.b.foo();

为了让我能够进行更复杂的代码分析,我的第二种方法是使用重写规则修改语法以生成更多的树。现在,第一个示例代码块生成:

程序
调用表达
标识符('foo')
参数列表
数组字面量
字符串文字('a')
字符串文字('b')
字符串文字('c')

这对于分析代码非常有用。但是,现在我无法轻松地重写代码。当然,我可以修改树结构来表示我想要的代码,但我不能用它来输出源代码。我曾希望与每个节点关联的标记至少能给我足够的信息,让我知道我需要在原始文本中的哪个位置进行修改,但我得到的只是标记索引或行/列号。要使用行号和列号,我将不得不对源代码进行一次尴尬的第二次遍历。

我怀疑我在理解如何正确使用 ANTLR 来做我需要的事情时遗漏了一些东西。我有没有更合适的方法来解决这个问题?

最佳答案

您正在尝试做的事情称为 program transformation ,即从另一个程序自动生成一个程序。您所做的“错误”是假设解析器就是您所需要的,并且发现它不是并且您必须填补空白。

可以很好地做到这一点的工具有解析器(构建 AST),意味着修改 AST(过程和模式定向),以及将(修改后的)AST 转换回合法源代码的 pretty-print 。您似乎在为 ANTLR 不附带 pretty-print 这一事实而苦苦挣扎。这不是其哲学的一部分; ANTLR 是一个(精细的)解析器生成器。其他答案建议使用 ANTLR 的“字符串模板”,它们本身不是 pretty-print ,但可以用来实现一个,但代价是实现一个。这比看起来更难做到;在 compiling an AST back to source code 上查看我的 SO 答案.

这里真正的问题是广为流传但错误的假设,即“如果我有一个解析器,我就可以很好地构建复杂的程序分析和转换工具”。请参阅我关于 Life After Parsing 的文章对此进行长时间的讨论;基本上,您需要更多“只是”解析器的工具来执行此操作,除非您想自己重建大部分基础架构而不是继续您的任务。实际程序转换系统的其他有用特征包括典型的源到源转换,这大大简化了在树中查找和替换复杂模式的问题。

例如,如果您具有源到源转换功能(我们的工具 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;您会注意到它的参数列表 ( , ) 是元引用的!),或者最后是一个“标签”,例如“replace”,它是一种特殊的树,代表 future 进行更多转换的意图。

这组特定的规则通过用 barized 版本替换候选函数调用来工作,并带有额外的意图“替换”来转换列表。其他两个转换通过一次处理一个列表的元素来转换“替换”来实现意图,并将替换进一步推到列表的下方,直到它最终掉到最后并完成替换。 (这是循环的转换等价物)。

您的具体示例可能会有所不同,因为您确实对细节并不精确。

应用这些规则来修改解析树后,DMS 可以简单地 pretty-print 结果(某些配置中的默认行为是“解析到 AST,应用规则直到耗尽, pretty-print AST”,因为这很方便)。

您可以在 (High School) Algebra as a DMS domain 看到“定义语言”、“定义重写规则”、“应用规则和 pretty-print ”的完整过程。 .

其他程序转换系统包括 TXLStratego .我们将 DMS 想象为这些的工业级版本,我们在其中构建了所有基础设施,包括 many standard language parsers and prettyprinters .

关于parsing - 使用ANTLR分析和修改源代码;我做错了吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11608084/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com