Logo Studenta

Prototipo de software-analizador lexico - 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-analizador léxico
NOMBRE
DANIEL VALDEMAR CUELLAR VALLES
MATRICULA
160623
Ciudad del Carmen, Campeche A 9 mayo del 2022
Índice
Introducción	3
Contenido	4
Sección de definiciones	6
Sección de opciones	7
Sección de tokens	7
Sección de reglas	9
Sección de rutinas del usuario	10
Pruebas	11
Conclusión	13
Referencias	14
Introducción
“La semántica es la ciencia que estudia los significados de la lengua: el modo en que se componen, sus mecanismos y procedimientos, etcétera. Además, en una lengua específica, las palabras que componen el léxico se ordenan de acuerdo con sus significados compartidos, a las asociaciones que a partir de ellos surgen, en estructuras mentales llamadas campos léxico-semánticos.” (Equipo editorial Etecé, 2021)
Esta ciencia está presente desde que nacemos pues la manera de expresarse cuando no se tiene conocimiento sobre el lenguaje es mediante señas y ruidos los cuales se asocian a una acción o necesidad. Es de esta manera que el ser humano logra comunicarse pues desde hace miles de años se crearon palabras que simplifican la manera de comunicarnos mediante acciones, cada palabra tiene su significado esto es conocido como el léxico, no es más que el conjunto de palabras conocidas de un idioma y de significados asociados, sin embargo, funciona en diferentes niveles la lengua formal y la lengua formal.
Estos lenguajes formales son en pocas palabras lenguajes cuyos símbolos son primitivos y las reglas para unir esos símbolos están debidamente especificadas, de la misma forma como las expresiones regulares las cuales se utilizan para crear autómatas validadores de lenguaje, por ejemplo. Estos ultimos sirven para dar certeza a un programador que un lenguaje funcione correctamente, esto claro si se hace de manera manual, sin embargo, hoy en día existen herramientas que facilitan la tarea de verificar un lenguaje. Siendo así las herramientas para el análisis léxico, dicho análisis es la primera fase de un compilador, fase que se encarga de leer el flujo de caracteres de entrada y transformarlo en una secuencia de componentes léxicos que utilizará el analizador sintáctico. 
Contenido
“Flex es una herramienta que permite generar analizadores léxicos. A partir de un conjunto de expresiones regulares, Flex busca concordancias en un fichero de entrada y ejecuta acciones asociadas a estas expresiones. Es compatible casi al 100% con Lex, una herramienta clásica de Unix para la generación de analizadores léxicos, pero es un desarrollo diferente realizado por GNU bajo licencia GPL.” (Hernández, 2008)
El analizador léxico realizado en la herramienta Flex se puede apreciar en la Ilustración 1 donde el código se divide en las secciones del propio Flex como pueden ser la sección de definiciones, la sección de reglas y la sección de rutinas del usuario, dentro de la sección de definiciones se divide en dos secciones las cuales son la sección de opciones y la sección de tokens. Dentro de estas mismas hay subsecciones donde se separa el código para que sea más entendible.
%{
#include <stdio.h>
FILE *yyin;
int linea=1;
%}
/*Seccion de opciones*/
/*Esta indica a Flex que lea sólo un fichero de entrada*/
%option noyywrap
/*Permite obtener el número de linea*/
%option yylineno
/* SECCION DE TOKENS */
DIGITO          [0-9]
LETRA           [a-zA-Z]
COMA            [","]
PUNTO           ["."]
GUION_BAJO      ["_"]
IGUAL           ["="]
IDENTIFICADOR   {LETRA}({LETRA}|{DIGITO}|{GUION_BAJO})*
ENTEROS_SIGNO   -?[1-9]{DIGITO}{0,4}
FLOTANTE        {DIGITO}{0,8}{PUNTO}{DIGITO}{1,8}
FLOTANTE_SIGNO  -?{DIGITO}{0,8}{PUNTO}{DIGITO}{1,8}
/*Caracteres de apertura y cierre*/
PARENTESIS_IZQ  ["("]
PARENTESIS_DER  [")"]
LLAVE_IZQ       ["{"]
LLAVE_DER       ["}"]
CORCHETE_IZQ    "["
CORCHETE_DER    "]"
FIN_SENTENCIA   [";"]
/*Palabras reservadas*/
BEGIN           "Begin"
SET             "set"
SUMA            "suma"
PRINT           "print"
END             "End"
/*SECCION DE REGLAS*/
%%
{FIN_SENTENCIA}         {printf("\nTOKEN RECONOCIDO FIN_SENTENCIA (%s)", yytext);}
{COMA}                  {printf("\nTOKEN RECONOCIDO COMA (%s)", yytext);}
{ENTEROS}               {printf("\nTOKEN RECONOCIDO ENTERO (%s)", yytext);}
{ENTEROS_SIGNO}         {printf("\nTOKEN RECONOCIDO ENTEROS_SIGNO (%s)", yytext);}
{FLOTANTE}              {printf("\nTOKEN RECONOCIDO FLOTANTE (%s)", yytext);}
{FLOTANTE_SIGNO}        {printf("\nTOKEN RECONOCIDO FLOTANTE_SIGNO (%s)", yytext);}
{IGUAL}                 {printf("\nTOKEN RECONOCIDO OPERADOR (%s)", yytext);}
{PARENTESIS_IZQ}        {printf("\nTOKEN RECONOCIDO PARENTESIS_IZQ (%s)", yytext);}
{PARENTESIS_DER}        {printf("\nTOKEN RECONOCIDO PARENTESIS_DER (%s)", yytext);}
{LLAVE_IZQ}             {printf("\nTOKEN RECONOCIDO LLAVE_IZQ (%s)", yytext);}
{LLAVE_DER}             {printf("\nTOKEN RECONOCIDO LLAVE_DER (%s)", yytext);}
{CORCHETE_IZQ}          {printf("\nTOKEN RECONOCIDO CORCHETE_IZQ (%s)", yytext);}
{CORCHETE_DER}          {printf("\nTOKEN RECONOCIDO CORCHETE_DER (%s)", yytext);}
{BEGIN}                 {printf("\nTOKEN RECONOCIDO BEGIN (%s)", yytext);}
{SET}                   {printf("\nTOKEN RECONOCIDO SET (%s)", yytext);}
{SUMA}                  {printf("\nTOKEN RECONOCIDO SUMA (%s)", yytext);}
{PRINT}                 {printf("\nTOKEN RECONOCIDO PRINT (%s)", yytext);}
{END}                   {printf("\nTOKEN RECONOCIDO END (%s)", yytext);}
{IDENTIFICADOR}         {printf("\nTOKEN RECONOCIDO IDENTIFICADOR (%s)", yytext);}
"\n"                    {linea++;}
"\t"                    {}
" "                     {}
.                       {printf("\nNo se reconoce el token: %s en la linea %d",yytext, linea);}
%%
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]);
        yylex();
    }
    printf("\nSe finalizo la lectura del archivo %s \n", argv[1]);
    fclose(yyin);
    return 0;
}
Ilustración 1-Analizador léxico completo
Sección de definiciones
En la primera sección mostrada en la Ilustración 2, la cual corresponde a las definiciones, se encuentra la biblioteca estándar de C seguidamente una constante que se usará como puntero para poder desplazarse por el archivo línea por línea, ya por último está un contador de líneas cuyo funcionamiento será mostrado más adelante.
%{
#include <stdio.h>
FILE *yyin;
int linea=1;
%}
Ilustración 2-Sección de definiciones
Sección de opciones
Seguidamente está la sección de opciones en donde se agregan funcionalidades a Flex mediante líneas de código un ejemplo de esto es la Ilustración 3 en la cual se encuentra un “option” este indica al analizador que solo se usará un archivo de entrada para el análisis léxico. Por consiguiente, en el segundo “option” permite obtener el número de línea en el que está situado el puntero al momento de leer el archivo (esta función es ajena al contador de línea previamente mostrado).
/*Seccion de opciones*/
/*Esta indica a Flex que lea sólo un fichero de entrada*/
%option noyywrap
/*Permite obtener el número de linea*/
%option yylineno
Ilustración 3-Sección de opciones
Sección de tokens
Identificador
En el mismo apartado del código se encuentra la sección de tokens (Ilustración 4), donde se especifican los componentes de cada función, identificador o número que se usara en el lenguaje propuesto. Para empezar esta sección están los tokens que sirven para componer identificadores y números con signo, el corchete que rodea cada componente indica que solo se pueden ingresar una vez ya que si se quiere más de un token individual se tiene que especificar en una composición de tokens para cumplir un propósito especifico. Una muestra de esto es el identificador el cual obliga que se empiece por una letra seguido de cero omás dígitos, guiones bajos u otras letras, esto es así pues los tokens después de la primera letra están condicionados por el “*” hace que se repitan cero o más veces.
Enteros
Después de los identificadores siguen los enteros sin signo, como se mencionó anteriormente para que acepte más de un carácter se hace una función compuesta, en este caso se agrega únicamente el símbolo “+” para aceptar más de un número.
Enteros con signo
El siguiente se trata de los números enteros con signo, la primicia de este tipo de dato son números negativos o positivos superiores a 1. Por lo tanto, la regla propone que puede venir un signo “-“ o no mediante el “?” seguidamente tiene que empezar con un número del 1 al 9 seguido de 0 a 4 números más indicado por los corchetes a su lado, los números a ingresar después del primero van del 0 al 9.
Flotantes
Los números flotantes consisten en dígitos del 0 al 9 con punto decimal por lo que la regla radica en cero u ocho dígitos los cuales se establecieron anteriormente (del 0 al 9), seguidamente se coloca un punto esto antes de que se ingrese uno u ocho dígitos, este límite se establece con los corchetes a la derecha de la etiqueta.
Flotantes con signo
Esta última regla compuesta es igual a la anterior, la única diferencia radica en el signo “-“ al inicio de la sentencia el cual como se dejó claro anteriormente funciona para que acepte uno o ningún símbolo.
/* SECCION DE TOKENS */
DIGITO          [0-9]
LETRA           [a-zA-Z]
COMA            [","]
PUNTO           ["."]
GUION_BAJO      ["_"]
IGUAL           ["="]
IDENTIFICADOR   {LETRA}({LETRA}|{DIGITO}|{GUION_BAJO})*
ENTEROS         {DIGITO}+
ENTEROS_SIGNO   -?[1-9]{DIGITO}{0,4}
FLOTANTE        {DIGITO}{0,8}{PUNTO}{DIGITO}{1,8}
FLOTANTE_SIGNO  -?{DIGITO}{0,8}{PUNTO}{DIGITO}{1,8}
Ilustración 4-Sección de tokens
Continuando se encuentran los tokens simples en la Ilustración 5 donde se agrupan los caracteres de apertura y cierre, aquí se localizan los delimitadores y el signo de puntuación que sirve para terminar una oración en el lenguaje propuesto. En esta sección se valida los paréntesis, las llaves, el punto y coma usado para terminar una oración y los corchetes, particularmente los corchetes se colocaron de manera diferente debido al tipo de símbolo pues genera confusión a Flex al colocarlo entre corchetes.
/*Caracteres de apertura y cierre*/
PARENTESIS_IZQ  ["("]
PARENTESIS_DER  [")"]
LLAVE_IZQ       ["{"]
LLAVE_DER       ["}"]
CORCHETE_IZQ    "["
CORCHETE_DER    "]"
FIN_SENTENCIA   [";"]
Ilustración 5-Caracteres de apertura y cierre
Para terminar la sección de definiciones y la subsección de tokens están las palabras reservadas mostradas en la Ilustración 6. Ya que el lenguaje es sensible a mayúsculas y minúsculas la forma de ingresar las frases será de esa única manera, esto quiere decir que, si se ingresan en mayúsculas o minúsculas completamente no serán aceptados, de la misma forma si falta alguna letra.
/*Palabras reservadas*/
BEGIN           "Begin"
SET             "set"
SUMA            "suma"
PRINT           "print"
END             "End"
Ilustración 6-Caracteres de apertura y cierre
Sección de reglas
A la mitad de la estructura del código Flex se encuentra la sección de reglas (Ilustración 7), en esta parte del código se establece el orden de entrada de cada token, así como la acción a realizar si se encuentra alguno. Como se mencionó se establece la jerarquía de ingreso, pues los símbolos simples tienen que ir al principio dejando a los tokens compuestos al final ya que el analizador léxico no puede saber si una entrada pertenece a un token simple o a uno compuesto. En cada línea se estipula que al momento de encontrar un token se muestre el mensaje de: “TOKEN RECONOCIDO” seguido del tipo y entre paréntesis el carácter exacto que encontró.
Casi al final en la instrucción que reconoce el salto de línea (“\n”) se está aumentando en uno el contador establecido al inicio del programa, puesto de esta manera pues si en esa línea de código no logro encontrar un token valido muestre la línea exacta junto con el carácter que está marcando el error.
/*SECCION DE REGLAS*/
%%
{FIN_SENTENCIA}         {printf("\nTOKEN RECONOCIDO FIN_SENTENCIA (%s)", yytext);}
{COMA}                  {printf("\nTOKEN RECONOCIDO COMA (%s)", yytext);}
{PUNTOYCOMA}            {printf("\nTOKEN RECONOCIDO PUNTOYCOMA (%s)", yytext);}
{ENTEROS}               {printf("\nTOKEN RECONOCIDO ENTERO (%s)", yytext);}
{ENTEROS_SIGNO}         {printf("\nTOKEN RECONOCIDO ENTEROS_SIGNO (%s)", yytext);}
{FLOTANTE}              {printf("\nTOKEN RECONOCIDO FLOTANTE (%s)", yytext);}
{FLOTANTE_SIGNO}        {printf("\nTOKEN RECONOCIDO FLOTANTE_SIGNO (%s)", yytext);}
{IGUAL}                 {printf("\nTOKEN RECONOCIDO OPERADOR (%s)", yytext);}
{PARENTESIS_IZQ}        {printf("\nTOKEN RECONOCIDO PARENTESIS_IZQ (%s)", yytext);}
{PARENTESIS_DER}        {printf("\nTOKEN RECONOCIDO PARENTESIS_DER (%s)", yytext);}
{LLAVE_IZQ}             {printf("\nTOKEN RECONOCIDO LLAVE_IZQ (%s)", yytext);}
{LLAVE_DER}             {printf("\nTOKEN RECONOCIDO LLAVE_DER (%s)", yytext);}
{CORCHETE_IZQ}          {printf("\nTOKEN RECONOCIDO CORCHETE_IZQ (%s)", yytext);}
{CORCHETE_DER}          {printf("\nTOKEN RECONOCIDO CORCHETE_DER (%s)", yytext);}
{BEGIN}                 {printf("\nTOKEN RECONOCIDO BEGIN (%s)", yytext);}
{SET}                   {printf("\nTOKEN RECONOCIDO SET (%s)", yytext);}
{SUMA}                  {printf("\nTOKEN RECONOCIDO SUMA (%s)", yytext);}
{PRINT}                 {printf("\nTOKEN RECONOCIDO PRINT (%s)", yytext);}
{END}                   {printf("\nTOKEN RECONOCIDO END (%s)", yytext);}
{IDENTIFICADOR}         {printf("\nTOKEN RECONOCIDO IDENTIFICADOR (%s)", yytext);}
"\n"                    {linea++;}
"\t"                    {}
" "                     {}
.                       {printf("\nNo se reconoce el token: %s en la linea %d",yytext, linea);}
Ilustración 7-Sección de reglas
Sección de rutinas del usuario
Esta es la última sección en el código, bien muestra la Ilustración 8 consiste en la sección donde el usuario puede agregar código directamente en C, en este caso la función main sirve para abrir y cerrar el archivo. 
%%
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]);
        yylex();
    }
    printf("\nSe finalizo la lectura del archivo %s \n", argv[1]);
    fclose(yyin);
    return 0;
}
Ilustración 8-Sección de rutinas de usuario
Pruebas
El primer requisito para probar el analizador léxico es escribir un programa en el lenguaje propuesto como en la Ilustración 9 donde se puede apreciar el ejemplo expuesto en clase.
Ilustración 9-Programa de ejemplo
En la terminal (Ilustración 10) se ingresa a la carpeta donde esté alojado el programa, una vez ahí se compila con Flex mediante el comando “flex nombre-del-archivo”, ya generado el fichero, se compila con C ese fichero con la orden “cc lex.yy.c -o nombre-del-ejecutable -lfl”. Una vez con el ejecutable en la carpeta deseada solo se ejecutará junto con el nombre del archivo donde se guardó el programa con el lenguaje propuesto.
Ilustración 10-Preparación de prueba
Al ejecutar el comando aparecerán los mensajes de “TOKEN RECONOCIDO” o los mensajes de error dependiendo el programa de prueba empleado, en este caso pudo reconocer todos y cada uno de los caracteres ingresados como se muestra en la Ilustración 11.
Ilustración 10-Preparación de prueba
	
