Comment flex supporte-t-il bison-location exactement?
j'essaie d'utiliser flex et bison pour créer un filtre, parce que je veux obtenir certains éléments de grammaire à partir d'un langage complexe. Mon plan est d'utiliser flex + bison pour reconnaître la grammaire, et vider l'emplacement des éléments d'intérêt. (Puis utilisez un script pour saisir le texte en fonction des emplacements vidés.)
j'ai trouvé que flex peut supporter une caractéristique de bison appelée bison-localisations, mais comment cela fonctionne exactement. J'ai essayé l'exemple dans le document flex, il semble que le yylloc n'est pas défini automatiquement par flex, je reçois toujours (1,0)-(1,0)
. Flex pourrait-il calculer automatiquement l'emplacement de chaque token? Si ce n'est pas le cas, quelle fonction d'interface est définie pour que je puisse l'implémenter? Est-il un exemple?
meilleure solution en ce qui concerne les outils?
Meilleures Salutations, Kevin
Edit:
Maintenant, l'interface pour yylex tourner:
int yylex(YYSTYPE * yylval_param,YYLTYPE * yylloc_param );
le manuel bison ne spécifie pas comment lexer devrait implémenter pour définir correctement yylloc_param. Pour me il est difficile de tracer manuellement le numéro de colonne de chaque token.
8 réponses
jetez un coup d'oeil à la section 3.6 du Bison manuel - cela semble couvrir les emplacements en détail. Combiné avec ce que vous avez trouvé dans le manuel Flex, cela peut être suffisant.
la déclaration yylex a probablement changé parce que vous avez utilisé un rentrant ou un pur-parser. Il semble que de nombreux documents sur le web suggèrent qu'il est nécessaire si vous voulez que l'emplacement des bisons fonctionne, mais ce n'est pas nécessaire.
j'avais aussi besoin de numéros de ligne et j'ai trouvé la documentation sur les bisons confuse à cet égard. La solution simple (en utilisant le var global yylloc): Dans votre fichier Bison il suffit d'ajouter 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
...
%%
...
le La macro yy_user_action est" appelée " avant chacune de vos actions et mises à jour yylloc. Maintenant vous pouvez utiliser les règles @n/@$ comme ceci:
statement : error ';' { fprintf(stderr, "Line %d: Bad statement.\n", @1.first_line); }
, ou utilisez le yylloc global var:
void yyerror(char *s)
{
fprintf(stderr, "ERROR line %d: %s\n", yylloc.first_line, s);
}
de plus, je cherchais aussi à mettre à jour l'emplacement des colonnes. Found http://oreilly.com/linux/excerpts/9780596155971/error-reporting-recovery.html ce qui avait plus de sens après avoir lu la réponse de Shlomi.
malheureusement il y a une faute de frappe sur cette page pour yylloc. J'ai simplifié ci-dessous un peu.
%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 y a peut être quelque chose se passe avec l'emplacement de colonne qui ne garde pas strictement la trace des colonnes mais plutôt continue d'augmenter. C'est juste mon ignorance et mon apologisation si ça confond quelqu'un. Je suis en train d'utiliser la colonne pour garder un nombre de caractères de dossier qui dans mon cas est plus bénéfique que l'emplacement de colonne.
J'espère que ça aidera.
ni L'un ni l'autre bison
ni flex
mises à jour yylloc
automatiquement, mais il n'est en fait pas difficile de le faire vous-même-si vous connaissez le truc.
L'astuce pour la mise en œuvre de yylloc
support est que, même si yyparse()
déclare yylloc
, il ne change jamais. Cela signifie que si vous modifiez yylloc
dans un appel à l'analyseur lexical, vous trouverez les mêmes valeurs, à l'appel suivant. Ainsi,yylloc
contient la position du dernier jeton. Puisque la fin du dernier token est la même que le mot de démarrer, vous pouvez utiliser la vieille yylloc
valeur pour vous aider à déterminer la nouvelle valeur.
En d'autres termes, yylex()
ne devrait pas calculeryylloc
; il doit mise à jouryylloc
.
à mettre à jour yylloc
, il faut d'abord copier le last_
valeurs first_
, puis mettre à jour le last_
valeurs pour refléter la longueur du jeton juste-assorti. (Ce n'est pas le strlen()
du jeton; c'est la longueur des lignes et des colonnes.) Nous pouvez le faire dans le YY_USER_ACTION
macro, qui est appelée juste avant que n'importe quelle action de lexer soit exécutée; cela garantit que si une règle correspond mais qu'elle ne renvoie pas de valeur (par exemple, une règle sautant un espace blanc ou des commentaires), l'emplacement de ce non-jeton est sauté, plutôt que d'être inclus au début du jeton réel, ou perdu d'une manière qui rend le suivi d'emplacement inexact.
Voici une version destinée à un analyseur de rentrant; vous pouvez la modifier pour un non-rentrant analyseur par le remplacement de l' ->
les opérateurs .
:
#define YY_USER_ACTION \
yylloc->first_line = yylloc->last_line; \
yylloc->first_column = yylloc->last_column; \
for(int i = 0; yytext[i] != ''; i++) { \
if(yytext[i] == '\n') { \
yylloc->last_line++; \
yylloc->last_column = 0; \
} \
else { \
yylloc->last_column++; \
} \
}
si vous préférez, vous pouvez à la place mettre 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 vous souciez seulement de garder le numéro de ligne. Cependant, si vous voulez aussi des numéros de colonne, vous devez en tenir compte.
Une façon de le faire est d'ajouter yycolumn = 1
règle partout où une nouvelle ligne apparaît (comme suggéré dans la réponse de David Elson) mais si vous voulez garder une trace de tous les endroits où une nouvelle ligne pourrait apparaître (espace blanc, commentaires, etc...) une alternative est d'inspecter le yytext
tampon 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 != ''; 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, une chose à noter est qu'une fois que vous commencez à garder la trace des numéros de colonne à la main, vous pourriez aussi bien garder la trace des numéros de ligne au même endroit et ne pas prendre la peine d'utiliser Flex's yylineno
option.
Donc, j'ai eu ce "travail", mais avec quelques étapes supplémentaires (j'ai peut-être négligé ici ... toutes mes excuses dans ce cas):
analyseur.y, que j'avais à dire:
#define YYLEX_PARAM &yylval, &yylloc
même
%locations
etbison --locations
, pour faire passer les données.lexer.l j'ai eu à utiliser
->
au lieu de.
yylloc
également en 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 eu à travailler.
je pense que j'ai réussi à le faire fonctionner ( le crédit va à l'écrivain de bison manuel analyseur lexical ltcalc). Par défaut, bison crée yylloc qui contient
{ first_line, first_column , last_line , last_column }
Nous avons seulement besoin 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 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 devrions initialiser les champs de la colonne à zéro sinon ils signaleront le mauvais colonne.
un ajout à la réponse de Shlomi:
si vous utilisez l'api %define.pur dans bison pour créer un parser de rentrée, vous devez également spécifier %option bison-emplacements dans flex. C'est parce que dans un réentrant analyseur yylloc n'est pas une variable globale, et doit être transmis dans l'analyseur lexical.
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