例如,如果我有下一个 perl 脚本:
use strict;
use warnings;
print $x;
Run Code Online (Sandbox Code Playgroud)
当我运行此脚本时,编译将失败并显示错误:
Global symbol "$x" requires explicit package name (did you forget to declare "my $x"?) at ...
Run Code Online (Sandbox Code Playgroud)
是否可以编写一些在发生此错误时调用的 perl 模块并自动修复此错误并继续编译?(即使链接到任何信息都可以)
# This code is incorrect.
# Here I just ask about such ability
# This code is very weak approximation how it might look
package AutoFix;
sub fix {
$main::x = 'You are defined now';
}
1;
Run Code Online (Sandbox Code Playgroud)
所以下一个代码不会失败并打印You are defined now:
use strict;
use warnings;
use AutoFix;
print $x;
Run Code Online (Sandbox Code Playgroud)
您想要做多少工作来创建可以确定修复内容的代码?而且,这些工作量与手动检查代码所需的工作量是否相当或更少?
现在,我写了所有这些,花了相当多的时间试图想出一个系统来分析 CPAN 安装程序的输出,以找出问题所在(CPANPLUS 的主要推动力,现在已成为历史)。说某些事情不对很容易,但除此之外还有很多痛苦。
在您的示例中,您有关于未声明变量的错误。AutoFix 如何知道它应该是包还是词法变量?您可以猜测其中之一,但实际上您有两个大问题:
即使是有经验的人类程序员也很难确定代码的意图(只需阅读 StackOverflow 问题注释)。编译代码通常不是正确的代码,因为它没有达到预期的结果。此外,程序员甚至理解问题吗?程序员写的代码(这里不正确)是否反映了代码应该做的实际工作?代码审查中的人类很难弄清楚这一点。Coverity 之类的工具可以猜测它所知道的问题,但它们无法更正代码。
但是让我们说程序员理解这个问题。他们是否正确表达了这一点?一般来说,根据我的经验,你编程的时间越长,你就越倾向于“不”。
这与您提到的数据库约束完全不同。这是针对预期和允许的情况的狭隘目标修复。考虑一个不同的平行:如果记录有纽约区号但有芝加哥地址,我应该修复城市吗?当我还是个年轻的笨蛋时,我对数据库做了类似的事情。这很愚蠢,因为我认为我知道一些我不知道的事情,每个了解情况的人都立即认出了它。即便如此,这些限制也是我们对我们对世界的了解建模的方式,而不是世界的实际情况。
现在,要制作 AutoFix,您需要制作一些可以查看代码、理解代码并弄清楚它应该做什么的东西。您可以进行猜测,但您没有在那里玩概率的基础。
技术问题解决不了。AutoFix 可以撤消编译指示的工作,这样某些类别的错误就不会出现,但那又怎样?有错误的程序会继续吗?这对任何人有什么帮助?
不仅如此,编译器在意识到无法解析某些内容时往往会抱怨。他们抱怨的往往不是问题。我在调试时教给人们的第一件事是他们需要立即查看错误消息中的行号的语句。您捕获的任何错误消息几乎都有无数个原因。
考虑此代码,它以与您的示例相同的方式失败(相同的错误消息),但出于完全不同但常见的原因:
use strict;
use warnings;
my $x = 5,
print $x++;
Run Code Online (Sandbox Code Playgroud)
你怎么知道修复应该是什么?这不是关于声明$x.
所以,您现在有两个案例,您可以构建您的修复程序。然后你遇到了另一个案例,所以你构建了它。你一直这样做,直到最终你有一个很大的修复字典。也许你会有点疯狂并进行一些机器学习(并且糟糕的代码和解决方案的语料库不会很酷)。
但是,程序仍然无法继续。它必须重新开始,因为它至少必须回到它应该做但没有做的地方。你不能仅仅重启程序,因为你不知道它是否是幂等的。重新运行程序可能会重做不应做的工作,例如将重复项插入数据库。
说了这么多,这种事情跟静态分析和重构浏览器有关。Adam Kennedy 的 Parse Perl Independent (PPI) 项目是在不编译的情况下理解 Perl 代码的第一步,然后朝着了解代码的哪些部分代表相同事物的 Smalltalk 理想迈进。如果您知道命名的两个事物foo是同一事物,则可以重新排列处理foo. 例如,如果您将一个方法从barto重命名set_bar,您可以立即知道哪些bar应该重命名,哪些属于其他类。
Adam 编写了Acme::BadExample并挑战任何人让它运行。他写道:“任何给定的 Perl 源代码都以奇异的伪量子状态存在,因为它同时证明了二元性和不确定性。”
Jos Boumans 站出来使用了一些令人费解的 Perl,然后他在Barely Legal XXX Perl 中展示了这些,我认为他在 2006 年首次提出。他的解决方案非常有创意,而且以一种我不想要的方式生产代码。
Perl 甚至不知道,按照设计,什么类型的东西会在一个变量中,甚至你可能会调用它的方法会存在。事实上,它非常依赖运行时,相信事情会在你需要的时候就位,所以我们经常说“只有 Perl 可以解析 perl”。您确实需要能够运行 Perl 代码才能正确编译它,因为BEGIN块会影响解析。例如,aBEGIN可以定义一个具有一定数量的子程序。你怎么解析foo 5, 6?你必须知道已经定义了什么。
Perl 具有其他“远距离动作”功能,这使得这更加困难。autodie重新定义 CORE 功能以添加额外的行为,但您可能无法在代码中看到这一点。您可以设置默认的正则表达式标志(我已经看到人们/isxm在不检查的情况下应用到整个文件的情况下有很多大问题)。