Alpha以多种语言重命名

时间:2011-04-29 20:11:56

标签: parsing scope multilingual rename abstract-syntax-tree

我的想法将是一个相当复杂的技术挑战:我希望能够以多种语言(尽可能多的)可靠地对重命名标识符进行alpha重命名。这需要特别考虑每种语言,我正在寻求建议,以便通过共享代码来最大限度地减少我需要做的工作量。像已经支持多种语言的统一解析或抽象语法框架这样的东西会很棒。

例如,这里有一些python代码:

def foo(x):
    def bar(y):
        return x+y
    return bar

x重命名为y,将x更改为y保留语义。所以它会变成:

def foo(y):
    def bar(y1):
        return y+y1
    return bar

了解我们如何将y重命名为y1以避免破坏代码?这就是为什么这是一个难题。看起来该程序必须非常了解范围的构成,而不仅仅是进行字符串搜索和替换。

我还希望保留尽可能多的格式:注释,间距,缩进。但这不是100%必要的,它会很好。

任何提示?

3 个答案:

答案 0 :(得分:5)

要安全地执行此操作,您需要能够确定

  • 代码
  • 中的所有标识符(以及那些不是,例如评论中间的内容)
  • 每个标识符的有效范围
  • 在文本
  • 中替换旧标识符的能力
  • 确定是否重命名标识符导致另一个名称被遮蔽的能力

要准确确定标识符,您至少需要一个语言准确的词法分析器。 PHP中的标识符与COBOL中的标识符不同。

要确定有效范围,您必须在实践中确定程序结构,因为大多数“范围”都是由这种结构定义的。这意味着你需要一个语言准确的解析器; PHP中的范围与COBOL中的范围不同。

要确定哪些名称在哪些范围内有效,您需要了解语言范围规则。您的语言可能会坚持标识符X将根据找到X的上下文引用不同的Xes(考虑使用不同参数命名为X的对象构造函数)。现在,您需要能够根据命名规则遍历范围结构。单继承,多继承,重载,默认类型都需要你为程序构建一个范围模型,将标识符和相应的类型插入每个范围,然后从遇到的标识符中爬出来。根据语言语义通过各种范围编写文本。您将需要符号表,继承链接,AST以及导航所有这些的能力。这些结构与PHP和COBOL不同,但它们有许多共同的想法,所以你可能需要一个具有共同概念支持的库。

重命名标识符,您必须修改文本。在一百万行代码中,您需要仔细指出 。修改AST节点是一种仔细指出的方法。实际上,您需要修改 all 与正在重命名的标识符相对应的标识符;你必须爬过树才能找到它们,或者在AST中记录所有参考文献,以便轻松找到它们。修改树后,必须在修改AST后重新生成源文本。这是很多机器;看到我SO answer on how to prettyprint ASTs预先知道你合理建议应该保留的所有东西。 (你的另一个选择是跟踪字符串文本所在的AST, 并读取/补丁/写入文件。)

在更新文件之前,您需要检查是否有阴影。请考虑以下代码:

 {  local x;
     x=1;
    {local y;
     y=2;
      {local z;
         z=y
         print(x);
      }
    }
 }

我们同意此代码打印“1”。现在我们决定将y重命名为x。 我们已经打破了范围,现在提到的打印声明 概念上,外部x指的是由重命名的y捕获的x。代码现在打印“2”,所以我们的重命名打破了它。这意味着必须检查可能在其中找到重命名变量的范围中的所有其他标识符,以查看新名称是否“捕获”了我们不期望的某个名称。 (如果打印声明打印z,这将是合法的。)

这是很多机器。

是的,有一个框架几乎包含所有这些以及许多强大的语言前端。请参阅我们的DMS Software Reengineering Toolkit。它具有生成AST的解析器,用于从AST生成文本的prettyprinters,通用符号表管理机制(包括支持多重继承),AST访问/修改机制。它有漂亮的印刷机制将AST转回文本。它具有实现名称和类型解析的front ends for C, C++, COBOL and Java(例如,即时符号表范围和符号表条目映射的标识符);它还有很多其他尚未实施范围的语言的前端。

我们刚刚完成了为Java实现“重命名”的练习。 (当然所有上述问题都出现了)。我们即将为C ++开始一个。

答案 1 :(得分:1)

您可以尝试为所涉及的语言创建基于Xtext的实现。 Xtext框架为跨语言重命名重构提供了可靠的基础结构。但是,您必须为每种语言提供至少“足够好”的范围分辨率的语法。

答案 2 :(得分:-1)

语言主要保证令牌将是唯一的,无论上下文如何。一个天真的第一种方法(这将打破很多很多代码)将是:

cp file file.orig
sed -i 's/\b(newTokenName)\b/TEMPTOKEN/g' file
sed -i 's/\b(oldTokenName)\b/newTokenName/g' file

使用GNU sed,这将打破PHP。将\ b重写为一般的令牌匹配,如([^ a-zA-Z~ $ -_] [^ a-zA-Z0-9~ $ -_])可用于大多数C,Java,PHP和Python ,但不是Perl(需要将@和%添加到令牌字符。除此之外,它需要一个适用于您想要添加的任何语言的插件架构。在某些时候,将有两种语言的变量和函数命名规则将是不兼容的,在这一点上,你需要在插件中做更多的事情。