Question

J'essaie d'utiliser flex et bison pour créer un filtre, car je souhaite obtenir certains éléments de grammaire d'un langage complexe. Mon plan est d'utiliser flex + bison pour reconnaître la grammaire et supprimer l'emplacement des éléments d'intérêt. (Ensuite, utilisez un script pour récupérer le texte en fonction des emplacements vidés.)

J'ai découvert que flex peut prendre en charge une fonctionnalité de bison appelée bison-locations, mais son fonctionnement est identique. J'ai essayé l'exemple dans le document flex, il semble que le yylloc ne soit pas réglé automatiquement par flex, j'obtiens toujours (1,0) - (1,0) . Est-ce que flex peut calculer l'emplacement de chaque jeton automatiquement? Si non, quelle fonction d'interface est définie pour moi à implémenter? Y a-t-il un exemple?

Une meilleure solution en ce qui concerne les outils?

Cordialement, Kevin

Modifier:

Maintenant, l'interface pour yylex se tourne vers:

int yylex(YYSTYPE * yylval_param,YYLTYPE * yylloc_param );

Le manuel de bison ne spécifie pas comment lexer doit mettre en œuvre pour définir correctement yylloc_param. Pour moi, il est difficile de tracer manuellement le numéro de colonne de chaque jeton.

Était-ce utile?

La solution

Consultez la section 3.6 de le manuel de Bison - qui semble couvrir les emplacements de manière assez détaillée. Combiné avec ce que vous avez trouvé dans le manuel Flex, cela peut être suffisant.

Autres conseils

La déclaration yylex a probablement changé car vous avez utilisé un analyseur syntaxique pur ou à réentrant. On dirait que de nombreux documents sur le Web suggèrent que cela est nécessaire si vous voulez que les emplacements de bison fonctionnent, mais ce n'est pas obligatoire.

J’avais également besoin de numéros de ligne et j’ai trouvé la documentation de Bison confuse à cet égard. La solution simple (en utilisant le var global yylloc): Dans votre fichier Bison, ajoutez simplement la directive% locations:

%{
...
%}
%locations
...
%%
...

dans votre lexer:

%{
...
#include "yourprser.tab.h"  /* This is where it gets the definition for yylloc from */
#define YY_USER_ACTION yylloc.first_line = yylloc.last_line = yylineno;
%}
%option yylineno
...
%%
...

La macro YY_USER_ACTION est "appelée". avant chacune de vos actions de jeton et met à jour yylloc. Vous pouvez maintenant utiliser les règles @N / @ $ comme ceci:

statement : error ';'   { fprintf(stderr, "Line %d: Bad statement.\n", @1.first_line); }

, ou utilisez la variable yylloc global:

void yyerror(char *s)
{
  fprintf(stderr, "ERROR line %d: %s\n", yylloc.first_line, s);
}

J'aime la réponse de Shlomi.

De plus, je recherchais également la mise à jour de l’emplacement de la colonne. http://oreilly.com/linux/excerpts/9780596155971/error- trouvé reporting-recovery.html , ce qui était plus logique après avoir lu la réponse de Shlomi.

Malheureusement, il y a une faute de frappe sur cette page pour yylloc. Je l'ai simplifié un peu plus bas.

Dans votre analyseur, ajoutez:

%locations

dans votre lexer:

%{

#include "parser.tab.h"

int yycolumn = 1;

#define YY_USER_ACTION yylloc.first_line = yylloc.last_line = yylineno; \
    yylloc.first_column = yycolumn; yylloc.last_column = yycolumn + yyleng - 1; \
    yycolumn += yyleng; \
    yylval.str = strdup(yytext);

%}

%option yylineno

Il se peut que quelque chose se passe avec l’emplacement des colonnes qui ne garde pas strictement le suivi des colonnes mais continue à augmenter. C'est juste mon ignorance et m'excuse si cela déroute personne. J'utilise actuellement column pour conserver un nombre de caractères de fichier qui, dans mon cas, est plus avantageux que l'emplacement de la colonne.

L’espoir que cela aide.

Ni bison ni flex ne mettent à jour yylloc automatiquement, mais ce n'est pas difficile de le faire vous-même si vous connaissez le truc.

L'astuce pour implémenter la prise en charge de yylloc est que, même si yyparse () déclare yylloc , il ne le change jamais. Cela signifie que si vous modifiez yylloc lors d'un appel au lexer, vous retrouverez les mêmes valeurs lors du prochain appel. Ainsi, yylloc contiendra la position du dernier jeton. La fin du dernier jeton étant identique à celle du début du jeton actuel, vous pouvez utiliser l'ancienne valeur yylloc pour vous aider à déterminer la nouvelle valeur.

En d'autres termes, yylex () ne doit pas calculer yylloc ; il devrait mettre à jour yylloc .

