Pegjs: Rapport d'erreur de reconception

Créé le 14 août 2013  ·  7Commentaires  ·  Source: pegjs/pegjs

Je vois trois problèmes avec le système actuel de rapport d'erreurs dans PEG.js :

  1. Les actions signalent les erreurs en renvoyant null
Reporting 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)).

Voir aussi #17.

  1. Il n'y a aucun moyen de produire des erreurs avec des messages entièrement personnalisés
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).

  1. La propriété expected de SyntaxError est difficile à traiter mécaniquement
The `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).

Chaque attente est représentée par une chaîne. Cette chaîne peut signifier un littéral attendu (représenté à l'aide de sa syntaxe PEG.js — une chaîne entre guillemets), une classe de caractères (représentée à l'aide de sa syntaxe PEG.js — [...] ) ou n'importe quel caractère (représenté par la chaîne "any" ). Pour différencier ces cas, il faut analyser manuellement les représentations de chaîne, ce qui rend inutilement difficile la création d'outils basés sur PEG.js tels que les autocompléteurs.

Je prévois de reconcevoir le système de rapport d'erreurs pour résoudre ces problèmes. Avant de décrire les changements auxquels je pense, une petite description du fonctionnement du système actuel est nécessaire.

Comment fonctionne actuellement le rapport d'erreur ?

Lorsqu'un analyseur PEG.js tente de faire correspondre un littéral de chaîne, une classe de caractères ou . , et échoue, il produit un _échec de correspondance_. Il se compose d'une _position_ et d'une _attente_ (description de ce que l'analyseur a essayé de faire correspondre).

Après avoir produit un échec de correspondance, l'analyseur revient en arrière et essaie éventuellement d'autres alternatives. Si aucun d'entre eux ne réussit et qu'il n'y a plus rien à essayer, l'analyseur lève l'exception SyntaxError . Lors de la définition de ses propriétés (telles que la position, les attentes, le message d'erreur, etc.), l'analyseur ne considère que l'échec de correspondance le plus éloigné (celui avec la plus grande position). S'il y a plus de tels échecs, leurs attentes sont combinées.

La situation est un peu plus compliquée si des règles nommées sont utilisées, mais cela n'est pas pertinent pour cette discussion.

Changements proposés

Je pense aux changements suivants dans le système de rapport d'erreur :

  1. Les attentes dans SyntaxError.expected ne seront pas représentées par des chaînes, mais par des objets. Les objets auront une propriété type (avec des valeurs comme "literal" , "class" , "any" , "eof" ) qui déterminera le type d'attente. Pour certains types d'attentes, d'autres propriétés contiendront les détails (par exemple pour l'attente "literal" où serait une propriété value contenant la chaîne attendue). Cela simplifiera le traitement mécanique des attentes.

Une alternative à la propriété type consiste à utiliser des classes. Mais je pense que la propriété type sera plus facile à gérer pour les utilisateurs.

  1. Les actions seront autorisées à retourner null . Ce sera une valeur normale et ne signifiera pas une erreur.
  2. Les actions pourront déclencher un échec de correspondance à l'aide d'une nouvelle fonction error . Il prendra un message d'erreur comme paramètre. Les échecs déclenchés par cette fonction (appelés _échecs de correspondance personnalisée_) consisteront en une _position_ et un _message d'erreur_. Ils n'auront aucune attente.

La fonction error n'interrompra pas l'exécution de l'action, elle la marquera simplement comme ayant échoué et enregistrera le message d'erreur. L'échec réel ne se produira qu'après la fin de l'exécution de l'action. Si la fonction error est invoquée plusieurs fois, la dernière invocation l'emportera (son message d'erreur sera utilisé).

Les échecs de correspondance personnalisés seront traités comme des échecs de correspondance normaux, ce qui signifie qu'ils n'arrêteront pas complètement l'analyse et laisseront l'analyseur revenir en arrière et éventuellement essayer d'autres alternatives. Il y a cependant une différence - lors de la levée finale de l'exception SyntaxError , la règle de combinaison des attentes qui s'applique aux échecs de correspondance réguliers ne s'appliquera pas aux échecs personnalisés. Si dans l'ensemble des échecs de correspondance avec la position la plus éloignée, il y a au moins un échec personnalisé, il remplacera simplement complètement les échecs réguliers. S'il y a plus d'échecs personnalisés, celui qui a été produit en dernier l'emportera.

Une exception SyntaxError basée sur un échec de correspondance personnalisée sera différente des exceptions basées sur un échec normal. Sa propriété message sera égale au message d'erreur de l'échec et sa propriété expected sera null .

Exemple

```
début = signe : [+-] ? chiffres :[0-9]+ {
var result = parseInt((sign || "") + digits.join(""), 10);

 if (result % 2 == 0) {
   error("The number must be an odd integer.");
 }

 return result;

}
```

À l'entrée 2 , l'analyseur généré à partir de la grammaire ci-dessus produira un SyntaxError avec message défini sur "The number must be an odd integer." et expected défini sur null

  1. Les actions pourront également déclencher un échec de correspondance régulier à l'aide de la fonction expected . Il prendra une description de valeur attendue comme paramètre. Cette fonction sera fournie principalement comme une commodité pour les situations où l'on n'a pas besoin de générer un message d'erreur complet et de générer automatiquement le formulaire "Expected _X_ but "2" found." est assez.

Une exception SyntaxError basée sur un échec de correspondance généré par la fonction expected sera similaire aux exceptions basées sur un échec normal.

Exemple

```
début = signe : [+-] ? chiffres :[0-9]+ {
var result = parseInt((sign || "") + digits.join(""), 10);

 if (result % 2 == 0) {
   expected("odd integer");
 }

 return result;

}
```

À l'entrée 2 , l'analyseur généré à partir de la grammaire ci-dessus produira un SyntaxError avec message défini sur "Expected odd integer but "2" found." et expected défini sur [ { type: "user", description: "odd integer" } ]

Prochaines étapes

J'accueille toutes les notes sur les modifications proposées - veuillez les ajouter en tant que commentaires. Je prévois de commencer à mettre en œuvre la proposition (ou une version modifiée basée sur les commentaires) bientôt.

feature

Commentaire le plus utile

Donc, il n'y a pas encore de fonction "warning" ?

Tous les 7 commentaires

Il serait préférable que plusieurs invocations de la fonction d'erreur conduisent à plusieurs erreurs. Vous pouvez ensuite continuer à analyser autant que possible.

string = '"' value:(!(eol / '"') .)+ '"' { return value; }
       / '"' value:(!(eol / '"') .)+     { error('unterminated string constant'); return value; }

Je recommanderais également d'ajouter la prise en charge des avertissements.

Il serait préférable que plusieurs invocations de la fonction d'erreur conduisent à plusieurs erreurs. Vous pouvez ensuite continuer à analyser autant que possible.

Pourriez-vous nommer des cas d'utilisation où vous en auriez besoin ? Il me semble que l'exemple que vous avez fourni fonctionnerait également avec ma proposition.

J'ai déjà quelques cas d'utilisation, mais je ne sais pas à quel point ils sont représentatifs, alors j'aimerais en voir d'autres.

Je recommanderais également d'ajouter la prise en charge des avertissements.

J'y pense aussi.

Un problème avec plusieurs erreurs et avertissements est qu'ils auraient besoin d'une interface différente de la simple et intuitive « parse renvoie une valeur en cas de succès ou une exception en cas d'erreur ». L'analyseur devrait signaler plusieurs erreurs en cas d'échec de l'analyse, ainsi que des avertissements en cas d'analyse réussie et non réussie.

Quel type d'API trouveriez-vous le plus intuitif ici ? Encore une fois, j'ai quelques idées, mais j'aimerais voir ce que les autres en pensent.

Pourriez-vous nommer des cas d'utilisation où vous en auriez besoin ? Il me semble que l'exemple que vous avez fourni fonctionnerait également avec ma proposition.

Mon principal cas d'utilisation concerne les erreurs d'analyse non fatales. Chaînes non terminées, identifiants commençant par un chiffre, commentaires imbriqués, points-virgules manquants, etc.

Généralement, il est préférable de laisser l'analyseur avancer aussi loin que possible, afin que le plus d'erreurs possible puisse être montré à l'utilisateur. Si l'analyseur pouvait même terminer l'analyse et laisser les étapes suivantes (par exemple la compilation) signaler également les erreurs, ce serait formidable.

C'est désormais implémenté . Le script tools/impact signale l'impact suivant sur les performances de l'ensemble des 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.)

Je pense qu'une pénalité de vitesse de 12,62 % et une pénalité de taille de 18,11 % suffisent pour résoudre un ensemble de problèmes aussi anciens.

Fermeture.

@dmajda : C'est une excellente nouvelle ! Je suis tellement content que null ne signale plus l'échec.

Donc, il n'y a pas encore de fonction "warning" ?

Le sujet de la fonction d'avertissement est suivi au #325

Cette page vous a été utile?
0 / 5 - 0 notes