Pegjs: Ajouter la possibilité de suivre la position du nœud

Créé le 13 août 2011  ·  15Commentaires  ·  Source: pegjs/pegjs

Les analyseurs générés par PEG.js ne suivent actuellement pas la position (ligne et colonne). J'aimerais ajouter cette fonctionnalité car elle serait très utile.

Une façon consiste à ajouter les propriétés line et column à chaque objet renvoyé comme résultat de correspondance :

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

Une autre méthode consiste simplement à rendre disponibles des variables spéciales line et column dans les actions/prédicats, en se référant à la position du début de la règle actuelle :

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

La première méthode est plus flexible, mais cette flexibilité n'est peut-être pas nécessaire en réalité. Je ne suis pas encore sûr de la manière dont je vais implémenter.

Les deux manières nuiraient à la performance. Pour éviter cela dans les cas où le suivi de position n'est pas requis, le suivi doit être activé uniquement si l'option trackPosition avec une valeur de vérité est passée à PEG.buildParser lors de la génération de l'analyseur.

feature

Commentaire le plus utile

@tomitrescak cette fonctionnalité a apparemment changé plusieurs fois dans le passé. Consultez le journal des modifications pour plus d'informations. tl;dr est que vous devez utiliser la fonction location() dans votre grammaire

Tous les 15 commentaires

Les deux options seront excellentes. Pour mes besoins, il suffit de ligne, la colonne ira bien.

Je cherche également une solution pour cela. Actuellement, j'ai rapidement piraté quelque chose basé sur computeErrorPosition(), mais il y a plusieurs défauts.

En surface, la première approche semble plus intuitive que la seconde.

j'aime mieux la première approche, aussi.

Une efficacité possible est que name.position soit une fonction, afin qu'elle puisse être calculée paresseusement. Cela pourrait alors renvoyer un tableau à 2 éléments de ligne et de colonne. Juste une idée pour ceux qui ne veulent pas calculer la position de chaque élément.

Avec le recul, il semble préférable de fournir les positions des caractères de début et de fin, puis de fournir une fonction d'assistance dans la bibliothèque qui convertit la position en ligne/colonne. Puisque dans le cas général, vous n'avez pas besoin de ligne/colonne à moins qu'il y ait une erreur, et une fois que vous avez atteint une erreur, vous n'en avez généralement besoin qu'une seule fois.

Je le fais actuellement d'une manière hackish en abusant des variables startPos0 et pos , ce qui semble hackish, mais fonctionne pour l'instant.

Jusqu'à ce qu'il y ait un correctif officiel, j'inclus cette fonction (une copie approximative de computeErrorPosition ) en haut de mes grammaires. Au moins, cela me donnera la position _actuelle_ (mais pas la position des nœuds individuels):

  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 };
  }

Ce problème a été résolu par une série de commits .

Vous pouvez maintenant passer l'option trackLineAndColumn à la fonction PEG.buildParser :

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

Définir trackLineAndColumn sur true rend deux nouvelles variables visibles dans les actions et les prédicats — line et column . Pour les actions, ces variables indiquent la position de départ de l'expression de l'action tandis que dans les prédicats, elles indiquent la position actuelle. Le comportement légèrement différent est motivé par l'utilisation prévue.

Le suivi des lignes et des colonnes est facultatif car il nuit aux performances (il rend les analyseurs environ 3 à 4 fois plus lents). Je pourrai peut-être optimiser cela quelque peu à l'avenir (j'ai d'abord essayé de le faire fonctionner).

Dans la description du problème, je mentionnais une approche différente de ce problème : ajouter les propriétés line et column à chaque résultat de correspondance. Bien que cette solution soit plus propre et préférée par les utilisateurs, j'ai réalisé que l'on ne peut pas définir de propriétés sur des valeurs primitives telles que des chaînes, des nombres ou des booléens (qui sont souvent renvoyés comme résultats de correspondance). Le retour d'instances d'objets d'emballage (par exemple String , Number ou Boolean ) serait une solution de contournement possible, mais cela conduirait probablement à des bogues subtils dans les analyseurs créés par les utilisateurs, car ces enveloppes se comportent légèrement différemment des primitives. J'ai donc décidé de mettre en œuvre la proposition alternative.

