Sunday, August 27, 2017

Proyecto Analisis Semantico para Java (Flex-Bison)




Proyecto de Autómatas y Compiladores





Introducción:
El programa a presentar tiene el conjunto de diferentes tipos de análisis visto en todo el curso de Autómatas y Compiladores; para esto se debe tener en cuenta ciertos conocimientos sobre lo que es un compilador que sera el encargado de traducir el lenguaje que ingresemos, cada tipo de análisis que se hace Código fuente que el compilador interpreta gracias maquina virtual y la maquina destino que cada una posee entre otros conceptos que irán acompañados con un ejemplo en C ++ el cual es trabajado con, Flex y Bison, que son dos herramientas para el análisis léxico y sintáctico respectivamente. 

Compilador:

La memoria posee un programa que se encarga en traducir el Código Fuente de un lenguaje respectivo que se ingrese y transformarlo a un Código Objeto. Puede ser código binario o código maquina, para esto se traduce cada instrucción que se aya ingresado y después enviarla a la memoria RAM para almacenarla en forma de una secuencia.
Habido ejecutado el compilador se puede repetir el proceso cuantas veces se desea aunque en caso se presente un problema se tendrá que corregir el código fuente y volverse a compilar.


Modelos de la Compilación:

Existen dos partes dentro de la compilación:


  • Análisis:
Esta se encarga en dividir al programa fuente en sus elementos que lo componen y crea un representa intermedia del programa fuente que permite al compilador tener acceso a diferentes lenguajes fuentes, esta conformado por el análisis Léxico, Sintáctico y Semántico ademas que con esta se podrá conocer las operaciones que en lenguaje fuente va a realizar y armar el árbol Sintáctico ( un tipo de estructura jerárquica).   


  • Síntesis:
Esta parte muestra las salidos de nuestro código que se ingreso como lenguaje objeto, para esta parte se necesita mas técnicas para las fases de generación de código intermedio, generador de código objeto y optimizan de código por temas de direccionamiento de memoria entre otros.

Representación de etapas del compilador y sub-partes

Análisis Léxico o Lineal (Scanner): 

Es la primera fase del compilador donde descompone y lee carácter por carácter para después agruparlos de forma que tengan un significado y sigan una secuencias especifica para los componentes léxicos ( tokens ), poder identificarlos y clasificaros para poder pasarlos a la siguiente fase que es el analizador sintáctico; en caso que identifique un error se daría por un símbolo que no se encuentre en el lenguaje o en un componente léxico.

Funciones del A. Léxico:
  • Leer carácter por carácter de la cadena de entrada
  • Manejar el archivo fuente (abrirlo, leer sus caracteres y cerrarlo)
  • Eliminar comentarios y espacios en blanco (espacios, tabuladores y fin de línea)
  • Relacionar los mensajes de error con las líneas del programa fuente
  • Introducir los identificadores en la tabla de símbolos
  • Reconocer los identificadores, palabras clave, constantes 
  • Contabilizar el número de líneas y columnas para emitir mensajes de error


Análisis Sintáctico o Jerárquico (Parser):

Esta parte lleva el control de los procesos y las sub-rutinas de los elementos restantes del compilador.
Los caracteres o componentes léxicos o tokens recibidos por A. Léxico se agrupan en frases gramaticales de forma que tomen un significado para ser interpretados y generar un árbol sintáctico.
Con esto podemos garantizar que solo sentencias que pertenezcan al lenguaje fuente serán procesadas ademas que mostraría los errores en caso que hubiesen.


Función del A. Sintáctico:

  • Recopilar información de distintos componentes léxicos o tokens y reunirlos en una tabla de símbolos para un aproxima utilización en el generador de código.
  • Encontrar un árbol sintáctico o derivación que contenga la cadena analizada ademas que esta debe estar dentro del axioma de la gramática y una sucesión de ordenadas de símbolos.

Manejo de Errores:

Los compiladores presentan diferentes tipos de manejo de errores como léxico, sintáctico, semántico o lógico por lo cual en este caso sintáctico se considera el mas complicado desde la creación del compilador. Gracias a esto para tener un manejado de errores sintáctico se tiene 3 puntos específicos: 


  • Indicar los errores de forma clara y precisa 
  • Aclarar el tipo de error y su localización
  • Recuperarse del error para seguir examinando la entrada
  • No ralentizar la compilación
