Ich höre oft Behauptungen, dass C ++ eine kontextsensitive Sprache ist. Nehmen Sie das folgende Beispiel:
a b(c);
Ist dies eine Variablendefinition oder eine Funktionsdeklaration? Das hängt von der Bedeutung des Symbols ab c
. Wenn c
es sich um eine Variable handelt , wird a b(c);
eine Variable mit dem Namen b
type definiert a
. Es wird direkt mit initialisiert c
. Wenn c
es sich jedoch um einen Typ handelt , a b(c);
wird eine Funktion mit dem Namen deklariert, die a b
annimmt c
und a zurückgibt a
.
Wenn Sie die Definition kontextfreier Sprachen nachschlagen, erfahren Sie im Grunde, dass alle Grammatikregeln linke Seiten haben müssen, die aus genau einem nicht-terminalen Symbol bestehen. Kontextsensitive Grammatiken hingegen erlauben beliebige Zeichenfolgen von terminalen und nicht terminalen Symbolen auf der linken Seite.
Beim Durchsuchen von Anhang A von "The C ++ Programming Language" konnte ich keine einzige Grammatikregel finden, die auf der linken Seite etwas anderes als ein einziges nicht-terminales Symbol enthielt. Das würde bedeuten, dass C ++ kontextfrei ist. (Natürlich ist jede kontextfreie Sprache auch in dem Sinne kontextsensitiv, dass die kontextfreien Sprachen eine Teilmenge der kontextsensitiven Sprachen bilden, aber das ist nicht der Punkt.)
Ist C ++ also kontextfrei oder kontextsensitiv?
quelle
Antworten:
Unten ist meine (aktuelle) Lieblingsdemonstration, warum das Parsen von C ++ (wahrscheinlich) Turing-vollständig ist , da es ein Programm zeigt, das genau dann syntaktisch korrekt ist, wenn eine bestimmte Ganzzahl eine Primzahl ist.
Ich behaupte also, dass C ++ weder kontextfrei noch kontextsensitiv ist .
Wenn Sie beliebige Symbolsequenzen auf beiden Seiten einer Produktion zulassen, erstellen Sie eine Typ-0-Grammatik ("uneingeschränkt") in der Chomsky-Hierarchie , die leistungsfähiger ist als eine kontextsensitive Grammatik. Uneingeschränkte Grammatiken sind Turing-vollständig. Eine kontextsensitive Grammatik (Typ 1) erlaubt mehrere Kontextsymbole auf der linken Seite einer Produktion, aber der gleiche Kontext muss auf der rechten Seite der Produktion erscheinen (daher der Name "kontextsensitiv"). [1] Kontextsensitive Grammatiken entsprechen linear begrenzten Turing-Maschinen .
In dem Beispielprogramm könnte die Hauptberechnung von einer linear begrenzten Turing-Maschine durchgeführt werden, so dass die Turing-Äquivalenz nicht ganz bewiesen wird, aber der wichtige Teil ist, dass der Parser die Berechnung durchführen muss, um eine syntaktische Analyse durchzuführen. Es könnte jede Berechnung gewesen sein, die als Vorlageninstanziierung ausgedrückt werden kann, und es gibt allen Grund zu der Annahme, dass die C ++ - Vorlageninstanziierung vollständig ist. Siehe zum Beispiel Todd L. Veldhuizens Artikel von 2003 .
Unabhängig davon kann C ++ von einem Computer analysiert werden, sodass es sicherlich von einer Turing-Maschine analysiert werden kann. Folglich könnte eine uneingeschränkte Grammatik dies erkennen. Das Schreiben einer solchen Grammatik wäre tatsächlich unpraktisch, weshalb der Standard dies nicht versucht. (Siehe unten.)
Das Problem mit der "Mehrdeutigkeit" bestimmter Ausdrücke ist meist ein roter Hering. Ambiguität ist zunächst ein Merkmal einer bestimmten Grammatik, keine Sprache. Selbst wenn nachgewiesen werden kann, dass eine Sprache keine eindeutigen Grammatiken aufweist, ist sie kontextfrei, wenn sie von einer kontextfreien Grammatik erkannt werden kann. Wenn es von einer kontextfreien Grammatik nicht erkannt werden kann, aber von einer kontextsensitiven Grammatik erkannt werden kann, ist es ebenfalls kontextsensitiv. Mehrdeutigkeit ist nicht relevant.
In jedem Fall sind
auto b = foo<IsPrime<234799>>::typen<1>();
die Ausdrücke jedoch , wie in Zeile 21 (dh ) im folgenden Programm, überhaupt nicht mehrdeutig. Sie werden je nach Kontext einfach unterschiedlich analysiert. Im einfachsten Ausdruck des Problems hängt die syntaktische Kategorie bestimmter Bezeichner davon ab, wie sie deklariert wurden (z. B. Typen und Funktionen). Dies bedeutet, dass die formale Sprache die Tatsache erkennen müsste, dass zwei Zeichenfolgen beliebiger Länge enthalten sind Das gleiche Programm ist identisch (Deklaration und Verwendung). Dies kann durch die "Kopier" -Grammatik modelliert werden, bei der es sich um die Grammatik handelt, die zwei aufeinanderfolgende exakte Kopien desselben Wortes erkennt. Mit dem Pump-Lemma ist es leicht zu beweisendass diese Sprache nicht kontextfrei ist. Eine kontextsensitive Grammatik für diese Sprache ist möglich, und eine Grammatik vom Typ 0 ist in der Antwort auf diese Frage enthalten: /math/163830/context-sensitive-grammar-for-the- Kopiersprache .Wenn man versuchen würde, eine kontextsensitive (oder uneingeschränkte) Grammatik zu schreiben, um C ++ zu analysieren, würde dies möglicherweise das Universum mit Skizzen füllen. Das Schreiben einer Turing-Maschine zum Parsen von C ++ wäre ein ebenso unmögliches Unterfangen. Selbst das Schreiben eines C ++ - Programms ist schwierig, und meines Wissens hat sich keines als richtig erwiesen. Aus diesem Grund versucht der Standard nicht, eine vollständige formale Grammatik bereitzustellen, und beschließt, einige der Parsing-Regeln in technischem Englisch zu schreiben.
Was im C ++ - Standard wie eine formale Grammatik aussieht, ist nicht die vollständige formale Definition der Syntax der C ++ - Sprache. Es ist nicht einmal die vollständige formale Definition der Sprache nach der Vorverarbeitung, die möglicherweise einfacher zu formalisieren ist. (Dies wäre jedoch nicht die Sprache: Die im Standard definierte C ++ - Sprache enthält den Präprozessor, und die Funktionsweise des Präprozessors wird algorithmisch beschrieben, da es in jedem grammatikalischen Formalismus äußerst schwierig wäre, sie zu beschreiben. Sie befindet sich in diesem Abschnitt des Standards, in dem die lexikalische Zerlegung beschrieben wird, einschließlich der Regeln, in denen sie mehrmals angewendet werden muss.)
Die verschiedenen Grammatiken (zwei überlappende Grammatiken für die lexikalische Analyse, eine vor der Vorverarbeitung und die andere, falls erforderlich, danach sowie die "syntaktische" Grammatik) sind in Anhang A mit diesem wichtigen Hinweis (Hervorhebung hinzugefügt) zusammengefasst:
Zum Schluss hier das versprochene Programm. Zeile 21 ist genau dann syntaktisch korrekt, wenn das N in
IsPrime<N>
eine Primzahl ist. Andernfallstypen
handelt es sich um eine Ganzzahl und nicht um eine Vorlage.typen<1>()
Daher wird sie als(typen<1)>()
syntaktisch falsch analysiert, da()
es sich nicht um einen syntaktisch gültigen Ausdruck handelt.[1] Technisch gesehen muss jede Produktion in einer kontextsensitiven Grammatik die folgende Form haben:
αAβ → αγβ
wobei
A
ein Nicht-Terminal undα
,β
sind möglicherweise leere Sequenzen von Symbolen der Grammatik, undγ
ist eine nicht-leere Sequenz. (Grammatiksymbole können entweder Terminals oder Nicht-Terminals sein).Dies kann
A → γ
nur im Kontext gelesen werden[α, β]
. In einer kontextfreien (Typ 2) Grammatikα
undβ
muss leer sein.Es stellt sich heraus, dass Sie Grammatiken auch mit der "monotonen" Einschränkung einschränken können, wobei jede Produktion die Form haben muss:
α → β
wo|α| ≥ |β| > 0
(|α|
bedeutet "die Länge vonα
")Es ist möglich zu beweisen, dass der Satz von Sprachen, die von monotonen Grammatiken erkannt werden, genau der gleiche ist wie der Satz von Sprachen, die von kontextsensitiven Grammatiken erkannt werden, und es ist häufig einfacher, Beweise auf monotonen Grammatiken zu basieren. Folglich ist es ziemlich üblich, dass "kontextsensitiv" verwendet wird, als ob es "monoton" bedeutet.
quelle
0
innerhalb der()
für ein einfaches), aber ich denke , es ist so interessant, weil es zeigt , dass Sie die Vorlage Instanziierung müssen , auch wenn erkennen Ein String ist ein syntaktisch korrektes C ++ - Programm. Wenn beide Zweige kompiliert werden, müsste ich härter arbeiten, um das Argument zu bestreiten, dass der Unterschied "semantisch" ist. Obwohl ich oft aufgefordert werde, "syntaktisch" zu definieren, hatZuerst beobachtete man mit Recht gibt es keine kontextsensitiven Regeln in der Grammatik am Ende des C ++ Standard, so dass Grammatik ist kontextfrei.
Diese Grammatik beschreibt die C ++ - Sprache jedoch nicht genau, da sie Nicht-C ++ - Programme wie z
oder
Die C ++ - Sprache, die als "die Menge wohlgeformter C ++ - Programme" definiert ist, ist nicht kontextfrei (es ist möglich zu zeigen, dass nur anspruchsvolle zu deklarierende Variablen dies ermöglichen). Da Sie theoretisch Turing-vollständige Programme in Vorlagen schreiben und ein Programm aufgrund seines Ergebnisses schlecht gestalten können, ist es nicht einmal kontextsensitiv.
Nun verwenden (unwissende) Personen (normalerweise keine Sprachtheoretiker, sondern Parser-Designer) in einigen der folgenden Bedeutungen normalerweise "nicht kontextfrei"
Die Grammatik am Ende des Standards erfüllt diese Kategorien nicht (dh sie ist mehrdeutig, nicht LL (k) ...), daher ist die C ++ - Grammatik für sie "nicht kontextfrei". Und in gewissem Sinne haben sie Recht, es ist verdammt schwer, einen funktionierenden C ++ - Parser zu erstellen.
Beachten Sie, dass die hier verwendeten Eigenschaften nur schwach mit kontextfreien Sprachen verbunden sind - Mehrdeutigkeit hat nichts mit Kontextsensitivität zu tun (tatsächlich helfen kontextsensitive Regeln normalerweise bei der Disambiguierung von Produktionen), die anderen beiden sind lediglich Teilmengen des Kontexts -freie Sprachen. Und das Parsen kontextfreier Sprachen ist kein linearer Prozess (obwohl das Parsen deterministischer Sprachen ist).
quelle
ambiguity doesn't have anything to do with context-sensitivity
Dies war auch meine Intuition, daher bin ich froh, jemanden zu sehen, der (a) zustimmt und (b) erklärt, wo ich nicht konnte. Ich glaube, es disqualifiziert alle Argumente, die aufa b(c);
der ursprünglichen Frage beruhen und diese teilweise befriedigen, deren Prämisse "oft gehörte" Behauptungen der Kontextsensitivität waren, die auf Mehrdeutigkeit zurückzuführen sind ... insbesondere, wenn es für die Grammatik tatsächlich keine Mehrdeutigkeit gibt, selbst in der MVP.Ja. Der folgende Ausdruck hat je nach typaufgelöstem Kontext eine unterschiedliche Reihenfolge der Operationen :
Bearbeiten: Wenn die tatsächliche Reihenfolge der Operationen variiert, ist es unglaublich schwierig, einen "normalen" Compiler zu verwenden, der vor dem Dekorieren einen nicht dekorierten AST analysiert (Weitergabe von Typinformationen). Andere kontextsensitive Dinge, die erwähnt werden, sind im Vergleich dazu "ziemlich einfach" (nicht, dass die Vorlagenbewertung überhaupt einfach ist).
Gefolgt von:
quelle
Um Ihre Frage zu beantworten, müssen Sie zwei verschiedene Fragen unterscheiden.
Die bloße Syntax fast jeder Programmiersprache ist kontextfrei. Typischerweise wird es als erweiterte Backus-Naur-Form oder kontextfreie Grammatik angegeben.
Selbst wenn ein Programm dem durch die Programmiersprache definierten kontextfreien Grammatik entspricht, ist es nicht unbedingt ein gültiges Programm. Es gibt viele nicht kontextfreie Poperties, die ein Programm erfüllen muss, um ein gültiges Programm zu sein. Die einfachste solche Eigenschaft ist beispielsweise der Umfang der Variablen.
Ob C ++ kontextfrei ist oder nicht, hängt von der Frage ab, die Sie stellen.
quelle
VARDECL : TYPENAME IDENTIFIER
, aber Sie können dies nicht haben, da Sie Typnamen auf CF-Ebene nicht von anderen Bezeichnern unterscheiden können. Ein weiteres Beispiel: Auf CF-Ebene können Sie nicht entscheiden, ob Siea*b
als Variablendeklaration (b
vom Typ Zeiger aufa
) oder als Multiplikation analysieren möchten.Vielleicht möchten Sie einen Blick auf The Design & Evolution of C ++ von Bjarne Stroustrup werfen . Darin beschreibt er seine Probleme beim Versuch, eine frühe Version von C ++ mit yacc (oder ähnlichem) zu analysieren, und wünschte, er hätte stattdessen rekursiven Abstieg verwendet.
quelle
Ja, C ++ ist kontextsensitiv, sehr kontextsensitiv. Sie können den Syntaxbaum nicht erstellen, indem Sie die Datei einfach mit einem kontextfreien Parser analysieren, da Sie in einigen Fällen das Symbol aus dem Vorwissen kennen müssen, um entscheiden zu können (dh beim Parsen eine Symboltabelle erstellen).
Erstes Beispiel:
Ist das ein Multiplikationsausdruck?
ODER
Ist dies eine Deklaration der
B
Variablen als Zeiger vom TypA
?Wenn A eine Variable ist, ist es ein Ausdruck, wenn A ein Typ ist, ist es eine Zeigerdeklaration.
Zweites Beispiel:
Ist dies ein Funktionsprototyp, der ein Argument vom
bar
Typ annimmt?ODER
Ist diese Deklarationsvariable
B
vom TypA
und ruft den Konstruktor von A mitbar
Konstante als Initialisierer auf?Sie müssen erneut wissen, ob
bar
es sich um eine Variable oder einen Typ aus der Symboltabelle handelt.Drittes Beispiel:
Dies ist der Fall, wenn das Erstellen einer Symboltabelle während des Parsens nicht hilft, da die Deklaration von x und y nach der Funktionsdefinition erfolgt. Sie müssen also zuerst die Klassendefinition durchsuchen und die Methodendefinitionen in einem zweiten Durchgang betrachten, um festzustellen, dass x * y ein Ausdruck und keine Zeigerdeklaration oder was auch immer ist.
quelle
A B();
ist eine Funktionsdeklaration auch in einer Funktionsdefinition. Suchen Sie nach der ärgerlichstenC ++ wird mit dem GLR-Parser analysiert. Das bedeutet, dass der Parser beim Parsen des Quellcodes möglicherweise auf Mehrdeutigkeiten stößt, jedoch fortfahren und entscheiden sollte, welche Grammatikregel später verwendet werden soll .
schau auch,
Warum kann C ++ nicht mit einem LR (1) -Parser analysiert werden?
Denken Sie daran , dass kontextfreie Grammatik nicht kann beschreiben ALLE die Regeln einer Programmiersprache Syntax. Beispielsweise wird die Attributgrammatik verwendet, um die Gültigkeit eines Ausdruckstyps zu überprüfen.
Sie können die folgende Regel mit kontextfreier Grammatik nicht beschreiben: Die rechte Seite der Zuweisung sollte vom gleichen Typ sein wie die linke Seite.
quelle
Ich habe das Gefühl, dass es eine gewisse Verwirrung zwischen der formalen Definition von "kontextsensitiv" und der informellen Verwendung von "kontextsensitiv" gibt. Ersteres hat eine klar definierte Bedeutung. Letzteres wird verwendet, um zu sagen "Sie benötigen Kontext, um die Eingabe zu analysieren".
Dies wird auch hier gefragt: Kontextsensitivität vs. Mehrdeutigkeit .
Hier ist eine kontextfreie Grammatik:
Es ist mehrdeutig. Um die Eingabe "x" zu analysieren, benötigen Sie einen Kontext (oder leben mit der Mehrdeutigkeit oder geben "Warnung: E8271 - Eingabe ist in Zeile 115 mehrdeutig" aus). Aber es ist sicherlich keine kontextsensitive Grammatik.
quelle
Keine Algol-ähnliche Sprache ist kontextfrei, da sie Regeln enthält, die Ausdrücke und Anweisungen einschränken, in denen Bezeichner je nach Typ angezeigt werden können, und weil die Anzahl der Anweisungen zwischen Deklaration und Verwendung unbegrenzt ist.
Die übliche Lösung besteht darin, einen kontextfreien Parser zu schreiben, der tatsächlich eine Obermenge gültiger Programme akzeptiert, und die kontextsensitiven Teile in Ad-hoc- "semantischen" Code einzufügen, der an Regeln angehängt ist.
C ++ geht dank seines Turing-vollständigen Vorlagensystems weit darüber hinaus. Siehe Stapelüberlauffrage 794015 .
quelle
Wahr :)
J. Stanley Warford. Computersysteme . Seiten 341-346.
quelle
Manchmal ist es schlimmer: Was meinen die Leute, wenn sie sagen, C ++ habe "unentscheidbare Grammatik"?
quelle
Es ist kontextsensitiv, ebenso
a b(c);
wie zwei gültige Analyseerklärungen und Variablen. Wenn Sie "Ifc
is a type" sagen , ist dies genau dort der Kontext, und Sie haben genau beschrieben, wie empfindlich C ++ darauf reagiert. Wenn Sie nicht den Kontext von "Was istc
?" Sie konnten dies nicht eindeutig analysieren.Hier wird der Kontext in der Auswahl der Token ausgedrückt. Der Parser liest einen Bezeichner als Typennamen-Token, wenn er einen Typ benennt. Dies ist die einfachste Lösung und vermeidet einen Großteil der Komplexität der Kontextsensitivität (in diesem Fall).
Bearbeiten: Es gibt natürlich mehr Probleme mit der Kontextsensitivität. Ich habe mich lediglich auf das konzentriert, das Sie gezeigt haben. Vorlagen sind dafür besonders unangenehm.
quelle
a<b<c>>d
richtig? (Ihr Beispiel ist eigentlich ein Klassiker aus C , wo es das einzige Hindernis ist, kontextfrei zu sein.)Die Produktionen im C ++ - Standard sind kontextfrei geschrieben, aber wie wir alle wissen, definieren wir die Sprache nicht wirklich genau. Einige der Probleme, die die meisten Menschen in der aktuellen Sprache als Mehrdeutigkeit ansehen, könnten (glaube ich) mit einer kontextsensitiven Grammatik eindeutig gelöst werden.
Betrachten wir für das offensichtlichste Beispiel die ärgerlichste Analyse :
int f(X);
. WennX
es sich um einen Wert handelt, wird diesf
als Variable definiert , mit der initialisiert wirdX
. WennX
es sich um einen Typ handelt, wird erf
als Funktion definiert, die einen einzelnen Parameter vom Typ verwendetX
.Wenn wir das aus grammatikalischer Sicht betrachten, könnten wir es so sehen:
Um ganz richtig zu sein, müssten wir natürlich einige zusätzliche "Dinge" hinzufügen, um die Möglichkeit intervenierender Deklarationen anderer Typen zu berücksichtigen (dh A und B sollten beide wirklich "Deklarationen einschließlich der Deklaration von X als ..." sein. oder etwas in dieser Reihenfolge).
Dies unterscheidet sich jedoch immer noch von einem typischen CSG (oder zumindest, woran ich mich erinnere). Dies hängt davon ab, dass eine Symboltabelle erstellt wird - der Teil, der speziell erkannt wird
X
als Typ oder Wert , nicht nur eine Art von Anweisung, die davor steht, sondern die richtige Art von Anweisung für das richtige Symbol / die richtige Kennung.Als solches müsste ich etwas tun, um sicherzugehen, aber meine unmittelbare Vermutung ist, dass dies nicht wirklich als CSG qualifiziert ist, zumindest da der Begriff normalerweise verwendet wird.
quelle
Der einfachste Fall einer nicht kontextfreien Grammatik besteht darin, Ausdrücke mit Vorlagen zu analysieren.
Dies kann als beides analysiert werden
Oder
Die beiden ASTs können nur durch Prüfung der Deklaration von 'a' eindeutig gemacht werden - der erstere AST, wenn 'a' eine Vorlage ist, oder der letztere, wenn nicht.
quelle
<
die eine Klammer sein müssen, wenn dies möglich ist (z. B. folgt sie einem Bezeichner, der eine Vorlage benennt). C ++ 11 fügte die Anforderung hinzu, dass>
und das erste Zeichen>>
als enge Klammern interpretiert werden müssen, wenn diese Verwendung plausibel ist. Dies wirkt sich auf die Analyse vona<b>c>
woa
eine Vorlage aber hat keine Auswirkung aufa<b<c>
.a();
(was ist entwederexpr.call
oderexpr.type.conv
)?Es hat sich gezeigt, dass C ++ - Vorlagen Turing Powerful sind. Obwohl dies keine formale Referenz ist, sollten Sie diesbezüglich einen Blick darauf werfen:
http://cpptruths.blogspot.com/2005/11/c-templates-are-turing-complete.html
Ich werde eine Vermutung wagen (so alt wie ein folkorischer und prägnanter CACM-Beweis, der zeigt, dass ALGOL in den 60er Jahren nicht durch eine CFG dargestellt werden konnte) und sagen, dass C ++ daher nur von einer CFG nicht korrekt analysiert werden kann. CFGs in Verbindung mit verschiedenen TP-Mechanismen in einem Baumpass oder bei Reduktionsereignissen - das ist eine andere Geschichte. Im Allgemeinen gibt es aufgrund des Halteproblems ein C ++ - Programm, das nicht als richtig / falsch angezeigt werden kann, aber dennoch richtig / falsch ist.
{PS- Als Autor von Meta-S (von mehreren Personen oben erwähnt) kann ich mit Sicherheit sagen, dass Thothic weder verstorben ist noch die Software kostenlos verfügbar ist. Vielleicht habe ich diese Version meiner Antwort so formuliert, dass ich nicht gelöscht oder auf -3 herabgestimmt werde.}
quelle
C ++ ist nicht kontextfrei. Ich habe es vor einiger Zeit in einem Compiler-Vortrag gelernt. Eine schnelle Suche ergab diesen Link, in dem im Abschnitt "Syntax oder Semantik" erklärt wird, warum C und C ++ nicht kontextfrei sind:
Wikipedia Talk: Kontextfreie Grammatik
Grüße,
Ovanes
quelle
Wenn Sie die Frage wörtlich stellen, sind natürlich fast alle Sprachen mit Bezeichnern kontextsensitiv.
Man muss wissen, ob ein Bezeichner ein Typname (ein Klassenname, ein durch typedef eingeführter Name, ein Typennamen-Vorlagenparameter), ein Vorlagenname oder ein anderer Name ist, um einen Teil der Verwendung des Bezeichners korrekt ausführen zu können. Zum Beispiel:
ist eine Umwandlung, wenn
name
es sich um einen Typnamen handelt, und ein Funktionsaufruf, wennname
es sich um einen Funktionsnamen handelt. Ein anderer Fall ist die sogenannte "ärgerlichste Analyse", bei der es nicht möglich ist, Variablendefinition und Funktionsdeklaration zu unterscheiden (es gibt eine Regel, die besagt, dass es sich um eine Funktionsdeklaration handelt).Diese Schwierigkeit hat die Notwendigkeit von
typename
undtemplate
mit abhängigen Namen eingeführt. Der Rest von C ++ ist meines Wissens nicht kontextsensitiv (dh es ist möglich, eine kontextfreie Grammatik dafür zu schreiben).quelle
Der richtige Link ist das Parsen von Ringen
Meta-S war Eigentum einer nicht mehr existierenden Firma namens Thothic. Ich kann jedem Interessierten eine kostenlose Kopie des Meta-S senden und habe sie in der Rna-Parsing-Forschung verwendet. Bitte beachten Sie, dass die in den Beispielordnern enthaltene "Pseudoknoten-Grammatik" von einem nicht-bioinformatischen, ausgereiften Programmierer geschrieben wurde und im Grunde nicht funktioniert. Meine Grammatiken verfolgen einen anderen Ansatz und funktionieren recht gut.
quelle
Ein großes Problem dabei ist, dass die Begriffe "kontextfrei" und "kontextsensitiv" in der Informatik etwas unintuitiv sind. Für C ++ ähnelt die Kontextsensitivität stark der Mehrdeutigkeit, dies ist jedoch im allgemeinen Fall nicht unbedingt der Fall.
In C / ++ ist eine if-Anweisung nur innerhalb eines Funktionskörpers zulässig. Das scheint es kontextsensitiv zu machen, oder? Nun, nein. Kontextfreie Grammatiken benötigen nicht die Eigenschaft, mit der Sie eine Codezeile herausreißen und feststellen können, ob sie gültig ist. Das ist eigentlich nicht kontextfrei. Es ist wirklich nur ein Label, das vage etwas impliziert, das damit zusammenhängt, wie es sich anhört.
Wenn nun eine Anweisung innerhalb eines Funktionskörpers unterschiedlich analysiert wird, abhängig von etwas, das außerhalb der unmittelbaren grammatikalischen Vorfahren definiert ist (z. B. ob ein Bezeichner einen Typ oder eine Variable beschreibt), wie im
a * b;
Fall, dann ist sie tatsächlich kontextsensitiv. Hier gibt es keine tatsächliche Mehrdeutigkeit; Es wird als Deklaration eines Zeigers analysiert, wenna
es sich um einen Typ handelt, andernfalls um eine Multiplikation.Kontextsensitiv zu sein bedeutet nicht unbedingt "schwer zu analysieren". C ist eigentlich nicht so schwer, weil die berüchtigte
a * b;
"Mehrdeutigkeit" mit einer Symboltabelle gelöst werden kann, dietypedef
s enthält, die zuvor angetroffen wurden. Es sind keine willkürlichen Vorlageninstanziierungen erforderlich (die sich als Turing Complete erwiesen haben), um diesen Fall zu lösen, wie dies C ++ gelegentlich tut. Es ist tatsächlich nicht möglich, ein C-Programm zu schreiben, das nicht in endlicher Zeit kompiliert werden kann, obwohl es dieselbe Kontextsensitivität wie C ++ aufweist.Python (und andere Whitespace-sensitive Sprachen) ist ebenfalls kontextabhängig, da der Status im Lexer erforderlich ist, um Einrückungs- und Dedent-Token zu generieren. Dies macht das Parsen jedoch nicht schwieriger als eine typische LL-1-Grammatik. Es wird tatsächlich ein Parser-Generator verwendet, was ein Teil dessen ist, warum Python solche nicht informativen Syntaxfehlermeldungen hat. Es ist auch wichtig anzumerken, dass es keine "Mehrdeutigkeit" wie das
a * b;
Problem in Python gibt, was ein gutes konkretes Beispiel für eine kontextsensitive Sprache ohne "mehrdeutige" Grammatik darstellt (wie im ersten Absatz erwähnt).quelle
Diese Antwort besagt, dass C ++ nicht kontextfrei ist. Es gibt eine Implikation (nicht vom Antwortenden), dass es nicht analysiert werden kann, und die Antwort bietet ein schwieriges Codebeispiel, das ein ungültiges C ++ - Programm erzeugt, wenn eine bestimmte Konstante keine ist Primzahl.
Wie andere beobachtet haben, unterscheidet sich die Frage, ob die Sprache kontextsensitiv / frei ist, von derselben Frage zu einer bestimmten Grammatik.
Um die Frage nach der Parsierbarkeit zum Stillstand zu bringen, biete ich empirische Beweise dafür, dass es kontextfreie Grammatiken für C ++ gibt, mit denen ein AST für eine kontextfreie Analyse des Quelltextes erstellt werden kann, indem er tatsächlich mit einer vorhandenen GLR analysiert wird -parser-basiertes Tool, das von einer expliziten Grammatik gesteuert wird.
Ja, es gelingt, "zu viel zu akzeptieren"; Nicht alles, was es akzeptiert, ist ein gültiges C ++ - Programm, weshalb zusätzliche Prüfungen (Typprüfungen) durchgeführt werden. Und ja, die Typprüfung kann auf Berechenbarkeitsprobleme stoßen. In der Praxis haben Tools dieses Problem nicht. Wenn Leute solche Programme schreiben würden, würde keiner von ihnen kompilieren. (Ich denke, der Standard begrenzt tatsächlich den Rechenaufwand, den Sie für das Entfalten einer Vorlage ausführen können. Die Berechnung ist also tatsächlich endlich, aber wahrscheinlich ziemlich umfangreich.)
Wenn Sie damit meinen, festzustellen, ob das Quellprogramm Mitglied der Gruppe der gültigen C ++ - Quellprogramme ist , stimme ich zu, dass das Problem viel schwieriger ist. Aber es ist nicht das Parsen , das das Problem ist.
Das Tool behebt dieses Problem, indem es die Analyse von der Typprüfung des analysierten Programms isoliert. (Wenn in Abwesenheit von Kontext mehrere Interpretationen vorhanden sind, wird ein Mehrdeutigkeitsknoten im Analysebaum mit mehreren möglichen Analysen aufgezeichnet. Die Typprüfung entscheidet, welche korrekt ist, und beseitigt die ungültigen Teilbäume.) Im folgenden Beispiel sehen Sie einen (teilweisen) Analysebaum. Der gesamte Baum ist zu groß, um in eine SO-Antwort zu passen. Beachten Sie, dass Sie einen Analysebaum erhalten, unabhängig davon, ob der Wert 234797 oder 234799 verwendet wird.
Das Ausführen des Namens- / Typ-Resolvers des Tools über den AST mit dem ursprünglichen Wert 234799 ist erfolgreich. Mit dem Wert 234797 schlägt der Namensauflöser (wie erwartet) mit der Fehlermeldung "typen is not type" fehl. und daher ist diese Version kein gültiges C ++ - Programm.
quelle