Welche Klasse von Sprachen erkennen echte moderne Regexe tatsächlich?
Immer wenn es eine Erfassungsgruppe mit unbegrenzter Länge und (.*)_\1
einer Rückreferenz gibt (z. B. ), stimmt ein regulärer Ausdruck jetzt mit einer nicht regulären Sprache überein. Dies allein reicht jedoch nicht aus, um so etwas wie S ::= '(' S ')' | ε
die kontextfreie Sprache passender Elternpaare zu erreichen.
Rekursive Regexes (die für mich neu sind, aber ich bin sicher, dass sie in Perl und PCRE existieren) scheinen zumindest die meisten CFLs zu erkennen.
Hat jemand in diesem Bereich Nachforschungen angestellt oder gelesen? Was sind die Grenzen dieser "modernen" Regexe? Erkennen sie streng mehr oder weniger als CFGs von LL- oder LR-Grammatiken? Oder gibt es beide Sprachen, die von einem regulären Ausdruck erkannt werden können, aber keine CFG und das Gegenteil?
Links zu relevanten Artikeln wären sehr dankbar.
quelle
Antworten:
Musterrekursion
Mit rekursiven Muster, haben Sie eine Form der rekursiven Abstiegs Anpassung .
Das ist in Ordnung für eine Vielzahl von Problemen, aber wenn man tatsächlich rekursive Abstieg tun möge Parsing , müssen Sie Capture - Gruppen hier einfügen und dort, und es ist umständlich die volle Parse - Struktur auf diese Weise zu erholen. Das Regexp :: Grammars- Modul von Damian Conway für Perl wandelt das einfache Muster in ein äquivalentes Muster um, das automatisch alle genannten Namen in eine rekursive Datenstruktur umwandelt, wodurch das Abrufen der analysierten Struktur erheblich vereinfacht wird. Ich habe ein Beispiel, das diese beiden Ansätze am Ende dieses Beitrags vergleicht.
Rekursionsbeschränkungen
Die Frage war, welche Arten von Grammatiken mit rekursiven Mustern übereinstimmen können. Nun, sie sind sicherlich rekursive Abstiegs- Matcher. Das einzige, was mir in den Sinn kommt, ist, dass rekursive Muster die linke Rekursion nicht verarbeiten können . Dies schränkt die Art der Grammatiken ein, auf die Sie sie anwenden können. Manchmal können Sie Ihre Produktionen neu anordnen, um die linke Rekursion zu vermeiden.
Übrigens unterscheiden sich PCRE und Perl geringfügig darin, wie Sie die Rekursion formulieren dürfen. Siehe die Abschnitte zu „RECURSIVE PATTERNS“ und „Recursion Difference from Perl“ in der Manpage pcrepattern . Beispiel: Perl kann damit umgehen,
^(.|(.)(?1)\2)$
wo PCRE es erfordert^((.)(?1)\2|.)$
.Rekursionsdemos
Der Bedarf an rekursiven Mustern tritt überraschend häufig auf. Ein gut besuchtes Beispiel ist, wenn Sie etwas finden müssen, das verschachtelt werden kann, z. B. ausgeglichene Klammern, Anführungszeichen oder sogar HTML / XML-Tags. Hier ist das Match für balenced parens:
Ich finde das schwieriger zu lesen, weil es kompakt ist. Dies kann im
/x
Modus leicht behoben werden, sodass Leerzeichen nicht mehr von Bedeutung sind:Da wir für unsere Rekursion Parens verwenden, wäre ein klareres Beispiel das Abgleichen verschachtelter einfacher Anführungszeichen:
Eine andere rekursiv definierte Sache, die Sie möglicherweise anpassen möchten, wäre ein Palindrom. Dieses einfache Muster funktioniert in Perl:
was Sie auf den meisten Systemen mit so etwas testen können:
Beachten Sie, dass die Implementierung der Rekursion durch PCRE aufwändiger ist
Dies liegt an Einschränkungen bei der Funktionsweise der PCRE-Rekursion.
Richtiges Parsen
Für mich sind die obigen Beispiele meistens Spielzeug-Streichhölzer, eigentlich gar nicht so interessant. Wenn es interessant wird, wenn Sie eine echte Grammatik haben, versuchen Sie zu analysieren. Beispielsweise definiert RFC 5322 eine E-Mail-Adresse ziemlich ausführlich. Hier ist ein "grammatikalisches" Muster, das dazu passt:
Wie Sie sehen, ist das sehr BNF-ähnlich. Das Problem ist, dass es sich nur um ein Match handelt, nicht um eine Erfassung. Und du willst das Ganze wirklich nicht nur mit dem Erfassen von Parens umgeben, denn das sagt dir nicht, welche Produktion zu welchem Teil passt. Mit dem zuvor erwähnten Regexp :: Grammars-Modul können wir.
Wie Sie sehen, erhalten Sie durch die Verwendung einer etwas anderen Notation im Muster jetzt etwas, das den gesamten Analysebaum für Sie in der
%/
Variablen speichert , wobei alles ordentlich beschriftet ist. Das Ergebnis der Transformation ist immer noch ein Muster, wie Sie vom=~
Operator sehen können. Es ist nur ein bisschen magisch.quelle
((DEFINE)…)
Idee ist äußerst leistungsfähig und nützlich und ermöglicht die Trennung der Deklaration (und ihrer Reihenfolge) von der Ausführung, genau wie bei jeder Top-Down-Programmierung. Ich kann mich nicht erinnern, welche anderen Sprachen eine Gruppenrekursion haben. es kann etwas Exotisches wie C♯ oder sein Typ sein.