Para tener un buen manejo de los errores es preciso saber el tipo de gramática que se acepta un analizador sintáctico debido que ayuda a localizar errores de mayor dificultad como la ambigüedad.
Esta gramática depende de la Jerarquía de Chomsky que consta de diferentes tipos de gramática y la propone como una cuádrupla G(N, T, P, S) ademas de incluir a las derivaciones las cuales se encargaran en representar a los arboles sintácticos.

N=No Terminales
T=Terminales
P=Reglas de producción
S=Axioma Inicial

Arboles Sintácticos:

Es la representación gráfica de las derivaciones para llegar a una respuesta especifica


Un árbol sintáctico es una representación compacta del árbol de análisis sintáctico en el que los operadores aparecen como nodos interiores y los operando de un operador son los hijos del nodo para ese operador.


Analizador Semántico:

La semántica esta relacionado a estructuras formales lenguaje acompañadas de gramáticas limitadas de tipo LR y LL pero aun a pesar de todo esto no se puede definir todos los elementos sintácticos del lenguaje por lo cual es necesario el analizador semántico.
La fase de análisis semántico de un procesador de lenguaje donde la información de procesamiento de un lenguaje, una vez que la estructura sintáctica de un programa haya sido obtenida. Es por tanto la fase posterior a la de análisis sintáctico y la última dentro del proceso de síntesis de un lenguaje de programación.

Se encargar en dar un significado coherente, la validez, el cumplimiento de restricciones  y la compatibilidad de las expresiones recibidas por el analizador sintáctico. Ademas que va acompañada de una jerarquía sintáctica para la identificación de operadores y operando.

Reglas Semánticas:

Una regla semántica dependerá de los símbolos que componga las reglas por lo cual cada evaluación de estos puede generar código, actualizar la tabla de símbolos, mensaje de error entre otras actividades. El análisis de los símbolos que comprenden las reglas semánticas permitirán definir los nodos de los arboles sintácticos.


Clasificación del A. Semántico:
  • Dinámica:
                   Aspectos dentro de la semántica que se puede conocer en la ejecución  
                       
  • Estática:
                   Aspectos dentro de la semántica que se manejan en el tiempo de compilación



Funciones del Analizador Semántico:

  • Identificar cada tipo de instrucción y sus componentes
  •  Completar la Tabla de Símbolos
  • Realizar distintas comprobaciones y validaciones:
                       · C. de tipos
                         . C. de flujo de control
                         . C. de unidad
                         . C. de emparejamiento


Manejo de errores:

El analizador semántico ya tiene la función de buscar los posibles errores de tipo semántico por esto un error de estos necesita información sensitiva al contexto para ser identificado; tenemos estos:
  • No coinciden los tipos
  • Variable no fue declarada
  • Identificador reservado se use de forma indebida
  • Declaración de variables múltiples en un ámbito
  • Acceder a una variable fuera de alcance
  • Parámetro formal y real no coincide




Implementación del Código:

Para empezar el trabajo de simular un compilador en si es un trabajo extenso, por lo cual en este código estará la parte de análisis del compilador.
Ademas que como ya lo había dicho antes se trabajara en C++, el hecho es que como nuestro trabajo se pidió en Flex y Bison los cuales son herramientas generadores de analizadores los cuales son perfectos desarrolladores de código GNU bajo licencias GPL.

Flex:

Este recorre la entrada hasta que llegue a concordar con el patrón de entrada y en caso se no concuerde ningún patrón lo botara igual como se encontró. Al leer los ficheros de entrada a menos que no se le haya indicado, este mostrara una descripción del escanee a generar.
Esta descripción es un par compuesto de las expresiones regulares y el código en C ++ denominas reglas.
Posee también la peculiaridad de formar un fichero tipo yy.lex.c la cual definirá una función 'yylex()' por lo cual en el momento de ejecutar dicho fichero se enlaza automáticamente con la librería del Flex para ser ejecutado y el programa corre o arranca analizando las entradas propiamente de un analizador léxico una por una.  

También se debe considerar que la entrada de Flex se divide en tres partes separadas por un '%%':

