Pegjs: Fügen Sie die Möglichkeit hinzu, die Knotenposition zu verfolgen

Erstellt am 13. Aug. 2011  ·  15Kommentare  ·  Quelle: pegjs/pegjs

Von PEG.js generierte Parser verfolgen derzeit keine Position (Zeile und Spalte). Ich würde diese Funktion gerne hinzufügen, da sie sehr nützlich wäre.

Eine Möglichkeit besteht darin, jedem Objekt, das als Übereinstimmungsergebnis zurückgegeben wird, die Eigenschaften line und column hinzuzufügen:

start = "a" b:"b" { return [b.line, b.column]; } // Returns [1, 2] on the input "ab".

Eine andere Möglichkeit besteht darin, spezielle line - und column -Variablen innerhalb von Aktionen/Prädikaten verfügbar zu machen, die sich auf die Position des Beginns der aktuellen Regel beziehen:

start = "a" "b" { return [line, column]; } // Returns [1, 1] on the input "ab".

Der erste Weg ist flexibler, aber diese Flexibilität wird möglicherweise nicht wirklich benötigt. Ich bin mir noch nicht sicher, welchen Weg ich implementieren werde.

Beide Wege würden die Leistung beeinträchtigen. Um dies in Fällen zu verhindern, in denen keine Positionsverfolgung erforderlich ist, sollte die Verfolgung nur aktiviert werden, wenn beim Generieren des Parsers die Option trackPosition mit einem wahren Wert an PEG.buildParser übergeben wird.

feature

Hilfreichster Kommentar

@tomitrescak Diese Funktion hat sich in der Vergangenheit anscheinend einige Male geändert. Werfen Sie einen Blick auf das Änderungsprotokoll für weitere Informationen. tl;dr ist, dass Sie die Funktion location() in Ihrer Grammatik verwenden müssen

Alle 15 Kommentare

Beide Optionen werden großartig sein. Für meine Bedürfnisse ist einfach Zeile, Spalte in Ordnung.

Auch hierfür suche ich nach einer Lösung. Derzeit habe ich schnell etwas auf der Grundlage von computeErrorPosition() gehackt, aber es hat mehrere Knicke.

Oberflächlich betrachtet erscheint der erste Ansatz intuitiver als der zweite.

Mir gefällt der erste Ansatz auch besser.

Eine mögliche Effizienz besteht darin, dass name.position eine Funktion ist, damit sie träge berechnet werden kann. Dies könnte dann ein Array mit zwei Elementen aus Zeile und Spalte zurückgeben. Nur eine Idee für diejenigen, die nicht die Position für jeden einzelnen Artikel berechnen möchten.

Im Nachhinein scheint es besser, die Start- und Endzeichenpositionen anzugeben und dann eine Hilfsfunktion in der Bibliothek bereitzustellen, die die Position in Zeile/Spalte umwandelt. Denn Zeile/Spalte braucht man im allgemeinen nur dann, wenn ein Fehler vorliegt, und wenn man einmal auf einen Fehler gestoßen ist, braucht man ihn typischerweise nur einmal.

Ich mache das derzeit auf eine hackige Art, indem ich die Variablen startPos0 und pos missbrauche, was sich hackig anfühlt, aber vorerst funktioniert.

Bis es eine offizielle Lösung gibt, füge ich diese Funktion (eine grobe Kopie von computeErrorPosition ) ganz oben in meine Grammatiken ein. Zumindest gibt es mir die _aktuelle_ Position (allerdings nicht die Position der einzelnen Knoten):

  function computeCurrentPos() {
    /*
     * The first idea was to use |String.split| to break the input up to the
     * error position along newlines and derive the line and column from
     * there. However IE's |split| implementation is so broken that it was
     * enough to prevent it.
     */

    var line = 1;
    var column = 1;
    var seenCR = false;

    for (var i = 0; i < pos; i++) {
      var ch = input.charAt(i);
      if (ch === '\n') {
        if (!seenCR) { line++; }
        column = 1;
        seenCR = false;
      } else if (ch === '\r' | ch === '\u2028' || ch === '\u2029') {
        line++;
        column = 1;
        seenCR = true;
      } else {
        column++;
        seenCR = false;
      }
    }

    return { line: line, column: column, pos: pos };
  }

Dieses Problem wurde durch eine Reihe von Commits behoben.

Sie können jetzt die Option trackLineAndColumn an die Funktion PEG.buildParser übergeben:

var parser = PEG.buildParser(myGrammar, { trackLineAndColumn: true});

Wenn Sie trackLineAndColumn auf true setzen, werden zwei neue Variablen in den Aktionen und Prädikaten sichtbar – line und column . Bei Aktionen bezeichnen diese Variablen die Startposition des Aktionsausdrucks, während sie in Prädikaten die aktuelle Position angeben. Das etwas andere Verhalten ist durch die erwartete Verwendung motiviert.

