Ich sehe drei Probleme mit dem aktuellen Fehlermeldesystem in PEG.js:
null
zurückgebenReporting using `null` is inflexible (it doesn't allow to carry any information about the kind of error) and makes it impossible to return `null` as a regular value from actions, which would be useful sometimes (for example [in a JSON parser](https://github.com/dmajda/pegjs/blob/791034fad92c9cd7a9d1c71187df03441bbfd521/examples/json.pegjs#L45)).
Siehe auch Nr. 17.
For example, imagine a HTTP 1.1 parser that wants to report an error about missing `Host` header in a HTTP request with a message like "A HTTP 1.1 request must contain a Host header". Currently the only way to do this is to throw an exception with desired message manually. This is cumbersome and it does not interact well with the backtracking behavior (throwing an exception halts the parsing completely, even when it is possible to backtrack and try another alternatives).
expected
von SyntaxError
ist mechanisch schwer zu verarbeitenThe `expected` property of `SyntaxError` is either an array of expectations (each describing one alternative which the parser expected at the position of error) or `null` (meaning that the end of input was expected).
Jede Erwartung wird durch eine Zeichenfolge dargestellt. Diese Zeichenfolge kann ein erwartetes Literal (dargestellt durch die PEG.js-Syntax – eine Zeichenfolge in Anführungszeichen), eine Zeichenklasse (dargestellt durch die PEG.js-Syntax – [...]
) oder ein beliebiges Zeichen (dargestellt durch die Zeichenfolge "any"
) bedeuten
Ich plane, das Fehlermeldesystem neu zu gestalten, um diese Probleme zu beheben. Bevor ich die Änderungen beschreibe, über die ich nachdenke, ist eine kleine Beschreibung erforderlich, wie das aktuelle System funktioniert.
Wenn ein PEG.js-Parser versucht, ein Zeichenfolgenliteral, eine Zeichenklasse oder .
abzugleichen, und fehlschlägt, erzeugt er einen _Match-Fehler_. Es besteht aus einer _Position_ und einer _Erwartung_ (Beschreibung dessen, was der Parser zu finden versucht hat).
Nach dem Erzeugen eines Übereinstimmungsfehlers geht der Parser zurück und versucht möglicherweise andere Alternativen. Wenn keiner von ihnen erfolgreich ist und es nichts mehr zu versuchen gibt, löst der Parser die Ausnahme SyntaxError
aus. Beim Festlegen seiner Eigenschaften (wie Position, Erwartungen, Fehlermeldung usw.) berücksichtigt der Parser nur den am weitesten entfernten Übereinstimmungsfehler (denjenigen mit der größten Position). Wenn es mehr solcher Fehler gibt, werden ihre Erwartungen kombiniert.
Die Situation ist etwas komplizierter, wenn benannte Regeln verwendet werden, aber dies ist für diese Diskussion irrelevant.
Ich denke über folgende Änderungen im Fehlermeldesystem nach:
SyntaxError.expected
werden nicht durch Strings repräsentiert, sondern durch Objekte. Die Objekte haben eine Eigenschaft type
(mit Werten wie "literal"
, "class"
, "any"
, "eof"
), die den Erwartungstyp bestimmt. Für einige Erwartungstypen enthalten andere Eigenschaften die Details (z. B. für die "literal"
-Erwartung wäre eine value
-Eigenschaft, die die erwartete Zeichenfolge enthält). Dies vereinfacht die mechanische Verarbeitung der Erwartungen.Eine Alternative zur Eigenschaft type
ist die Verwendung von Klassen. Aber ich denke, die Eigenschaft type
wird für Benutzer einfacher zu handhaben sein.
null
zurückgeben. Es handelt sich um einen regulären Wert, der keinen Fehler anzeigt.error
-Funktion einen Übereinstimmungsfehler auslösen. Als Parameter wird eine Fehlermeldung verwendet. Fehler, die von dieser Funktion ausgelöst werden (sogenannte _benutzerdefinierte Übereinstimmungsfehler_), bestehen aus einer _Position_ und einer _Fehlermeldung_. Sie werden keine Erwartungen haben.Die Funktion error
unterbricht die Ausführung der Aktion nicht, sie markiert sie lediglich als fehlgeschlagen und speichert die Fehlermeldung. Der tatsächliche Fehler wird erst nach Abschluss der Aktionsausführung erzeugt. Wenn die Funktion error
mehrmals aufgerufen wird, gewinnt der letzte Aufruf (es wird seine Fehlermeldung verwendet).
Fehler bei benutzerdefinierten Übereinstimmungen werden wie normale Übereinstimmungsfehler behandelt, was bedeutet, dass sie das Parsen nicht vollständig anhalten und den Parser zurückverfolgen und möglicherweise andere Alternativen ausprobieren lassen. Es gibt jedoch einen Unterschied: Wenn schließlich die SyntaxError
-Ausnahme ausgelöst wird, gilt die Erwartungskombinationsregel, die für reguläre Übereinstimmungsfehler gilt, nicht für die benutzerdefinierten. Wenn es in der Menge der Übereinstimmungsfehler mit der am weitesten entfernten Position mindestens einen benutzerdefinierten gibt, werden die regulären einfach vollständig überschrieben. Wenn es mehr benutzerdefinierte Fehler gibt, gewinnt der zuletzt produzierte.
Eine SyntaxError
-Ausnahme, die auf einem benutzerdefinierten Match-Fehler basiert, unterscheidet sich von Ausnahmen, die auf einem regulären Fehler basieren. Die Eigenschaft message
entspricht der Fehlermeldung des Fehlers und die Eigenschaft expected
ist null
.
Beispiel
```
Start = Zeichen:[+-]? Ziffern:[0-9]+ {
var result = parseInt((sign || "") + digits.join(""), 10);
if (result % 2 == 0) {
error("The number must be an odd integer.");
}
return result;
}
```
Bei der Eingabe 2
erzeugt der aus der obigen Grammatik generierte Parser ein SyntaxError
, wobei message
auf "The number must be an odd integer."
und expected
auf null
gesetzt ist
expected
auszulösen. Als Parameter wird eine erwartete Wertbeschreibung verwendet. Diese Funktion wird hauptsächlich als Bequemlichkeit für Situationen bereitgestellt, in denen man keine vollständige Fehlermeldung und das automatisch generierte Formular "Erwartet _X_, aber "2" gefunden" generieren muss. reicht.Eine SyntaxError
-Ausnahme, die auf einem von der expected
-Funktion generierten Übereinstimmungsfehler basiert, ähnelt Ausnahmen, die auf einem regulären Fehler basieren.
Beispiel
```
Start = Zeichen:[+-]? Ziffern:[0-9]+ {
var result = parseInt((sign || "") + digits.join(""), 10);
if (result % 2 == 0) {
expected("odd integer");
}
return result;
}
```
Bei der Eingabe 2
erzeugt der aus der obigen Grammatik generierte Parser ein SyntaxError
, wobei message
auf "Expected odd integer but "2" found."
und expected
auf [ { type: "user", description: "odd integer" } ]
gesetzt ist
Ich freue mich über alle Anmerkungen zu den vorgeschlagenen Änderungen – bitte fügen Sie sie als Kommentare hinzu. Ich plane, bald mit der Umsetzung des Vorschlags (oder einer modifizierten Version basierend auf Feedback) zu beginnen.
Es wäre besser, wenn mehrere Aufrufe der Fehlerfunktion zu mehreren Fehlern führen würden. Sie könnten dann so viel wie möglich mit dem Parsen fortfahren.
string = '"' value:(!(eol / '"') .)+ '"' { return value; }
/ '"' value:(!(eol / '"') .)+ { error('unterminated string constant'); return value; }
Ich würde auch empfehlen, Unterstützung für Warnungen hinzuzufügen.
Es wäre besser, wenn mehrere Aufrufe der Fehlerfunktion zu mehreren Fehlern führen würden. Sie könnten dann so viel wie möglich mit dem Parsen fortfahren.
Können Sie einige Anwendungsfälle nennen, in denen Sie dies benötigen würden? Es scheint mir, dass das von Ihnen angegebene Beispiel auch mit meinem Vorschlag funktionieren würde.
Ich habe bereits einige Anwendungsfälle, aber ich weiß nicht, wie repräsentativ sie sind, also würde ich gerne mehr sehen.
Ich würde auch empfehlen, Unterstützung für Warnungen hinzuzufügen.
Ich denke auch darüber nach.
Ein Problem mit mehreren Fehlern und Warnungen besteht darin, dass sie eine andere Schnittstelle benötigen würden als die einfache und intuitive „ parse
gibt bei Erfolg einen Wert oder bei einem Fehler eine Ausnahme zurück“. Der Parser müsste mehrere Fehler bei nicht erfolgreicher Analyse sowie Warnungen sowohl bei erfolgreicher als auch bei nicht erfolgreicher Analyse melden.
Welche Art von API würden Sie hier am intuitivsten finden? Auch hier habe ich einige Ideen, aber ich würde gerne sehen, was andere denken.
Können Sie einige Anwendungsfälle nennen, in denen Sie dies benötigen würden? Es scheint mir, dass das von Ihnen angegebene Beispiel auch mit meinem Vorschlag funktionieren würde.
Mein primärer Anwendungsfall sind nicht schwerwiegende Parsing-Fehler. Nicht abgeschlossene Zeichenfolgen, Bezeichner, die mit einer Zahl beginnen, verschachtelte Kommentare, fehlende Semikolons usw.
Im Allgemeinen ist es am besten, den Parser so weit wie möglich vorrücken zu lassen, damit dem Benutzer so viele Fehler wie möglich angezeigt werden können. Wenn der Parser das Parsen überhaupt beenden könnte und die nächsten Schritte (z. B. Kompilierung) ebenfalls Fehler melden könnten, wäre das großartig.
Dies wird nun umgesetzt . Das Skript tools/impact
meldet die folgenden Auswirkungen auf die Leistung des gesamten Satzes von Commits:
Speed impact
------------
Before: 1144.21 kB/s
After: 999.89 kB/s
Difference: -12.62%
Size impact
-----------
Before: 863523 b
After: 1019968 b
Difference: 18.11%
(Measured by /tools/impact with Node.js v0.6.18 on x86_64 GNU/Linux.)
Ich denke, dass 12,62 % Geschwindigkeitseinbußen und 18,11 % Größeneinbußen in Ordnung sind, um eine so lange bestehende Reihe von Problemen zu lösen.
Schließen.
@dmajda : Das sind großartige Neuigkeiten! Ich bin so froh, dass null
keinen Fehler mehr signalisiert.
Es gibt also noch keine „Warn“-Funktion?
Das Thema Warnfunktion wird bei #325 verfolgt
Hilfreichster Kommentar
Es gibt also noch keine „Warn“-Funktion?