Gibt es Compiler, die versuchen, Syntaxfehler selbst zu beheben? [geschlossen]

15

Vor einiger Zeit hörte ich, dass es einen Compiler gab, der versuchte, Syntaxfehler zu beheben, indem er den Kontext analysierte und daraus folgerte, was beabsichtigt war.

Gibt es einen solchen Compiler wirklich? Offensichtlich hat es wenig praktischen Wert, aber es wäre sehr interessant, damit zu spielen und daraus zu lernen.

Nathan Osman
quelle
3
Fällt IntelliSense in diese Kategorie? Bei vielen Compilern werden ähnliche Fehler wie bei [Semikolon] erwartet.
Robert Harvey
1
@ Robert: Nein, aber das ist ein guter Punkt.
Nathan Osman
1
Ein Freund von mir hat den C-Präprozessor ziemlich gehackt, zum Beispiel 'inlcude -> include', und versucht herauszufinden, wo offene Bedingungen hätten geschlossen werden müssen. Es war seine Masterarbeit, die er schnell für etwas einfacheres aufgab. Trotzdem eine interessante Frage!
Tim Post
3
Der AC # -Compiler schlägt mit SEHR nützlichen Fehlermeldungen fehl. Das zusammen mit einer guten Dokumentation, die online für jeden Fehlercode verfügbar ist, funktioniert ziemlich gut. Es ist eine schlechte Idee, die Syntax automatisch zu korrigieren, obwohl HTML-Interpreter (z. B. Browser) dies sowieso oft tun.
Job
1
Der Compiler, auf den Sie sich beziehen, war der ursprüngliche PL / I. Es ging davon aus, dass alles, was der Programmierer schrieb, etwas bedeutet haben musste, und versuchte zu erraten, was das sein könnte. Nach meiner Erfahrung hat es in der Tat sehr schlecht geraten!
david.pfx

Antworten:

28

In gewissem Sinne ist der Akt der Compilierung wird Folgern , was bestimmte Syntax tun sollte, und somit ein Syntaxfehler ist , wenn der Compiler nicht in der Lage ist , es herauszufinden. Sie können weitere "Vermutungen" hinzufügen, um den Compiler weitere Dinge ableiten zu lassen und flexibler mit der Syntax umzugehen. Dies muss jedoch durch einen bestimmten Satz von Regeln erfolgen. Und diese Regeln werden dann ein Teil der Sprache und sind keine Fehler mehr.

Also, nein, es gibt wirklich keine solchen Compiler, weil die Frage keinen Sinn ergibt. Das Erraten, was Syntaxfehler nach bestimmten Regeln bewirken sollen, wird nur ein Teil der Syntax.

In diesem Sinne gibt es ein gutes Beispiel für einen Compiler, der dies tut: Beliebiger C-Compiler. Sie drucken oft nur eine Warnung aus, die nicht so ist, wie sie sein sollte, und gehen dann davon aus, dass Sie X gemeint haben, und fahren fort. Dies ist in der Tat das "Erraten" von unklarem Code (obwohl es sich meistens nicht um Syntax an sich handelt), etwas, das ebenso gut die Kompilierung mit einem Fehler hätte stoppen und daher als Fehler qualifizieren können.

Lennart Regebro
quelle
4
Das ist die richtige Antwort. Sobald ein Compiler einen Fehler beheben kann, handelt es sich nicht mehr wirklich um einen Fehler. Perl ist (in?) Berühmt für dieses "Do What I Mean" -Verhalten und wählt aus, was der Programmierer mit größter Wahrscheinlichkeit als nicht eindeutige Quelle bezeichnet hat.
Jon Purdy
Perl opfert die Ausführlichkeit für die Quellcodegröße.
Nathan Osman
@ George Edison: Das ist entweder eine Tautologie oder ein Widerspruch.
Jon Purdy
Oder eine tiefe Einsicht. :)
Lennart Regebro
23

