Logo Studenta

Prototipo sintactico - Daniel Cuellar

¡Este material tiene más páginas!

Vista previa del material en texto

UNACAR
Universidad Autónoma del Carmen
“Por la Grandeza de México”
Facultad de Ciencias de la Información
MATERIA
LENGUAJES FORMALES Y AUTOMATAS
TRABAJO
Prototipo de software y reporte del analizador sintáctico
NOMBRE
DANIEL VALDEMAR CUELLAR VALLES
MATRICULA
160623
Ciudad del Carmen, Campeche A 19 mayo del 2022
Desarrollo
“GNU bison es un programa generador de analizadores sintácticos de propósito general perteneciente al proyecto GNU disponible para prácticamente todos los sistemas operativos, se usa normalmente acompañado de flex, aunque los analizadores léxicos se pueden también obtener de otras formas.
Bison convierte la descripción formal de un lenguaje, escrita como una gramática libre de contexto LALR, en un programa en C, C++, o Java que realiza análisis sintáctico. Es utilizado para crear analizadores para muchos lenguajes, desde simples calculadoras hasta lenguajes complejos.” (Wikipedia, 2021)
Bison fue la herramienta que se utilizó para la elaboración del prototipo sintáctico esta misma puede ser encontrada en su página oficial[footnoteRef:1], como complemento a esta herramienta su opto por utilizar Flex para su análisis léxico ya que se complementan muy bien a la hora de generar un lenguaje, así mismo Flex puede descargarse desde su página oficial[footnoteRef:2]. [1: (Corbett, 2014)] [2: (Paxson, 1995)] 
El código realizado se puede apreciar en la Imagen 1, este mismo se irá explicando sección por sección hasta comprobar su funcionamiento. 
%{
    void yyerror (char *s);
    int yylex();
    #include <stdio.h>
    FILE *yyin;
%}
%token ENTEROS
%token ENTEROS_SIGNO
%token FLOTANTE
%token FLOTANTE_SIGNO
%token SALTO
%token BEGAN
%token SET
%token SUMA
%token INTERSECCION
%token PRINT
%token END
%token IDENTIFICADOR
%start INICIO
%%
    INICIO  : BEGAN SALTO {printf("Inicio correcto\n");}
        | SET IDENTIFICADOR ',' IDENTIFICADOR ',' IDENTIFICADOR ';' SALTO {printf("Declaracion correcta\n");}
        | IDENTIFICADOR '=' '(' Expr ')' ';' SALTO
        | Oper {;}
        | PRINT '(' IDENTIFICADOR ')' ';' SALTO {printf("Impresion correcta\n");}
        | END {printf("Final correcto\n");}
        /*PERMITE LA RECURSIVIDAD*/
        | INICIO BEGAN SALTO {printf("Inicio correcto\n");}
        | INICIO SET IDENTIFICADOR ',' IDENTIFICADOR ',' IDENTIFICADOR ';' SALTO {printf("Declaracion correcta\n");}
        | INICIO IDENTIFICADOR '=' '(' Expr ')' ';' SALTO
        | INICIO Oper {;}
        | INICIO PRINT '(' IDENTIFICADOR ')' ';' SALTO {printf("Impresion correcta\n");}
        | INICIO END {printf("Final correcto\n");}
        ;
    
    Expr    : ENTEROS ',' Expr {;}
        | ENTEROS_SIGNO ',' {printf("Asignacion de enteros con signo correcta\n");}
        | FLOTANTE ',' {printf("Asignacion de flotantes sin signo correcta\n");}
        | FLOTANTE_SIGNO ',' {printf("Asignacion de flotantes con signo correcta\n");}
        | ENTEROS {printf("Asignacion de enteros sin signo correcta\n");}
        | ENTEROS_SIGNO {printf("Asignacion de enteros con signo correcta\n");}
        | FLOTANTE {printf("Asignacion de flotantes sin signo correcta\n");}
        | FLOTANTE_SIGNO {printf("Asignacion de flotantes con signo correcta\n");}
        | Expr ENTEROS ',' {;}
        ;
    Oper    : IDENTIFICADOR '=' {;}
        | SUMA'(' IDENTIFICADOR ',' IDENTIFICADOR ')'';' SALTO {printf("Operacion correcta\n");}
        | INTERSECCION'(' IDENTIFICADOR ',' IDENTIFICADOR ')'';' SALTO {printf("Operacion correcta\n");}
        ;