Conclusión
En un analizador léxico “Su principal función consiste en leer los caracteres de entrada y elaborar como salida una secuencia de componentes léxicos que utiliza el analizador sintáctico para hacer el análisis. Esta interacción, suele aplicarse convirtiendo al analizador léxico en una subrutina o corrutina del analizador sintáctico. Recibida la orden "obtén el siguiente componente léxico" del analizador sintáctico, el analizador léxico leelos caracteres de entrada hasta que pueda identificar el siguiente componente léxico.” (Pérez Pérez & Monroy Cedillo , s.f.)
Un analizador léxico aparte de aplicarse al análisis del lenguaje también se aplican a lenguajes de consulta y sistemas de recuperación de información. La primera razón para emplear herramientas formales es que permiten expresar con precisión y, generalmente, de forma breve. Este se encarga de buscar los componentes léxicos o palabras que componen el programa fuente, según unas reglas o patrones su entrada se puede definir como una secuencia de caracteres. 
Otra ventaja de las herramientas formales es que permiten razonar sobre la corrección de los diseños y permiten conocer los límites de lo que se hacer. Por ejemplo, el lema de bombeo permite saber que no es posible utilizar expresiones regulares para modelar componentes léxicos que tengan el mismo número de paréntesis abiertos que cerrados, por lo que averiguar si algo está bien paren tizado será tarea del analizador sintáctico. Como ultima ventaja del empleo de lenguajes formales, se ilustraré la existencia de herramientas para automatizar la implementación. Por ejemplo, el paso de las expresiones regulares a un programa que las reconozca se puede hacer mediante un generador de analizadores léxicos como flex.
Referencias
Equipo editorial Etecé. (05 de 08 de 2021). Concepto.de. Obtenido de https://concepto.de/lexico/
Hernández, R. B. (09 de 05 de 2008). Introducción a Flex y Bison. Obtenido de http://webdiis.unizar.es/asignaturas/LGA/material_2004_2005/Intro_Flex_Bison.pdf
Pérez Pérez , I., & Monroy Cedillo , J. (s.f.). 2.1. Función del Analizador Léxico. Obtenido de http://cidecame.uaeh.edu.mx/lcc/mapa/PROYECTO/libro32/21_funcin_del_analizador_lxico.html

Otros materiales

Materiales relacionados

12 pag.
Prototipo sintactico - Daniel Cuellar

User badge image

Desafío México Veintitrés

4 pag.
Prototipo (1) - Daniel Cuellar

User badge image

Desafío México Veintitrés

12 pag.
Prototipo sintactico - Daniel Cuellar

User badge image

Desafío México Veintitrés