Klingt sehr gefährlich. Wenn ein Compiler versucht, auf Ihre Absicht zu schließen, sie falsch einleitet, den Code repariert und Sie dann nicht informiert (oder in einer Warnung darauf hinweist, dass Sie wie jeder andere auch ignorieren), können Sie möglicherweise Code ausführen ernsthaft etwas Schaden anrichten.

Ein solcher Compiler wurde wahrscheinlich absichtlich NICHT erstellt.

Nganju
quelle
5
Ich weiß das. Ein solcher Compiler hätte keine Verwendung für die Kompilierung, aber das Konzept ist sehr interessant und hat Lernpotential.
Nathan Osman
2
Fast alle aktuellen IDE-Versionen bieten Vorschläge für die Syntax und sind sehr hilfreich. und für den Rest des Teils mit Nganju einverstanden
Jigar Joshi
Ich würde einen solchen Compiler nicht verwenden. Es fällt unter die Überschrift "Schwarze Magie".
Michael K
Hmmm, wo würdest du die Typinferenz von Scala auf dieser Skala bewerten? Nachdem ich es ausprobiert habe, würde es sagen, es ist ein wesentlicher Beitrag zum prägnanten Code. Andererseits hat es mich gelegentlich in den Fuß geschossen (z. B. weil ich dachte, ich hätte mit Listen zu tun, aber eigentlich immer noch mit Sets).
am
Wir haben Sachen wie Autoscope in OMP, also ist ein bisschen davon machbar. Natürlich hat der Code, an dem ich arbeite, die automatische Überprüfung deaktiviert, weil wir ihm nicht vertrauen. Ich konnte sehen, dass ein interaktiver Compiler fragte: "Meintest du XXX?". So weit würde ich gehen. Und selbst das ist wahrscheinlich zu gefährlich.
Omega Centauri
12

In der IDE für eine Programmiersprache wird heutzutage normalerweise ein Compiler im Hintergrund ausgeführt, sodass Analysedienste wie Syntax-Coloring, IntelliSense, Fehler usw. bereitgestellt werden können. Offensichtlich muss ein solcher Compiler in der Lage sein, tief gebrochenen Code zu verstehen. Meistens ist der Code beim Bearbeiten nicht korrekt. Aber wir müssen noch einen Sinn daraus machen.

Normalerweise wird die Fehlerbehebungsfunktion jedoch nur während der Bearbeitung verwendet. Es macht wenig Sinn, dies für die eigentliche Kompilierung in "Mainline" -Szenarien zuzulassen.

Interessanterweise haben wir diese Funktion in den JScript.NET-Compiler eingebaut. Grundsätzlich ist es möglich, den Compiler in einen Modus zu versetzen, in dem wir dem Compiler erlauben, fortzufahren, selbst wenn ein Fehler auftritt, wenn die IDE sich davon erholt hätte. Sie können Visual Basic- Code eingeben, den JScript.NET-Compiler darauf ausführen und haben eine vernünftige Chance, dass am anderen Ende ein funktionierendes Programm herauskommt!

Dies ist eine amüsante Demo, die sich jedoch aus vielen Gründen als nicht sehr gut für "Mainline" -Szenarien herausstellt. Eine vollständige Erklärung wäre ziemlich langwierig. Die kurze Erklärung ist, dass es Programme gibt, die unvorhersehbar und versehentlich funktionieren , und dass es schwierig ist, denselben Code über mehrere Compiler oder mehrere Versionen desselben Compilers auszuführen. Die hohen Kosten, die die Funktion verursacht, sind nicht durch die geringen Vorteile gerechtfertigt.

Peter Torr, der das Feature damals als Premierminister hatte, diskutiert es kurz in diesem Blogbeitrag aus dem Jahr 2003 .

Obwohl wir diese Funktion über die Skript-Hosting-APIs der JScript .NET-Engine bereitstellen, sind mir keine echten Kunden bekannt, die sie jemals verwendet haben.