%%
void yyerror (char *s) {printf("Instruccion invalida\n");}
int main(int argc, char *argv[]){ 
    if ((yyin = fopen(argv[1], "rt")) == NULL){
        printf("\nError al abrir el archivo: %s\n", argv[1]);
        return 1;
    }else{
        printf("\nEl archivo %s se abrio correctamente\n", argv[1]);
        yyparse();
    }
    printf("\nSe finalizo la lectura del archivo %s \n", argv[1]);
    fclose(yyin);
    return 0;
}
Imagen 1-Código del analizador sintáctico
En la Imagen 2 se puede apreciar las declaraciones en C++ donde la primera linea le indica que hacer al analizador en caso de encontrar un error, este error surge al no haber ninguna paridad entre las reglas que se están colocando y el texto introducido al correr el programa. En la siguiente linea se encuentra la función que permite la comunicación entre el documento del analizador sintáctico y el léxico; seguidamente se ubica la función estándar cuyo objetivo de existir es permitir usar los comandos por defecto de C++. Por último, está el puntero que permite al programa desplazarse por todo el documento.
%{
    void yyerror (char *s);
    int yylex();
    #include <stdio.h>
    FILE *yyin;
%}
Imagen 2-Declaraciones en C++
La siguiente parte del código realizado corresponde a las declaraciones Bison (Imagen 3) en donde se referencian todos los tokens recolectados en el analizador léxico, lo importante a destacar en esta sección es que se tiene que dejar claro cuál es la palabra para iniciar las producciones gramaticales, en este caso fue la palabra INICIO.
%token ENTEROS
%token ENTEROS_SIGNO
%token FLOTANTE
%token FLOTANTE_SIGNO
%token SALTO
%token BEGAN
%token SET
%token SUMA
%token INTERSECCION
%token PRINT
%token END
%token IDENTIFICADOR
%start INICIO
Imagen 3-Reglas de la gramática
La sección más importante del código se ubica en las reglas de la gramática la cual esta ilustrada en la Imagen 4, la misma indica los pasos a seguir del código introducido. La forma de indicar al usuario que el código introducido es correcto se imprime en panta “-Situación- correcto”, así mismo cada sentencia ( a excepción de Begin y End) tendrán al final un “;” y la palabra “SALTO” que referencia al fin de la linea para pasar a la siguiente instrucción. 
Como primer token esta el inicio, es el token en el cual se van a derivar todas las instrucciones que indica la gramática, la primera sentencia después del inicio corresponde a la palabra reservada para iniciar el programa “Begin”. Seguidamente se colocó un símbolo “|” para indicar que en caso de no cumplirse la anterior condición se valide la que está a continuación, por ende, el paso a seguir es colocar la instrucción que contenga los tokens en el orden para la asignación de variables.
Después sigue la opción para inicializar una variable mediante los identificadores los símbolos requeridos para hacer esta función y una expresión referenciada que estará definida más adelante. Después de esta sentencia se hace referencia a una sentencia que servirá para hacer las operaciones del lenguaje propuesto, la razón para referenciar toda la sentencia es para evitar conflictos con el compilador pues dos sentencias inician con un identificador provocando errores donde no debería haberlos.
La penúltima opción en esta primera parte de reglas es la opción para imprimir, esta consta únicamente de una palabra reservada, dos paréntesis y un identificador que hace alusión a la variable donde están almacenados los datos. Por ultimo la instrucción para finalizar el programa en este caso la palabra reservada “End”.
Las anteriores corresponden a las reglas principales sin embargo en la siguiente sección del código se encuentran las mismas reglas con la única diferencia que hay un “INICIO” antes de cada una, esto es para permitir la recursividad a la hora de comprobar la gramática.
%%
    INICIO  : BEGAN SALTO {printf("Inicio correcto\n");}
        | SET IDENTIFICADOR ',' IDENTIFICADOR ',' IDENTIFICADOR ';' SALTO {printf("Declaracion correcta\n");}
        | IDENTIFICADOR '=' '(' Expr ')' ';' SALTO
        | Oper {;}
        | PRINT '(' IDENTIFICADOR ')' ';' SALTO {printf("Impresion correcta\n");}
        | END {printf("Final correcto\n");}
        /*PERMITE LA RECURSIVIDAD*/
        | INICIO BEGAN SALTO {printf("Inicio correcto\n");}
        | INICIO SET IDENTIFICADOR ',' IDENTIFICADOR ',' IDENTIFICADOR ';' SALTO {printf("Declaracion correcta\n");}
        | INICIO IDENTIFICADOR '=' '(' Expr ')' ';' SALTO
        | INICIO Oper {;}
        | INICIO PRINT '(' IDENTIFICADOR')' ';' SALTO {printf("Impresion correcta\n");}
        | INICIO END {printf("Final correcto\n");}
        ;