La page de test en ligne sera-t-elle mise à jour pour prendre en charge cette fonctionnalité ?

@paulftw L'éditeur en ligne utilise toujours la dernière version stable de PEG.js (actuellement 0.6.2). Je le mettrai à jour une fois que j'aurai publié PEG.js 0.7.0 (qui inclura cette fonctionnalité). À moins que quelque chose d'inattendu ne se produise, cela se produira dans la seconde moitié d'avril.

Notez que le code source du site Web est disponible dans GitHub , donc si vous êtes impatient, vous pouvez l'exécuter et le modifier vous-même.

Je viens de réaliser que toutes les lignes et colonnes sont basées sur 1, tandis que les tableaux JS ainsi que presque tous les langages et bibliothèques modernes utilisent une indexation basée sur 0.

Une chance de revenir sur cette décision de conception ?

@paulftw Pouvez-vous donner un exemple de générateur d'analyseur qui signale les lignes et les colonnes comme étant basées sur 0 ?

L'idée derrière ma décision est que ces nombres seront très probablement affichés aux utilisateurs (dans les messages d'erreur, en tant que positions de nœud, etc.), donc l'indexation basée sur 1 a plus de sens que celle basée sur 0. Pour le traitement machine, la variable offset sera probablement utilisée le plus souvent, celle-ci étant basée sur 0.

Une chance de revenir sur cette décision de conception ?

Oui, si je deviens convaincu :-)

J'utilise peg.js pour surligner du texte dans l'éditeur Ace. Tout naturellement, les numéros de ligne Ace sont basés sur 0.
Il semble cependant que dans Bison les localisations soient basées sur 1.
http://git.savannah.gnu.org/cgit/bison.git/tree/src/location.c#n73

Si c'est effectivement le cas, alors je devrais arrêter de discuter.

Vous ne savez pas quel genre d'exemple vous voulez voir.
En ce moment, j'utilise le code suivant pour trouver un nom de symbole :

ravisseur
= "" { return new Position(line - 1, column - 1); }

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

@paulftw J'ai divisé ce problème en un problème distinct . J'attendrais que les utilisateurs adoptent la version 0.7.0 et voient quelle est l'utilisation courante pour décider.

Je sais que c'est fermé depuis longtemps, mais comment puis-je faire en sorte que cela fonctionne ? Suis assez noob dans ce domaine. J'ai compilé ma grammaire avec le drapeau "trackLineAndColumn': true", mais les nœuds ne contiennent toujours pas de lignes et de colonnes. Quoi d'autre est nécessaire?

Vous mentionnez ce qui suit, je ne sais tout simplement pas comment l'utiliser, est-ce documenté quelque part, s'il vous plaît ?

Définir trackLineAndColumn sur true rend deux nouvelles variables visibles dans les actions et les prédicats — ligne et colonne. Pour les actions, ces variables indiquent la position de départ de l'expression de l'action tandis que dans les prédicats, elles indiquent la position actuelle. Le comportement légèrement différent est motivé par l'utilisation prévue.

@tomitrescak cette fonctionnalité a apparemment changé plusieurs fois dans le passé. Consultez le journal des modifications pour plus d'informations. tl;dr est que vous devez utiliser la fonction location() dans votre grammaire

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

Questions connexes

ceymard picture ceymard  ·  32Commentaires

ceremcem picture ceremcem  ·  22Commentaires

dmajda picture dmajda  ·  20Commentaires

dmajda picture dmajda  ·  71Commentaires

jiyinyiyong picture jiyinyiyong  ·  34Commentaires