Eric Lippert
quelle
Ich wünschte, mein Arbeitgeber hätte die Ressourcen, um so zu experimentieren. Wir führen nachts nicht einmal Unit-Tests durch, weil es so viele Funktionen und Fehler zu beheben gibt :(
Job
1
Dies ist die Art von Antwort, auf die ich gehofft habe ... wie ich bereits sagte - offensichtlich hat eine solche Funktion wenig praktischen Nutzen, bietet aber eine großartige Möglichkeit, einige Techniken zu erlernen, die auf andere Dinge angewendet werden könnten. (Sprachanalyse usw.)
Nathan Osman
1
@Job: Allgemein gilt: Wenn Sie die Komponententests nicht regelmäßig durchführen, müssen Sie noch viel mehr Fehler beheben .
Eric Lippert
Ich weiß bereits, was ich in Bezug auf meinen Job tun muss, anstatt hier zu jammern. Bei einigen Software-Unternehmen verstehen die Leute an der Spitze den Unterschied zwischen einem Prototyp und einem fertigen Produkt nicht wirklich. Denn pixelweise gibt es oft keinen großen Unterschied. Es ist unklug, nicht mit einem Prototyp zu beginnen, damit keine Zeit verschwendet wird. Aber die schreckliche Antwort "sieht gut aus, wie viele Tage, um diese in Produktion zu bringen?". Das sind die gleichen Leute, die misstrauisch wären, wenn die Ingenieure ihnen sagen würden, dass sie Zeit für die Infrastruktur oder das Refactoring aufwenden müssen. Ich höre sogar, dass Spolsky es nicht mag.
Job
10

Das erste, was mir in den Sinn kommt, ist das automatische Einfügen von Semikolons in Javascript . Eine schreckliche, schreckliche Eigenschaft, die niemals in die Sprache hätte gelangen dürfen.

Das soll nicht heißen, dass es keinen besseren Job hätte machen können. Wenn es in der folgenden Zeile nach vorn schaute, könnte es eine bessere Einschätzung der Absicht des Programmierers geben, aber am Ende des Tages, wenn es mehrere gültige Wege gibt, auf denen die Syntax hätte verschwinden können, gibt es wirklich keinen Ersatz für den Programmierer explizit zu sein.

Dean Harding
quelle
1
Ich stimme der JavaScript-Funktion zum Einfügen von Semikolons von Herzen zu - völlig nutzlos.
Nathan Osman
7

Ich denke, wenn ein Compiler eine falsche Syntax korrigieren könnte, sollte diese Syntax in der Sprache dokumentiert sein.

Der Grund für Syntaxfehler ist, dass ein Parser den abstrakten Syntaxbaum nicht aus dem Programm heraus erstellen konnte. Dies passiert, wenn ein Token nicht am richtigen Ort ist. Um zu erraten, wo sich dieses Token befinden sollte, ob es entfernt werden sollte oder ob ein anderes Token hinzugefügt werden sollte, um den Fehler zu beheben, benötigen Sie eine Art Computer, der die Absicht eines Programmierers erraten kann. Wie könnte eine Maschine das erraten:

int x = 5 6;

Sollte sein:

int x = 5 + 6;

Es könnte genauso gut eine der folgenden sein: 56, 5 - 6, 5 & 6. Ein Compiler kann das nicht wissen.

Diese Technologie existiert noch nicht.

jjnguy
quelle
1
Eine solche Technologie kann nicht existieren. Gedankenlesen ist nicht erlaubt; Alle Anweisungen müssen eindeutig aus dem Code stammen.
Job
Richtig, aber ich meinte wirklich: "Gibt es Compiler, die versuchen, ungültige Syntax zu korrigieren, indem sie Vermutungen basierend auf dem Kontext anstellen?" Die Tatsache, dass der Compiler ungültige Syntax korrigiert, macht die Syntax nicht gültig. Außerdem ist mir klar, dass ein solches Tool für die Codeentwicklung unbrauchbar wäre.
Nathan Osman
6

Obwohl dies nicht ganz dasselbe ist, hat sich HTML sozusagen in die Katastrophe verwandelt. Browser haben schlechtes Markup toleriert und als nächstes wussten Sie, dass Browser A nicht so rendern konnte wie Browser B (ja, es gibt andere Gründe, aber dies war einer der ersten, besonders vor ungefähr 10 Jahren, bevor einige der Lockerungsregeln zur Konvention wurden ).

Wie Eric Lippert schlussfolgert, werden viele dieser Dinge am besten von der IDE erledigt, nicht vom Compiler. So können Sie sehen, was die automatischen Bits für Sie zu vermasseln versuchen.

Die Strategie, von der ich denke, dass sie jetzt vorherrscht, ist die kontinuierliche Verbesserung der Sprache, anstatt den Compiler zu lockern: Wenn es wirklich etwas ist, das der Compiler automatisch herausfinden kann, dann führe ein gut definiertes Sprachkonstrukt darum herum ein.

Als unmittelbares Beispiel kommen Autoeigenschaften in C # in Frage (nicht die einzige Sprache mit ähnlichen Eigenschaften): Da die Mehrheit der Getter / Setter in einer App eigentlich nur Wrapper um ein Feld sind, lassen Sie den Entwickler nur deren Eigenschaften angeben Absicht und lassen Sie den Compiler den Rest injizieren.

Was mich dann zum Nachdenken bringt: Die meisten Sprachen im C-Stil tun dies bereits zu einem gewissen Grad. Für Dinge, die automatisch herausgefunden werden können, verfeinern Sie einfach die Syntax:

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

Kann reduziert werden auf:

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

Letztendlich denke ich, dass es darauf ankommt: Der Trend ist, dass Sie den Compiler nicht "intelligenter" oder "lockerer" machen. Es ist die Sprache , die schlauer oder lockerer gemacht wird.

Außerdem kann zu viel "Hilfe" gefährlich sein, wie der klassische "if" -Fehler:

if (true == x)
    if (true == y)
       dothis();
else
    dothat();
MIA
quelle
Es ist zu beachten, dass XHTML eine Lösung für das Durcheinander bietet, das durch die schlechten HTML-Spezifikationen verursacht wurde.
Nathan Osman
2
if (x && y) dothis(); else dothat();würde etwas besser aussehen.
Job
1
Eine Katze stirbt jedes Mal, wenn sich jemand mit trueoder vergleicht false.
JensG
2

Als ich in den späten 80ern und frühen 90ern FORTRAN und PL / I auf DEC- und IBM-Minicomputer- und Mainframe-Systemen codierte, erinnere ich mich an die regelmäßigen Abmeldungen der Compiler wie "bla bla Fehler". ". Damals war dies ein Erbe der (noch früheren, vor meiner Zeit) Tage der Stapelverarbeitung und Lochkarten, als es wahrscheinlich eine enorme Wartezeit zwischen dem Einreichen Ihres Codes zum Ausführen und dem Zurückerhalten der Ergebnisse gab. Daher war es für Compiler sehr sinnvoll, den Programmierer zu überdenken und den ersten aufgetretenen Fehler nicht abzubrechen, sondern fortzusetzen. Wohlgemerkt, ich erinnere mich nicht, dass die "Korrekturen" besonders raffiniert waren. Als ich schließlich auf interaktive Unix-Workstations (Sun, SGI usw.) wechselte,

timday
quelle
2
Diese Compiler würden fortfahren, aber sie würden NUR fortfahren, um zu versuchen, weitere Fehler zu finden, sodass Sie (möglicherweise) mehrere Probleme beheben können, bevor Sie sie erneut einreichen. Moderne PCs sind so schnell, dass es durchaus machbar ist, dass ein "interaktiver" Compiler beim ersten Syntaxfehler anhält und Sie in einen Editor versetzt. (Und in der Tat, der ursprüngliche Turbo Pascal, in den frühen 1980er Jahren, arbeitete genau so. Es war schön.)
John R. Strohm
1
Ja, ich erinnere mich, dass der IBM PL / I-Optimierungscompiler gelegentlich fehlende BEGIN- und END-Anweisungen liefert, ISTR auch fehlende Semikolons.
TMN
1

Das Ziel eines Compilers ist es, ausführbare Dateien zu erstellen, die sich wie gewünscht verhalten. Wenn ein Programmierer etwas Ungültiges schreibt, selbst wenn der Compiler mit einer Wahrscheinlichkeit von 90% erraten kann, was beabsichtigt war, ist es im Allgemeinen besser, den Programmierer zu bitten, das Programm zu reparieren, um die Absicht klar zu machen, als den Compiler voranzutreiben und eine ausführbare Datei zu erstellen das hätte eine erhebliche Chance, einen Fehler zu verbergen.

Natürlich sollten Sprachen im Allgemeinen so gestaltet sein, dass Code, der Absichten klar ausdrückt, legal ist, und Code, der Absichten nicht klar ausdrückt, sollte verboten sein, aber das bedeutet nicht, dass dies der Fall ist. Betrachten Sie den folgenden Code [Java oder C #]

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

Es f1wäre hilfreich, wenn ein Compiler eine implizite Typumwandlung für die Zuweisung hinzufügt , da der Programmierer nur eine logische Sache f1enthalten möchte (den floatWert, der 1/10 am nächsten kommt). Anstatt Compiler zu ermutigen, unzulässige Programme zu akzeptieren, ist es für die Spezifikation jedoch besser , implizite Double-to-Float-Konvertierungen in bestimmten Kontexten zuzulassen. Auf der anderen Seite ist die Zuweisung zu d1möglicherweise oder möglicherweise nicht das, was der Programmierer wirklich beabsichtigt hat, aber es gibt keine Sprachregel, die dies verbietet.

Die schlimmsten Arten von Sprachregeln sind solche, bei denen Compiler Rückschlüsse auf Fälle ziehen, in denen etwas nicht legitimerweise anders kompiliert werden könnte, ein Programm jedoch "aus Versehen" in einem Fall gültig sein könnte, in dem ein Rückschluss beabsichtigt war. Viele Situationen mit implizitem Ende der Aussage fallen in diese Kategorie. Wenn ein Programmierer, der zwei separate Anweisungen schreiben möchte, einen Anweisungsabschluss weglässt, kann ein Compiler normalerweise auf die Anweisungsgrenze schließen, kann aber gelegentlich als eine Anweisung etwas betrachten, das als zwei verarbeitet werden sollte.

Superkatze
quelle
0

Syntaxfehler sind besonders schwer zu korrigieren. Nehmen )wir den Fall eines fehlenden Rechts : Wir wissen, dass wir den Code durch Einfügen eines reparieren können, aber es gibt normalerweise viele Stellen, an denen wir einen Code einfügen und ein syntaktisch korrektes Programm erhalten könnten.

Ein viel einfacherer Punkt sind falsch geschriebene Bezeichner (beachten Sie jedoch, dass dies keine Syntaxfehler sind). Man kann den Bearbeitungsabstand zwischen dem nicht auflösbaren Bezeichner und allen Bezeichnern im Gültigkeitsbereich berechnen und durch Ersetzen des nicht auflösbaren Wortes durch dasjenige, das der Benutzer höchstwahrscheinlich gemeint hat, würde man in vielen Fällen ein korrektes Programm finden. Es stellt sich jedoch heraus, dass es immer noch besser ist, den Fehler zu kennzeichnen und die IDE gültige Ersetzungen vorschlagen zu lassen.

Ingo
quelle
-1

Ein solcher Compiler wäre einfach eine entspannte, nicht standardmäßige Implementierung der zu kompilierenden Sprache.

Rei Miyasaka
quelle
-2

Es wurde mehrmals ausprobiert, aber oft hat es nicht den gewünschten Effekt erzielt: Denken Sie an HAL 9000 oder GlaDOS.

cbrandolino
quelle
-3

In C können Sie keine Arrays nach Wert übergeben, aber der Compiler ermöglicht Ihnen das Schreiben von:

void foo(int array[10]);

was dann stillschweigend umgeschrieben wird als:

void foo(int* array);

Wie dumm ist das denn? Ich würde hier lieber einen harten Fehler als ein stilles Umschreiben bevorzugen, da diese Sonderregel viele Programmierer zu der Annahme veranlasst hat, dass Arrays und Zeiger im Grunde dasselbe sind. Sie sind nicht.

fredoverflow
quelle