Imagen 4-Reglas principales
En la Imagen 5 se encuentra una regla que, referenciada anteriormente, está indica que al referenciarse puede ser sustituida por números enteros, enteros con signo, flotantes y flotantes con signo. Esto es posible ya que en cada sentencia se coloca el token de correspondiente al dato ingresado y por último se hace referencia a la expresión principal para poder ingresar más de dos números.
    Expr    : ENTEROS ',' Expr {;}
        | ENTEROS_SIGNO ',' {printf("Asignacion de enteros con signo correcta\n");}
        | FLOTANTE ',' {printf("Asignacion de flotantes sin signo correcta\n");}
        | FLOTANTE_SIGNO ',' {printf("Asignacion de flotantes con signo correcta\n");}
        | ENTEROS {printf("Asignacion de enteros sin signo correcta\n");}
        | ENTEROS_SIGNO {printf("Asignacion de enteros con signo correcta\n");}
        | FLOTANTE {printf("Asignacion de flotantes sin signo correcta\n");}
        | FLOTANTE_SIGNO {printf("Asignacion de flotantes con signo correcta\n");}
        | Expr ENTEROS ',' {;}
        ;
Imagen 5-Asignación de números
En la última sección de las reglas de la gramática se encuentran las dos opciones que tiene el lenguaje para hacer operaciones con los conjuntos (Imagen 6), los cuales son “Intersección” y “Suma”. En estas sentencias no es necesario la recursividad ya que solo se puede ingresar una de las dos, su forma de funcionar es como indica el código, ira una variable primero seguido de un símbolo igual y el tipo de operación a realizar por último dentro de dos paréntesis la variable en la cual se hará la operación.
    Oper    : IDENTIFICADOR '=' {;}
        | SUMA'(' IDENTIFICADOR ',' IDENTIFICADOR ')'';' SALTO {printf("Operacion correcta\n");}
        | INTERSECCION'(' IDENTIFICADOR ',' IDENTIFICADOR ')'';' SALTO {printf("Operacion correcta\n");}
        ;