definiciones
%%
reglas
%%
código de usuario

  • Definiciones:
      Contiene definiciones de nombres y condiciones de arranque
  • Reglas:
   Contiene a los patrones sin ningún tipo de símbolo ademas de empezar el arranque. Estos son escritos gracias al conjunto de caracteres ASCII disponibles exceptuando símbolos como el espacio en blanco, tabulador, salto de linea y caracteres especiales.



Bison:

Su propósito es convertir la descripción ya antes explicada por un analizador Léxico para una gramática independiente al contexto. Igualmente al anterior posee varios contextos en el desarrollo libre y compatibilidad con el lenguaje C o C++ ademas que al momento que describe bien una gramática indica si el fichero pertenece o no al lenguaje generador.

La forma general de Bison no es tan diferente a Flex excepto por la zona de Reglas gramaticales que es un aditivo del mismo analizador Sintáctico:
%{
declaraciones en C
%}
Declaraciones de Bison
%%
Reglas gramaticales
%%
Código C adicional



  • Declaraciones:
                      En Bison son necesario declarar los símbolos no terminales y terminales, también mostrar de donde provienen cierto tipos de operadores y tipos de valores.
  • Reglas:
                      Estas son producciones de la gramática asociadas a acciones ( dependiendo del lenguaje que se trabaje C o C++ ) que se ejecuta cuando las reglas concuerdan.

En el caso de los hablados Símbolos no terminales y terminales en Bison tokens se deben declarar en la zona de declaraciones y dependiendo que sea deben ir en mayúsculas o minúsculas. 
A pesar de esto existen 3 formas de declarar símbolos terminales en la gramática:

  • Token declarados: Igual como se declararía un identificador en C se una un % adelante de los tokens.
  •  Token de carácter: Se usa para los constantes de un solo carácter simplemente usando comillas ' ' encerrando al toquen correspondiente.
  • Token de asociatividad: Dependiendo que tan asociados estén a un operador de usa los %left o los %right.

Semántica del lenguaje:

Cuando se asocian varios valores a los tokens y agrupaciones estos recién reciben un valor semántico, por lo cual mediante se crean estas son reconocidas y vueltas a utilizar para el mismo tipo de valores por lo que se necesitara diferentes tipo de datos para diferentes tokens y agrupaciones. 

Una Acción siempre acompaña a una regla sintáctica y contiene código fuente que sera ejecutado por cada regla reconocida; con esto sabemos que una acción es un conjunto de sentencias las cuales tiene la particularidad de ejecutarse dependiendo la posición donde se hayan ubicado en el código.






Resultado de imagen para javaAcabado las definiciones y el uso como desarrollo el código para estas dos herramientas se empieza la explicación del programa.
Nuestro que nuestro compilador analizara una o conjunto de sentencias para el Lenguaje Java con el que cuenta con una gran cantidad de Operadores matemáticos, lógicos, etc; con una cantidad aproximada de 32 ademas de un conjunto aproximado de 50 palabras reservadas las cuales por un uso practico solo se usaron las necesarias.

Como ya antes explicado en como usar el Flex y Bison se hizo la creación de los ficheros tipo yy.lex.c y el yy.syntax accediendo a las respectivas librerías de los mismo. 


Recordando lo dicho se paso a reconocer e implementar en el código los símbolos y los tokens necesarios para que el programa para ubicarlos en los respectivos archivos .

Para el Flex:




















































Seguido a esto apuntábamos al archivo que contendría el ejemplo y las condiciones necesarias para que el fichero de entrada yyin analice los tokens que el archivo presentara. 


Para el Bison:
 Se empezó a declarar los tokens necesarios para que a base de gramáticas comprobamos si los datos ingresados tenían una jerarquía preestablecida y enviarla a la tabla de símbolos correspondiente.

























Después de esto se estableció las reglas sintácticas que cada token debía seguir para las producciones de las gramáticas para el lenguaje Java; estas consistían en condicionales, bucles, sentencias de todo tipo, declaración de variables y clases, etc.
Se tomo todas las posibles para el lenguaje respetando las estructura que el Bison daba.






















Ejemplo de Codigo en Java a probar





Integrantes:



  • ARANDA CARRANZA, JEAN PIERO
  • RUIZ OBANDO, RONALD ALEXANDER
  • SUCRE GAMBOA, CARLOS ANDRES 








































No comments:

Post a Comment