stringtranslate.com

Bisonte ÑU

GNU Bison , comúnmente conocido como Bison , es un generador de analizadores que forma parte del Proyecto GNU . Bison lee una especificación en la sintaxis de Bison (descrita como " BNF legible por máquina " [3] ), advierte sobre cualquier ambigüedad en el análisis y genera un analizador que lee secuencias de tokens y decide si la secuencia se ajusta a la sintaxis especificada por la gramática.

Los analizadores generados son portátiles: no requieren compiladores específicos. Bison genera de forma predeterminada analizadores LALR(1) pero también puede generar analizadores canónicos LR , IELR(1) y GLR . [4]

En modo POSIX , Bison es compatible con Yacc , pero también tiene varias extensiones sobre este programa anterior, incluyendo

Flex , un analizador léxico automático , se utiliza a menudo con Bison para tokenizar datos de entrada y proporcionar tokens a Bison. [5]

Bison fue escrito originalmente por Robert Corbett en 1985. [1] Más tarde, en 1989, Robert Corbett lanzó otro generador de analizadores llamado Berkeley Yacc . Richard Stallman hizo que Bison fuera compatible con Yacc . [6]

Bison es software gratuito y está disponible bajo la Licencia Pública General GNU , con una excepción (que se analiza a continuación) que permite utilizar su código generado sin activar los requisitos de copyleft de la licencia.

Características

Generación de contraejemplos

Un tema delicado con los generadores de analizadores LR es la resolución de conflictos (cambiar/reducir y reducir/reducir conflictos). Con muchos generadores de analizadores LR, la resolución de conflictos requiere el análisis del autómata analizador, lo que exige cierta experiencia por parte del usuario.

Para ayudar al usuario a comprender los conflictos de manera más intuitiva, Bison puede generar contraejemplos automáticamente. Para gramáticas ambiguas , Bison a menudo puede incluso producir contraejemplos que muestren que la gramática es ambigua.

Por ejemplo, en una gramática que sufre el infame problema de colgar otra cosa , informa Bison

doc/if-then-else.y: advertencia : cambiar/reducir el conflicto en el token "else" [- Wcounterexamples ] Ejemplo: "si" expr "entonces"  "si" expr "entonces" stmt   "else" stmt derivación de turnos if_stmt  ↳ "si" expr "entonces"  stmt   if_stmt  ↳ "si" expr "entonces" stmt   "else" stmt Ejemplo: "si" expr "entonces"  "si" expr "entonces" stmt   "else" stmt Reducir la derivación if_stmt  ↳ "si" expr "entonces"  stmt  "si no" stmt   if_stmt  ↳ "si" expr "entonces" stmt  

Reentrada

La reentrada es una característica que se ha agregado a Bison y no existe en Yacc.

Normalmente, Bison genera un analizador que no es reentrante . Para lograr la reentrada %define api.purese debe utilizar la declaración. Se pueden encontrar más detalles sobre la reentrada de Bison en el manual de Bison. [7]

Idiomas de salida

Bison puede generar código para C , C++ , D y Java . [8]

Para utilizar el analizador generado por Bison desde otros idiomas, se puede utilizar una herramienta de enlace de idiomas como SWIG .

Licencia y distribución del código generado.

Debido a que Bison genera código fuente que a su vez se agrega al código fuente de otros proyectos de software, surgen algunas preguntas simples pero interesantes sobre derechos de autor.

No se requiere una licencia compatible con GPL

El código generado por Bison incluye cantidades significativas de código del propio proyecto Bison. El paquete Bison se distribuye bajo los términos de la Licencia Pública General (GPL) de GNU, pero se ha agregado una excepción para que la GPL no se aplique a la salida. [9] [10]

Las versiones anteriores de Bison estipulaban que partes de su producción también tenían licencia GPL, debido a la inclusión de la función yyparse() del código fuente original en la salida.

Distribución de paquetes usando Bison

Los proyectos de software libre que utilizan Bison pueden tener la opción de distribuir el código fuente que su proyecto introduce en Bison, o el código C resultante generado por Bison. Ambos son suficientes para que un destinatario pueda compilar el código fuente del proyecto. Sin embargo, distribuir sólo la entrada conlleva el pequeño inconveniente de que los destinatarios deben tener instalada una copia compatible de Bison para que puedan generar el código C necesario al compilar el proyecto. Y distribuir solo el código C en la salida crea el problema de hacer que sea muy difícil para los destinatarios modificar el analizador, ya que este código no fue escrito ni por un humano ni para humanos; su propósito es ingresarlo directamente en un compilador de C.

Estos problemas se pueden evitar distribuyendo tanto los archivos de entrada como el código generado. La mayoría de las personas compilarán utilizando el código generado, lo cual no es diferente de cualquier otro paquete de software, pero cualquiera que quiera modificar el componente del analizador puede modificar primero los archivos de entrada y volver a generar los archivos generados antes de compilar. Los proyectos que distribuyen ambos generalmente no tienen los archivos generados en sus sistemas de control de revisiones . Los archivos sólo se generan al realizar un lanzamiento.

Algunas licencias, como la GPL , exigen que el código fuente esté en " la forma preferida de la obra para realizar modificaciones ". Los proyectos GPL que utilizan Bison deben distribuir los archivos que son la entrada para Bison. Por supuesto, también pueden incluir los archivos generados.

Usar

Debido a que Bison fue escrito como un reemplazo de Yacc y es en gran medida compatible, el código de muchos proyectos que utilizan Bison también podría introducirse en Yacc. Esto hace que sea difícil determinar si un proyecto "utiliza" código fuente específico de Bison o no. En muchos casos, el "uso" de Bison podría sustituirse trivialmente por el uso equivalente de Yacc o uno de sus otros derivados.

Bison tiene características que no se encuentran en Yacc, por lo que se puede decir verdaderamente que algunos proyectos "utilizan" Bison, ya que Yacc no sería suficiente.

La siguiente lista es de proyectos que se sabe que "utilizan" Bison en el sentido más amplio, que utilizan herramientas de desarrollo de software gratuitas y distribuyen código destinado a ser introducido en Bison o en un paquete compatible con Bison.

Un ejemplo completo de analizador reentrante

El siguiente ejemplo muestra cómo usar Bison y flex para escribir un programa de calculadora simple (solo suma y multiplicación) y un programa para crear un árbol de sintaxis abstracta . Los dos archivos siguientes proporcionan la definición y la implementación de las funciones del árbol de sintaxis.

/* * Expression.h * Definición de la estructura utilizada para construir el árbol de sintaxis. */ #ifndef __EXPRESSION_H__ #define __EXPRESSION_H__/** * @brief El tipo de operación */ typedef enum tagEOperationType { eVALUE , eMULTIPLY , eADD } EOperationType ;      /** * @brief La estructura de la expresión */ typedef struct tagSExpression { EOperationType tipo ; /* /< tipo de operación */      valor entero ; /* /< válido sólo cuando el tipo es eVALUE */ struct tagSExpression * left ; /* /< lado izquierdo del árbol */ struct tagSExpression * right ; /* /< lado derecho del árbol */ } SExpression ;           /** * @brief Crea un identificador * @param valor El valor del número * @return La expresión o NULL en caso de no tener memoria */ SExpression * createNumber ( int valor );  /** * @brief Crea una operación * @param type El tipo de operación * @param left El operando izquierdo * @param right El operando derecho * @return La expresión o NULL en caso de no tener memoria */ SExpression * createOperation ( EOperationType escriba , SExpression * izquierda , SExpression * derecha );      /** * @brief Elimina una expresión * @param b La expresión */ void deleteExpression ( SExpression * b );  #endif /* __EXPRESIÓN_H__ */
/* * Expression.c * Implementación de funciones utilizadas para construir el árbol de sintaxis. */#incluir "Expresión.h" #incluir <stdlib.h> /** * @brief Asigna espacio para la expresión * @return La expresión o NULL si no hay suficiente memoria */ static SExpression * allocateExpression () { SExpression * b = ( SExpression * ) malloc ( sizeof ( SExpression ));        si ( b == NULL ) devuelve NULL ;      b -> tipo = eVALUE ; b -> valor = 0 ;      b -> izquierda = NULO ; b -> derecha = NULO ;      devolver b ; } SExpression * createNumber ( int valor ) { SExpression * b = allocateExpression ();       si ( b == NULL ) devuelve NULL ;      b -> tipo = eVALUE ; b -> valor = valor ;      devolver b ; } SExpression * createOperation ( EOperationType tipo , SExpression * izquierda , SExpression * derecha ) { SExpression * b = allocateExpression ();           si ( b == NULL ) devuelve NULL ;      b -> tipo = tipo ; b -> izquierda = izquierda ; b -> derecha = derecha ;         devolver b ; } void eliminarExpresión ( SExpresión * b ) { si ( b == NULL ) retorno ;        eliminarExpresión ( b -> izquierda ); eliminarExpresión ( b -> derecha );  gratis ( segundo ); }

Los tokens que necesita el analizador Bison se generarán mediante flex.

% {/* * Archivo Lexer.l * Para generar el analizador léxico ejecute: "flex Lexer.l" */#incluye "Expresión.h" #incluye "Parser.h"  #incluir <stdio.h> % }% opción outfile = "Lexer.c" encabezado - archivo = "Lexer.h" % opción warn nodefault    % opción reentrante noyywrap nunca - nounistd interactivo % opción bisonte - puente     %%[ \ r \ n \ t ] * { continuar ; /* Saltar espacios en blanco. */ } [ 0-9 ] + { sscanf ( yytext , "%d" y yylval - > valor ); devolver TOKEN_NUMBER ; }            "*" { devolver TOKEN_STAR ; } "+" { regresar TOKEN_PLUS ; } "(" { devolver TOKEN_LPAREN ; } ")" { devolver TOKEN_RPAREN ; }                . { continuar ; /* Ignora caracteres inesperados. */ }   %%int yyerror ( SExpression ** expresión , escáner yyscan_t , const char * msg ) { fprintf ( stderr , "Error: %s \n " , msg ); devolver 0 ; }             

Los nombres de los tokens suelen ser neutrales: "TOKEN_PLUS" y "TOKEN_STAR", no "TOKEN_ADD" y "TOKEN_MULTIPLY". Por ejemplo, si admitiéramos el "+" unario (como en "+1"), sería incorrecto llamarlo "+" "TOKEN_ADD". En un lenguaje como C, "int *ptr" denota la definición de un puntero, no un producto: sería incorrecto llamar a este "*" "TOKEN_MULTIPLY".

Dado que los tokens los proporciona flex, debemos proporcionar los medios para comunicarse entre el analizador y el lexer . [23] El tipo de datos utilizado para la comunicación, YYSTYPE , se establece mediante la declaración %union de Bison .

Dado que en este ejemplo utilizamos la versión reentrante de flex y yacc, nos vemos obligados a proporcionar parámetros para la función yylex , cuando se llama desde yyparse . [23] Esto se hace a través de las declaraciones Bison %lex-param y %parse-param . [24]

% {/* * Archivo Parser.y * Para generar el analizador ejecute: "bison Parser.y" */#incluye "Expresión.h" #incluye "Parser.h" #incluye "Lexer.h"   // hace referencia a la implementación proporcionada en Lexer.l int yyerror ( SExpression ** expresión , yyscan_t scanner , const char * msg );       % }% código requiere { typedef void * yyscan_t ; }     % salida "Parser.c" % define "Parser.h"  % define API . puro % lex - param { escáner yyscan_t } % parse - param { SExpression ** expresión } % parse - param { escáner yyscan_t }             % unión { valor int ; SExpresión * expresión ; }     % token TOKEN_LPAREN "(" % token TOKEN_RPAREN ")" % token TOKEN_PLUS "+" % token TOKEN_STAR "*" % token < valor > TOKEN_NUMBER "número"           % tipo < expresión > expr  /* Precedencia (creciente) y asociatividad:  a+b+c es (a+b)+c: asociatividad izquierda  a+b*c es a+(b*c): la precedencia de "*" es mayor que la de " +". */ % restante "+" % restante "*"  %%entrada : expr { * expresión = $1 ; } ;        expr : expr [ L ] "+" expr [ R ] { $$ = crearOperación ( eADD , $L , $R ); } | expr [ L ] "*" expr [ R ] { $$ = crearOperación ( eMULTIPLY , $L , $R ); } | "(" expr [ E ] ")" { $$ = $E ; } | "número" { $$ = crearNúmero ( $1 ); } ;                                           %%

El código necesario para obtener el árbol de sintaxis utilizando el analizador generado por Bison y el escáner generado por flex es el siguiente.

/* * archivo principal.c */#incluye "Expresión.h" #incluye "Parser.h" #incluye "Lexer.h"   #incluir <stdio.h> int yyparse ( SExpression ** expresión , escáner yyscan_t );    SExpresión * getAST ( const char * expr ) { SExpresión * expresión ; escáner yyscan_t ; Estado YY_BUFFER_STATE ;          if ( yylex_init ( & scanner )) { /* no se pudo inicializar */ return NULL ; }       estado = yy_scan_string ( expr , escáner );    if ( yyparse ( & expresión , escáner )) { /* error al analizar */ return NULL ; }        yy_delete_buffer ( estado , escáner );  yylex_destroy ( escáner ); expresión de retorno ; } int evaluar ( SExpression * e ) { cambiar ( e -> tipo ) { caso eVALUE : devolver e -> valor ; caso eMULTIPLY : retorno evaluar ( e -> izquierda ) * evaluar ( e -> derecha ); caso eADD : devolver evaluar ( e -> izquierda ) + evaluar ( e -> derecha ); predeterminado : /* no debería estar aquí */ return 0 ; } }                          int principal ( vacío ) { prueba de caracteres [] = "4 + 2*10 + 3*(5 + 1)" ; SExpression * e = getAST ( prueba ); int resultado = evaluar ( e ); printf ( "El resultado de '%s' es %d \n " , prueba , resultado ); eliminarExpresión ( e ); devolver 0 ; }                   

Un archivo MAKE simple para construir el proyecto es el siguiente.

# MakefileARCHIVOS = Lexer.c Parser.c Expression.c main.c CC = g++ CFLAGS = -g -ansi          prueba : $( ARCHIVOS ) $( CC ) $( CFLAGS ) $( ARCHIVOS ) -o prueba     Lexer.c : Lexer . Flexiono Lexer.l​  Parser.c : Analizador . y Lexer . c bison parser.y   limpio : rm  -f  *.o  *~  Lexer.c  Lexer.h  Parser.c  Parser.h prueba 

Ver también

Referencias

  1. ^ ab Corbett, Robert Paul (junio de 1985). Semántica estática y recuperación de errores del compilador (Ph.D.). Universidad de California, Berkeley . DTIC ADA611756.
  2. ^ Akim Demaille (25 de septiembre de 2021). "Bisonte 3.8.2".
  3. ^ "Lenguaje y gramática (Bison 3.8.1)". www.gnu.org . Consultado el 26 de diciembre de 2021 .
  4. ^ Manual del bisonte: Introducción.
  5. ^ Levine, John (agosto de 2009). flexionar y bisonte. Medios O'Reilly. ISBN 978-0-596-15597-1.
  6. ^ "AUTORES". bisonte.git. Sabana GNU . Consultado el 26 de agosto de 2017 .
  7. ^ Bison Manual: un analizador puro (reentrante)
  8. ^ Manual del bisonte: resumen de la declaración del bisonte
  9. ^ Manual de bisontes: condiciones para utilizar bisontes
  10. ^ Un archivo de código fuente, parse-gram.c, que incluye la excepción
  11. ^ "análisis-gram.y". bisonte.git. Sabana GNU . Consultado el 29 de julio de 2020 .
  12. ^ "LexerParser en CMake". github.com .
  13. ^ Cambios, nuevas funciones y correcciones de la serie de versiones GCC 3.4
  14. ^ Cambios, nuevas funciones y correcciones de la serie de versiones GCC 4.1
  15. ^ Definición de gramática de Golang
  16. ^ "Parser.yy - Repositorio GNU LilyPond Git". git.savannah.gnu.org .
  17. ^ "4. Análisis de SQL: flex & bison [Libro]".
  18. ^ "GNU Octave: Archivo fuente Libinterp/Parse-tree/Oct-parse.cc".
  19. ^ "¿Qué hay de nuevo en Perl 5.10.0?". perl.org.
  20. ^ "La etapa del analizador". postgresql.org. 30 de septiembre de 2021.
  21. ^ "Analizador de resonancia magnética Ruby". github.com .
  22. ^ "Analizador XML de syslog-ng". github.com . 14 de octubre de 2021.
  23. ^ ab Flex Manual: escáneres C con analizadores Bison Archivado el 17 de diciembre de 2010 en Wayback Machine.
  24. ^ Manual de Bison: convenciones de llamada para analizadores puros

Otras lecturas

enlaces externos