Die Zeilen- und Spaltenverfolgung ist optional, da sie die Leistung beeinträchtigt (Parser werden dadurch etwa 3-4 × langsamer). Möglicherweise kann ich dies in Zukunft etwas optimieren (ich habe zuerst versucht, es zum Laufen zu bringen).

In der Problembeschreibung habe ich einen anderen Ansatz für dieses Problem erwähnt: Hinzufügen der Eigenschaften line und column zu jedem Übereinstimmungsergebnis. Obwohl diese Lösung sauberer war und von Benutzern bevorzugt wurde, wurde mir klar, dass man keine Eigenschaften für primitive Werte wie Zeichenfolgen, Zahlen oder boolesche Werte festlegen kann (die häufig als Übereinstimmungsergebnisse zurückgegeben werden). Das Zurückgeben von Instanzen von Wrapping-Objekten (z. B. String , Number oder Boolean ) wäre eine mögliche Problemumgehung, würde aber wahrscheinlich zu subtilen Fehlern in den von Benutzern erstellten Parsern führen, da diese Wrapper verhalten sich etwas anders als die Primitiven. Daher habe ich mich entschlossen, den Alternativvorschlag umzusetzen.

Wird die Online-Testseite aktualisiert, um diese Funktion zu unterstützen?

@paulftw Der Online-Editor verwendet immer die neueste stabile Version von PEG.js (derzeit 0.6.2). Ich werde es aktualisieren, sobald ich PEG.js 0.7.0 veröffentliche (das diese Funktion enthalten wird). Sofern nichts Unerwartetes passiert, wird dies in der zweiten Aprilhälfte geschehen.

Beachten Sie, dass der Quellcode der Website in GitHub verfügbar ist. Wenn Sie also ungeduldig sind, können Sie ihn selbst ausführen und ändern.

Ich habe gerade festgestellt, dass alle Zeilen und Spalten 1-basiert sind, während JS-Arrays sowie so ziemlich alle modernen Sprachen und Bibliotheken eine 0-basierte Indizierung verwenden.

Gibt es eine Chance, diese Designentscheidung rückgängig zu machen?

@paulftw Können Sie ein Beispiel für einen Parser-Generator geben, der Zeilen und Spalten als 0-basiert meldet?

Die Idee hinter meiner Entscheidung ist, dass diese Zahlen den Benutzern höchstwahrscheinlich angezeigt werden (in Fehlermeldungen, als Knotenpositionen usw.), sodass eine 1-basierte Indizierung sinnvoller ist als eine 0-basierte. Für die maschinelle Verarbeitung wird wahrscheinlich am häufigsten die Variable offset verwendet, die 0-basiert ist.

Gibt es eine Chance, diese Designentscheidung rückgängig zu machen?

Ja, wenn ich überzeugt bin :-)

Ich verwende peg.js, um Text im Ace-Editor hervorzuheben. Ganz natürlich sind Ass-Liniennummern 0-basiert.
Es scheint jedoch, dass die Standorte in Bison 1-basiert sind.
http://git.savannah.gnu.org/cgit/bison.git/tree/src/location.c#n73

Wenn dies tatsächlich der Fall ist, dann sollte ich aufhören zu streiten.

Nicht sicher, welche Art von Beispiel Sie sehen möchten.
Im Moment verwende ich folgenden Code, um einen Symbolnamen zu finden:

Fänger
= "" { neue Position zurückgeben (Zeile - 1, Spalte - 1); }

Symbol
= start:captor name:IDENT end:captor S* { return new Symbol(name, new Range(start, end)); }

@paulftw Ich habe dieses Problem in eine separate Ausgabe aufgeteilt . Ich würde warten, bis Benutzer 0.7.0 übernehmen und sehen, was die vorherrschende Verwendung ist, um zu entscheiden.

Ich weiß, das ist lange geschlossen, aber wie kann ich das zum Laufen bringen? Bin da ziemlich noob. Ich habe meine Grammatik mit dem Flag "trackLineAndColumn': true" kompiliert, aber Knoten enthalten immer noch keine Zeilen und Spalten. Was wird noch benötigt?

Sie erwähnen Folgendes, ich weiß nur nicht, wie ich es verwenden soll. Ist es bitte irgendwo dokumentiert?

Wenn Sie trackLineAndColumn auf true setzen, werden zwei neue Variablen in den Aktionen und Prädikaten sichtbar – Zeile und Spalte. Bei Aktionen bezeichnen diese Variablen die Startposition des Aktionsausdrucks, während sie in Prädikaten die aktuelle Position angeben. Das etwas andere Verhalten ist durch die erwartete Verwendung motiviert.

@tomitrescak Diese Funktion hat sich in der Vergangenheit anscheinend einige Male geändert. Werfen Sie einen Blick auf das Änderungsprotokoll für weitere Informationen. tl;dr ist, dass Sie die Funktion location() in Ihrer Grammatik verwenden müssen

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen