是否有任何尝试自行修复语法错误的编译器?[关闭]


15

不久前,我听说曾经有一个编译器试图通过分析上下文并推断出意图来修复语法错误。

这样的编译器真的存在吗?显然,它没有什么实用价值,但是玩和学习会很有意思。


3
IntelliSense属于这一类吗?许多编译器的错误类似于预期的[分号]。
罗伯特·哈维

1
@罗伯特:不,但这是一个好点。
内森·奥斯曼

1
我的一个朋友在C预处理器上做了很多黑客工作,例如'inlcude-> include',还有一些工作试图弄清楚应该在哪里关闭开放条件。那是他的硕士论文,他很快就放弃了,以求更轻松一些。仍然是一个很有趣的问题!
蒂姆·波斯特

3
AC#编译器失败,并显示非常有用的错误消息。结合在线提供的有关每个错误代码的优质文档,效果很好。尽管HTML解释器(例如,浏览器)经常这样做,但是自动更正语法是个坏主意。
作业

1
您所指的编译器是原始的PL / I。它假定程序员编写的任何内容都必须具有某种含义,并试图猜测可能是什么。根据我的经验,它确实非常糟糕!
david.pfx 2014年

Answers:


28

从某种意义上说,编译的行为是在推断某种语法的含义,因此,语法错误就是编译器无法弄清楚的时候。您可以添加更多的“猜测”功能,以使编译器推断出更多的内容,并使语法更加灵活,但是它必须通过一组特定的规则进行推断。这些规则成为语言的一部分,不再是错误。

所以,不,实际上没有这样的编译器,因为这个问题没有道理。猜测根据某些规则要执行的语法错误只是语法的一部分。

从这个意义上讲,有一个很好的编译器示例可以执行此操作:任何C编译器。他们通常只会打印出一些不应该出现的警告,然后假设您的意思是X,然后继续。实际上,这是在“猜测”不清楚的代码(尽管本质上基本上不是语法),同样可能由于错误而停止了编译,因此可以视为错误。


4
这是正确的答案。一旦编译器可以从错误中恢复,它就不再是真正的错误。Perl在这种“按我的意思做”的行为中很出名,选择程序员在给定的歧义源中最有可能意味着什么。
乔恩·普迪

Perl牺牲了冗长的源代码大小。
内森·奥斯曼

@乔治·爱迪生(George Edison):这要么是重言式,要么是矛盾。
乔恩·普迪

还是深刻的见解。:)
Lennart Regebro 2010年

23

听起来真的很危险。如果编译器试图推断您的意图,推断错误,修复代码,然后不告诉您(或在警告您与所有人一样,忽略您),那么您将在运行可能认真做些破坏。

像这样的编译器可能是故意没有创建的。


5
我知道。这样的编译器对于编译没有用,但是这个概念非常有趣并且具有学习潜力。
内森·奥斯曼

2
几乎所有最新的IDE都提供了语法建议,这确实很有帮助。其余部分与nganju一致
Jigar Joshi

我不会使用这样的编译器。它属于“黑魔法”的标题。
迈克尔·K

嗯,在这种规模上,您如何评价Scala的类型推断?经过尝试,它会说这是对简洁代码的重大贡献。另一方面,它偶尔会打我的脚(例如,因为我以为我正在处理列表,但实际上仍在处理集合)。
2010年

我们在OMP中有诸如自动范围检查之类的东西,因此它是可行的。当然,由于我们不信任它,因此我正在处理的代码已自动关闭。我可以看到有一个交互式编译器,询问“您是不是要XXX?”。那就是我愿意去的地方。甚至那可能也太危险了。
Omega Centauri 2010年

12

目前,用于编程语言的IDE通常以某种方式在后台运行编译器,因此它可以提供分析服务,例如语法着色,IntelliSense,错误等。显然,这样的编译器需要能够理解深陷的代码。在大多数情况下,编辑时代码不正确。但是我们仍然必须理解它。

但是,通常,错误恢复功能仅在编辑时使用。允许在“主线”场景下进行实际编译没有多大意义。

有趣的是,我们确实将该功能内置到JScript.NET编译器中。基本上,有可能将编译器置于一种模式,在这种模式下,即使IDE可以从编译器中恢复,即使遇到错误,我们也可以继续进行编译。您可以输入Visual Basic代码,在其上运行JScr​​ipt.NET编译器,并有可能在另一端出现正常运行的程序!