%%
Imagen 6-Operaciónes de variables
Como se puede apreciar en la Imagen 7 el código que sigue es la última sección correspondiente al código C, como primera función está el yyerror que funciona para mandar un mensaje en caso de no cumplirse ninguna de las reglas anteriores.
Por último, se encuentra la función para leer un archivo, esta misma puede incorporarse en Flex para leer cualquier archivo, solo hay que cambiar la sentencia “yyparse()” por “yylex()”.
void yyerror (char *s) {printf("Instruccion invalida\n");}
int main(int argc, char *argv[]){ 
    if ((yyin = fopen(argv[1], "rt")) == NULL){
        printf("\nError al abrir el archivo: %s\n", argv[1]);
        return 1;
    }else{
        printf("\nEl archivo %s se abrio correctamente\n", argv[1]);
        yyparse();
    }
    printf("\nSe finalizo la lectura del archivo %s \n", argv[1]);
    fclose(yyin);
    return 0;
}
Imagen 7-Codigo C
Ya hecho el código en la herramienta se probó con un programa escrito en el lenguaje propuesto. Este mismo se puede apreciar en la Imagen 8.
Imagen 8-Codigo en el lenguaje propuesto
Los comandos para compilar el código escrito se pueden apreciar en la Imagen 9, más específicamente son los siguientes. Para el análisis léxico es “Flex -nombre del programa-“ y para el análisis sintáctico “Bison -yd -nombre del programa-“ una vez generados los ficheros de ambas herramientas se procesan con un compilador C en este caso se utilizó “MinGW[footnoteRef:3]”. La instrucción para generar el ejecutable es “gcc lex.yy.c y.tab.c -o -nombre del ejecutable-“ y con esto acabaría la parte que le corresponde a las herramientas solo resta probar que funcione en un caso real. [3: (Peters, 2000)] 
Imagen 9-Comandos para generar los ficheros
Continuando en la linea de comandos se probará que el programa escrito en el lenguaje propuesto es correcto, así como el código escrito en las herramientas para el análisis léxico y sintáctico. Se coloca el nombre del ejecutable y el nombre del programa a analizar como se muestra en la Imagen 10.
Y como es posible ver todo el programa corre a la perfección sin detectar ningún error, es posible identificar donde puede haber un error gracias a los mensajes de alerta al comprobar que una linea es correcta.
Imagen 10-Resultados de los programas
Conclusión
“Los analizadores sintácticos fueron extensivamente estudiados durante los años 70 del siglo XX, detectándose numerosos patrones de funcionamiento en ellos, cosa que permitió la creación de programas generadores de analizadores sintácticos a partir de una especificación de la sintaxis del lenguaje en forma Backus-Naur por ejemplo, tales y como Yacc, GNU bison y JavaCC.” (ExuRed, 2019)
Gracias a las herramientas para el análisis sintáctico como Bison es posible que existan los analizadores léxicos y por ende los compiladores pues, aparte de comprobar errores en la propia gramática se encargan de definir el orden en el que tiene que ser introducidos los comandos. Aparte de su aplicación en los distintos lenguajes de programación, los analizadores sintácticos también son aplicados en las páginas web desde el código “HTML” en donde lo traduce a un entorno visual mediante un motor de diseño en la pantalla hasta el propio código “URL” el cual descompone esquemas complejos para analizar la dirección ingresada. Con lo anterior dicho deja clara la utilidad de las herramientas de software para el análisis sintáctico pues va desde lo más especializado hasta lo más común como podría ser una página web. 
Gracias a lo aprendido en todo el proceso de crear un lenguaje para realizar operaciones con conjuntos fue posible compilar de manera satisfactoria los programas de ejemplo en el lenguaje propuesto. Ya que sin lo aprendido con la realización de autómatas finitos no deterministas y las producciones quedaría inconcluso este trabajo.
Referencias
Corbett, R. P. (06 de 08 de 2014). GNU Operating System. Obtenido de http://www.gnu.org/software/bison/
ExuRed. (16 de 07 de 2019). Analizador sintáctico. Obtenido de https://www.ecured.cu/Analizador_sintáctico
Paxson, V. (03 de 1995). Flex for Windows. Obtenido de http://gnuwin32.sourceforge.net/packages/flex.htm
Peters, C. (09 de 02 de 2000). MinGW - Minimalist GNU for Windows. Obtenido de https://sourceforge.net/projects/mingw/
Wikipedia. (11 de 09 de 2021). GNU Bison. Obtenido de https://es.wikipedia.org/wiki/GNU_Bison#:~:text=GNU%20bison%20es%20un%20programa,también%20obtener%20de%20otras%20formas.

Continuar navegando