Pour mettre à jour yylloc , nous devons d’abord copier les valeurs de last _ dans first _ , puis mettre à jour le last _ . valeurs pour refléter la longueur du jeton qui vient d’être apparié. (Ce n'est pas le strlen () du jeton; c'est la longueur des lignes et des colonnes.) Nous pouvons le faire dans la macro YY_USER_ACTION , appelée juste avant. toute action lexer est effectuée; cela garantit que si une règle correspond mais ne renvoie pas de valeur (par exemple, une règle ignorant les espaces ou les commentaires), l'emplacement de ce non-jeton est ignoré, plutôt que d'être inclus au début du jeton réel, ou perdu de manière à rendre le suivi de localisation inexact.

Voici une version destinée à un analyseur syntaxique réentrant; vous pouvez le modifier pour un analyseur non réentrant en échangeant les opérateurs - > pour . :

#define YY_USER_ACTION \
    yylloc->first_line = yylloc->last_line; \
    yylloc->first_column = yylloc->last_column; \
    for(int i = 0; yytext[i] != '\0'; i++) { \
        if(yytext[i] == '\n') { \
            yylloc->last_line++; \
            yylloc->last_column = 0; \
        } \
        else { \
            yylloc->last_column++; \
        } \
    }

Si vous préférez, vous pouvez placer ce code dans une fonction et faire en sorte que la macro appelle la fonction, mais les deux techniques sont équivalentes.

La réponse de Shomi est la solution la plus simple si vous souhaitez uniquement conserver le numéro de ligne. Toutefois, si vous souhaitez également connaître les numéros de colonne, vous devez les suivre.

Une façon de le faire est d’ajouter des règles yycolumn = 1 partout où une nouvelle ligne apparaît (comme suggéré dans la réponse de David Elson), mais si vous ne souhaitez pas garder une trace de tous les lieux, une nouvelle ligne pourrait afficher (espaces, commentaires, etc ...) une alternative consiste à inspecter le tampon yytext au début de chaque action:

static void update_loc(){
  static int curr_line = 1;
  static int curr_col  = 1;

  yylloc.first_line   = curr_line;
  yylloc.first_column = curr_col;

  {char * s; for(s = yytext; *s != '\0'; s++){
    if(*s == '\n'){
      curr_line++;
      curr_col = 1;
    }else{
      curr_col++;
    }
  }}

  yylloc.last_line   = curr_line;
  yylloc.last_column = curr_col-1;
}

#define YY_USER_ACTION update_loc();

Enfin, il convient de noter qu’une fois que vous commencez à garder à la main le numéro des colonnes, vous pouvez également suivre les numéros de ligne au même endroit et ne plus vous soucier d’utiliser le yylineno de Flex. option.

Donc, je veux que cela "fonctionne", mais avec quelques étapes supplémentaires (je les ai peut-être négligées ici ... toutes mes excuses):

  1. Dans parser.y , je devais dire:

    #define YYLEX_PARAM &yylval, &yylloc
    

    même avec % locations et bison --locations , pour le faire passer les données.

  2. Dans lexer.l , je devais utiliser - > au lieu de . pour < code> yylloc

  3. Également dans lexer.l , j'ai réinitialisé la colonne dans l'action:

    [\n] { yycolumn = 1; }
    

Évidemment un peu plus complexe, pour \ r , etc., mais au moins, je l’ai obtenu.

Je pense avoir réussi à le faire fonctionner (le mérite revient au rédacteur du manuel de bison Analyseur lexical ltcalc ). Par défaut, le bison crée un yylloc contenant

{ first_line, first_column , last_line , last_column }

Il suffit de mettre à jour ces valeurs dans notre analyseur lexical. Ex:

[ \t]     { ++yylloc.last_column; }
[\n]      { yyloc.last_column = 0; return EOL; }
[a-zA-Z]+ { 
            yylloc.last_column += strlen(yytext);
            return IDENTIFIER;
          }

Maintenant dans le bison, pour récupérer ces champs:

statement : IDENTIFIER '=' expression 
            { printf("%d - %d\n", @1.last_line, @1.last_column); }

Par défaut, ces champs sont initialisés à un. Nous devons initialiser les champs de colonne à zéro, sinon ils indiqueront la mauvaise colonne.

Un ajout à la réponse de Shlomi:

Si vous utilisez% define api.pure in bison pour créer un analyseur syntaxique réentrant, vous devez également spécifier% option bison-locations dans flex. En effet, dans un analyseur syntaxique réentrant, yylloc n’est pas une variable globale et doit être transmis au lexer.

Donc, dans l'analyseur:

%define api.pure
%locations

dans le lexer:

#include "yourprser.tab.h"
#define YY_USER_ACTION yylloc.first_line = yylloc.last_line = yylineno;
%option bison-locations
%option yylineno
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top