这是一个有趣的演示,但是由于很多原因,它对于“主线”场景来说并不是一个很好的功能。完整的解释将很冗长;简短的解释是,它使程序无法正常运行偶然发生,并且使通过多个编译器或同一编译器的多个版本运行相同的代码变得困难。该功能增加的大笔费用并不能因小额利益而得到证明。

彼得·托尔(Peter Torr)是当天PM的功能发布者,他在2003年的此博客文章中对此进行了简短的讨论。

尽管我们确实通过JScript .NET引擎的脚本托管API公开了此功能,但我不知道有任何真正的客户曾经使用过此功能。


我希望我的雇主有这样的资源进行试验;我们甚至都不在晚上运行单元测试,因为要添加的功能太多,而且需要修复的错误:(
Job

1
正如我之前所提到的,这就是我希望得到的那种答案-显然,这种功能几乎没有实际用途,但是它将提供一种很好的方式来学习一些可应用于其他事物的技术。(语言分析等)
内森·奥斯曼

1
@Job:一般的看法是,如果您不定期运行单元测试,则会有更多的bug要修复
埃里克·利珀特

我已经知道我需要做什么而不是在这里抱怨。在某些软件公司中,高层管理人员并没有真正理解原型与最终产品之间的区别。毕竟,在像素方面通常没有太大的区别。不从原型开始是不明智的,这样就不会浪费时间。但是可怕的回应“看起来不错,将其投入生产需要多少天?”。如果工程师告诉他们,他们需要花时间在基础架构上或进行重构,那么这些人就是可疑的人。我听说什至Spolsky不喜欢它。
工作

10

我想到的第一件事是Javascript的自动分号插入。一种可怕的,可怕的功能,它本不应该融入语言中。

这并不是说它做得更好。如果在下面的行放眼望去,那么它可能是能够做出更好的猜测程序员的意图,但在这一天结束时,如果有多个有效的方式语法可能已经走了,那么就真的没有替代品对于程序员来说是明确的。


1
我衷心同意JavaScript分号插入功能-完全没用。
内森·奥斯曼

7

在我看来,如果编译器可以修复错误的语法,则应以该语言记录该语法。

语法错误的原因是因为解析器无法在程序之外创建抽象语法树。当令牌不正确时会发生这种情况。为了猜测该令牌的位置,是否应该删除该令牌,或者是否应该添加其他令牌以修复错误,您需要某种可以猜测程序员意图的计算机。机器如何猜测:

int x = 5 6;

应该是:

int x = 5 + 6;

它可以很容易被任何如下:565 - 65 & 6。编译器无法知道。

该技术尚不存在。


1
这样的技术不存在。不允许读心术;所有指令必须明确来自代码。
工作

是的,但是我真正的意思是“有没有任何编译器试图通过根据上下文进行猜测来纠正无效语法”。编译器纠正无效语法的事实不会使语法有效。而且,我意识到这样的工具对于代码开发将是无用的。
内森·奥斯曼

6

尽管不是完全一样,但这是HTML变成灾难的原因。浏览器容忍了不良的标记,接下来您知道的是,浏览器A不能像浏览器B那样渲染(是的,还有其他原因,但这是最常见的几种之一,尤其是大约在10年前,一些松散规则成为惯例之前)。

正如埃里克·利珀特(Eric Lippert)推断的那样,其中许多事情最好由IDE处理,而不是由编译器处理。那让您看看自动钻头正在为您搞砸什么。

我认为现在占主导地位的策略是不断完善语言,而不是放松编译器:如果确实是编译器可以自动找出的东西,则围绕它引入定义良好的语言构造。

我想到的直接示例是C#中的自动属性(不是唯一具有类似特征的语言):鉴于任何应用程序中的大多数getter / setter实际上只是围绕字段的包装,因此允许开发人员指出其字段目的,让编译器注入其余的内容。

然后,我想到:大多数C样式语言已经在某种程度上做到了这一点。对于可以自动找出的内容,只需改进语法:

 if (true == x)
 {
    dothis();
 }
 else
 {
    dothat();
 }

可以简化为:

if (true == x)
    dothis();
else
    dothat();

最后,我认为可以归结为:趋势是您不会使编译器“更智能”或“更松散”。这是使语言变得更聪明或更宽松的语言

此外,过多的“帮助”可能很危险,例如经典的“ if”错误:

if (true == x)
    if (true == y)
       dothis();
else
    dothat();

应当指出,XHTML为HTML不良规范所造成的混乱提供了解决方案。
内森·奥斯曼

2
if (x && y) dothis(); else dothat();看起来会稍微好一点。
工作

1
猫死了每一个有人对比较时间truefalse
JensG 2014年

2

当我在80年代末和90年代初在DEC和IBM小型计算机及大型机系统上对FORTRAN和PL / I进行编码时,我似乎记得编译器会定期注销诸如“等等等等的消息;假设等等等等的消息。” ”。那时,这是批处理和打孔卡(甚至比我更早的时候)时代的遗留物,在提交代码运行和返回结果之间可能要等待很长时间。因此,对于编译器进行第二次猜测,然后继续尝试而不是中止遇到的第一个错误非常有意义。提醒您,我不记得这些“修正”特别复杂。当我最终移到交互式Unix工作站(Sun,SGI等)时,


2
这些编译器将继续运行,但是仅出于试图查找更多错误的目的而将继续运行,因此您可以(潜在地)在重新提交之前修复一些问题。现代PC足够快,以至于“交互式”编译器完全可以在出现第一个语法错误时停止并进入编辑器。(而且,实际上,最初的Turbo Pascal在1980年代初就以这种方式工作。很好。)
John R. Strohm 2010年

1
是的,我记得IBM PL / I优化编译器偶尔会提供缺少的BEGIN和END语句,而ISTR也会提供缺少的分号。
TMN

1

编译器的目标是产生表现所需的可执行文件。如果程序员写了一些无效的东西,即使编译器可以90%的概率猜到了意图,通常最好还是要求程序员修复程序以使意图更清楚,而不是让编译器继续执行并生成可执行文件。这将有很大的机会掩盖错误。

当然,通常应该对语言进行设计,以使能够清楚表达意图的代码是合法的,而不能清晰表达意图的代码则应被禁止,但这并不意味着它们是合法的。考虑以下代码[Java或C#]

const double oneTenth = 0.1;
const float  oneTenthF = 0.1f;
...
float f1 = oneTenth;
double d1 = oneTenthF;

让编译器为该分配添加隐式类型转换f1将是有帮助的,因为程序员可能只想f1包含一个逻辑的东西(float最接近1/10 的值)。但是,与其鼓励编译器接受不正确的程序,不如在某些情况下允许规范隐式从双精度浮点数转换为规范。另一方面,分配给d1程序员的意图可能是也可能不是,或不是程序员真正想要的,但是没有语言规则禁止它。

最糟糕的语言规则是那些在某些情况下如果不能合法编译的情况下编译器将进行推理的语言规则,但是在意图进行推理的情况下程序可能“偶然地”有效的语言规则。许多涉及隐式声明结尾的情况都属于此类。如果打算编写两个单独的语句的程序员省略了一个语句终止符,则编译器通常可以设法推断出语句边界,但有时可能会将一个应视为两个的东西视为一个语句。


0

语法错误尤其难以纠正。以缺少权利)为例:我们知道可以通过插入一个代码来修复代码,但是通常在很多地方我们可以插入一个代码并获得语法正确的程序。

容易弄错的是标识符拼写错误(但请注意,这不是语法错误)。可以计算出无法解析的标识符与范围内所有标识符之间的编辑距离,并且通过将不可解析的单词替换为用户最有可能表示的那个单词,在许多情况下都会提出正确的程序。但是,事实证明,标记错误仍然更好,并让IDE建议有效的替换方法。


-1

这样的编译器只是它所编译语言的轻松,非标准实现。


-2

它已经尝试了多次,但通常无法达到预期的效果:想想HAL 9000或GlaDOS。


-3

在C语言中,不能按值传递数组,但是编译器允许您编写:

void foo(int array[10]);

然后将其默写为:

void foo(int* array);

这有多愚蠢?我宁愿在这里使用硬错误,也不愿进行无声重写,因为这种特殊规则使许多程序员相信数组和指针基本上是同一回事。他们不是。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.