Pregunta

Estoy tratando de usar flex y bison para crear un filtro, porque quiero obtener ciertos elementos gramaticales de un lenguaje complejo. Mi plan es usar flex + bison para reconocer la gramática y descartar la ubicación de los elementos de interés. (Luego, use un script para capturar texto de acuerdo con las ubicaciones descargadas).

Encontré que flex puede soportar una característica de bison llamada bison-ubicaciones, pero cómo funciona exactamente. Probé el ejemplo en documento flex, parece que yylloc no se configura automáticamente por flex, siempre obtengo (1,0) - (1,0) . ¿Flex podría calcular la ubicación de cada token automáticamente? Si no, ¿qué función de interfaz se define para que implemente? ¿Hay algún ejemplo?

¿Alguna solución mejor con respecto a las herramientas?

Saludos cordiales, Kevin

Edición :

Ahora la interfaz para yylex se vuelve a:

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

manual de bison no especifica cómo se debe implementar lexer para configurar correctamente yylloc_param. Para mí es difícil rastrear manualmente el número de columna de cada token.

¿Fue útil?

Solución

Eche un vistazo a la sección 3.6 de el manual de Bison , que parece cubrir ubicaciones con cierto detalle. Combinado con lo que encontró en el manual de Flex, eso puede ser suficiente.

Otros consejos

La declaración yylex probablemente cambió porque usaste un reentrante o un analizador puro. Parece que muchos documentos en la web sugieren que es obligatorio si desea que las ubicaciones de bisontes funcionen, pero no es obligatorio.

También necesitaba números de línea y la documentación de Bison me pareció confusa a ese respecto. La solución simple (usando el var yylloc global): En su archivo Bison simplemente agregue la directiva% ubicaciones:

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

en tu 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 se llama " antes de cada una de sus acciones de token y actualizaciones yylloc. Ahora puede usar las reglas @N / @ $ como esta:

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

, o use el yylloc global var:

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

Me gusta la respuesta de Shlomi.

Además, también estaba buscando actualizar la ubicación de la columna. Encontrado http://oreilly.com/linux/excerpts/9780596155971/error- reporting-recovery.html que tenía más sentido después de leer la respuesta de Shlomi.

Desafortunadamente hay un error tipográfico en esa página para yylloc. Lo he simplificado un poco más abajo.

En su analizador agregue:

%locations

en tu 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

Puede haber algo con la ubicación de la columna que no hace un seguimiento estricto de las columnas, sino que simplemente sigue aumentando. Esa es solo mi ignorancia y disculparme si confunde a alguien. Actualmente estoy usando la columna para mantener un recuento de caracteres de archivo que en mi caso es más beneficioso que la ubicación de la columna.

Espero que eso ayude.

Ni bison ni flex actualizan yylloc automáticamente, pero en realidad no es difícil hacerlo usted mismo & # 8212; si conoce el truco.

El truco para implementar el soporte yylloc es que, aunque yyparse () declara yylloc , nunca lo cambia. Eso significa que si modifica yylloc en una llamada al lexer, encontrará los mismos valores en la siguiente llamada. Por lo tanto, yylloc contendrá la posición del último token. Dado que el final del último token es el mismo que el inicio del token actual, puede usar el antiguo valor yylloc para ayudarlo a determinar el nuevo valor.

En otras palabras, yylex () no debe calcular yylloc ; debería actualizar yylloc .

Para actualizar yylloc , primero debemos copiar los valores de last_ a first_ y luego actualizar el last_ valores para reflejar la longitud del token que coincide. (Este no es el strlen () del token; es la longitud de las líneas y columnas). Podemos hacerlo en la macro YY_USER_ACTION , que se llama justo antes se realiza cualquier acción lexer; eso garantiza que si una regla coincide pero no devuelve un valor (por ejemplo, una regla que omite espacios en blanco o comentarios), se omite la ubicación de ese no token, en lugar de incluirse al comienzo del token real, o perdido de una manera que hace que el seguimiento de ubicación sea inexacto.

Aquí hay una versión destinada a un analizador reentrante; puede modificarlo para un analizador no reentrante cambiando los operadores - > por . :

#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 lo prefiere, podría poner ese código en una función y hacer que la macro llame a la función, pero las dos técnicas son equivalentes.

La respuesta de Shomi es la solución más simple si solo te importa mantener el número de línea. Sin embargo, si también desea números de columna, debe realizar un seguimiento de ellos.

Una forma de hacerlo es agregar las reglas yycolumn = 1 en todas partes donde aparece una nueva línea (como se sugiere en la respuesta de David Elson), pero si no desea hacer un seguimiento de todos los lugares que podría crear una nueva línea mostrar (espacios en blanco, comentarios, etc.) una alternativa es inspeccionar el búfer yytext al comienzo de cada acción:

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();

Finalmente, una cosa a tener en cuenta es que una vez que comience a realizar un seguimiento manual de los números de columna, también podría realizar un seguimiento de los números de línea en el mismo lugar y no molestarse en usar el yylineno de Flex opción.

Entonces, conseguí esto para "trabajar", pero con un par de pasos adicionales (puede que los haya pasado por alto aquí ... disculpas en ese caso):

  1. En parser.y , tenía que decir:

    #define YYLEX_PARAM &yylval, &yylloc
    

    incluso con % ubicaciones y bison --locations , para que pase los datos.

  2. En lexer.l tuve que usar - > en lugar de . para < code> yylloc

  3. También en lexer.l , restablezco la columna en la acción:

    [\n] { yycolumn = 1; }
    

Obviamente un poco más complejo, para \ r etc., pero al menos lo hice funcionar.

Creo que logré hacerlo funcionar (el crédito va al autor del manual de bison analizador léxico ltcalc ). Por defecto, bison crea yylloc que contiene

{ first_line, first_column , last_line , last_column }

Solo necesitamos actualizar esos valores en nuestro analizador léxico. Ej:

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

Ahora en bisonte, para recuperar esos campos:

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

Por defecto, estos campos se inicializan en uno, deberíamos inicializar los campos de la columna a cero; de lo contrario, informarán la columna incorrecta.

Una adición a la respuesta de Shlomi:

Si está utilizando% define api.pure en bison para crear un analizador reentrante, también debe especificar% option bison-ubicaciones en flex. Esto se debe a que en un analizador reentrante, yylloc no es una variable global y debe pasarse al lexer.

Entonces, en el analizador:

%define api.pure
%locations

en el lexer:

#include "yourprser.tab.h"
#define YY_USER_ACTION yylloc.first_line = yylloc.last_line = yylineno;
%option bison-locations
%option yylineno
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top