23
Actividad 2 Programación Tema: Conocer la importancia de un analizador léxico, sintáctico y semántico a partir de un generador de código.

Actividad 2 Analizador léxico, sintáctico y semántico

  • Upload
    maryr

  • View
    24.301

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Actividad 2 Analizador léxico, sintáctico y semántico

Actividad 2 Programación

Tema: Conocer la importancia de un analizador léxico, sintáctico y semántico a partir de un generador de código.

Page 2: Actividad 2 Analizador léxico, sintáctico y semántico

Análisis Léxico. El analizador léxico es la primera fase de un compilador, lee caracteres de entrada para formar componentes e identificarlos o clasificarlos y pasar la información de los componentes al analizador sintáctico. Realiza además funciones como eliminar espacios en blanco, saltos de línea, tabuladores, ignorar comentarios, detección y recuperación de errores. Los errores que un analizador léxico reconoce son símbolos no válidos o no reconocidos por el léxico del lenguaje o que no forman parte de ningún componente léxico.

Page 3: Actividad 2 Analizador léxico, sintáctico y semántico

También entre sus funciones están . Manejar el fichero fuente

Leer los caracteres de la entrada Generar una secuencia de componentes léxicos (TOKENS) Eliminar comentarios, delimitadores (espacios, símbolos de puntación, 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 Manejar macros Controlar si es de formato libre o no

Libre: PASCAL, ALGOL No libre: FORTRAN, BASIC

Page 4: Actividad 2 Analizador léxico, sintáctico y semántico

Análisis Sintáctico.

Gramática _ Permite definir un lenguaje mediante reglas que nos

permiten generar o producir cadenas de un lenguaje. _ Estas gramáticas son similares a las gramáticas de los

lenguajes naturales, pero mucho más restrictivas y sencillas.

_ Un ejemplo de regla de una gramática: Oración _ Sujeto predicado _ Estas reglas se suelen llamar reglas de reescritura: el

símbolo Oración se puede reescribir por el símbolo Sujeto seguido del símbolo Predicado.

Autómata: Al igual que con los lenguajes regulares podemos definir

un autómata como una máquina reconocedora de cadenas (palabras) de un determinado lenguaje.

Los autómatas con los que trabajaremos en este tema son algo más complejos que los AF

Page 5: Actividad 2 Analizador léxico, sintáctico y semántico

Análisis Semántico.

La fase de análisis semántico de un procesador de lenguaje es aquélla que computa la información adicional necesaria para el 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.

Sintaxis de un lenguaje de programación es el conjunto de reglas formales que especifican la estructura de los programas pertenecientes a dicho lenguaje. Semántica de un lenguaje de programación es el conjunto de reglas que especifican el significado de cualquier sentencia sintácticamente válida. Finalmente, el análisis semántico1 de un procesador de lenguaje es la fase encargada de detectar la validez semántica de las sentencias aceptadas por el analizador sintáctico.

Page 6: Actividad 2 Analizador léxico, sintáctico y semántico

2. Que es el análisis léxico en cuanto a:

            a) manejo de localidades temporales de memoria (buffers) La forma más fácil de leer un programa es carácter por carácter pero es

ineficiente. La forma más eficiente es realizar una copia a la memoria de todo el código

fuente. Pero esto en la gran mayoría de las ocasiones es impráctico por las dimensiones de los programas. Para solucionar este problema se sugiere utilizar buffers.

  Manejo de buffers: Existen muchas formas de dividir el trabajo, pero siempre se deberá llevar dos

punteros, uno al carácter actual y otro al inicial del lexema. El manejo de buffers es esencial para realizar el análisis de grandes programas

de mejor manera La diferente de velocidad entre los dos tipos de memoria es muy grande, por lo

que resulta interesante definir algún tipo de estrategia que reduzca este diferencial.

Un buffer se define como un conjunto de bytes que son leídos o escritos desde un dispositivo de almacenamiento, en la memoria primaria. Cuando se desea leer una información, se lee un bloque de información en el que aparece. La modificación de un dato se realiza sobre el buffer, que posteriormente debe ser enviado al dispositivo de almacenamiento. La utilización de esta técnica permite reducir el número de accesos a memoria secundaria.

Page 7: Actividad 2 Analizador léxico, sintáctico y semántico

Número de Buffers y Velocidad de Acceso: El manejo de buffers por parte del administrador de

ficheros permite reducir el número de accesos a memoria secundaria. Pero una cuestión fundamental es el número de buffers a utilizar. Si sólo se utiliza un buffer, un problema que realice lecturas y escrituras de modo alterno, debería leer un bloque en cada operación. - Esto se resuelve mediante la utilización de un buffer para escritura y otro para lectura. Pero la lectura, o escritura, alterna sobre varios ficheros puede provocar el mismo problema. Otra alternativa es la utilización de ambos bloques para lecturas y escrituras de modo alternado.

La generalización de esta idea es el caso real, varios buffers que se manejan de modo indistinto para lecturas y escrituras. La gestión de estos buffers es realizada por el administrador de ficheros, aunque el usuario puede controlar el número de buffers.

Si todos los buffers están ocupados, se debe vaciar uno de ellos para posibilitar una lectura. Normalmente se utiliza al algoritmo LRU, es decir, se vacía el buffer menos recientemente utilizado.

Page 8: Actividad 2 Analizador léxico, sintáctico y semántico

b) creación de tablas de símbolos

Tabla: conjunto de pares clave-valor, llamados elementos de la tabla.

La tabla de símbolos es una componente necesaria de un compilador. Al declarar un identificador (normalmente una sola vez), éste es insertado en la tabla. Cada vez que se utilice el identificador se realizará una búsqueda en la tabla para obtener la información asociada (el valor). Verifican que la semántica sea correcta y ayuda en la generación apropiada del código.

  Búsqueda: dada la clave de un elemento, encontrar su

valor. Inserción: Dado un par clave-valor, añadir un elemento

nuevo a la tabla. Cambio de valor: Buscar el elemento y cambiar su valor. Borrado: Eliminar un elemento de la tabla.

Page 9: Actividad 2 Analizador léxico, sintáctico y semántico

Longitud de búsqueda (o tiempo de acceso):   De una clave: Li = número de comparaciones con elementos de la tabla para

encontrar esa clave. Máxima: LM = número máximo de comparaciones para encontrar cualquier clave. Media (esperada): Lm = número medio de comparaciones para encontrar un valor. Si la frecuencia de todas las claves es la misma:

Lm = (S Li)/N Si la frecuencia de todas las claves no es la misma: Lm = S pi.Li Grado de ocupación: s = n/N donde n=número de elementos en la tabla y N=capacidad máxima de la tabla.

Función de búsqueda: B : K→E asocia a cada clave k un elemento B(k). Valor asociado a una clave k: v(B(k)). Puede ser múltiple, en cuyo caso normalmente

se convierte en un puntero. Si está en la tabla puede almacenarse consecutivamente o en subtablas paralelas.

Tablas de símbolos (identificadores) La clave es el identificador. El valor está formado por:

Atributos del identificador. Puntero a la posición de memoria asignada. La clave puede sustituirse por un puntero.

Los identificadores pueden estar empaquetados. La longitud del identificador puede especificarse en la tabla o delante del nombre, o

ser implícita.  Tablas consecutivas: Todos los elementos ocupan posiciones de memoria

adyacentes. Tablas ligadas: cada elemento apunta al siguiente. Tablas doblemente ligadas: cada elemento apunta al siguiente y al anterior. Tablas no ordenadas Inserción: en el primer lugar vacío.

 

Page 10: Actividad 2 Analizador léxico, sintáctico y semántico

c) Manejo de errores léxicos El compilador tiene que reportar clara y exactamente la presencia de

errores recuperarse de cada error lo suficientemente rápido para poder detectar errores subsecuentes, debe tratar de evitar mensajes falsos de error, Un error produce un token erróneo.

Un token o componente léxico es una cadena de caracteres que tiene un significado coherente en cierto lenguaje de programación.

  Recuperación en modo pánico: este tipo de estrategia es la más común.

Consiste en que cuando se detecta una cadena no reconocible, se siguen leyendo caracteres hasta que se vuelve a detectar un token válido. Borrar un carácter extraño. Insertar un carácter que falta (e.g. reemplazar 2C por 2*C). Reemplazar un carácter incorrecto por otro correcto (e.g. reemplazar INTEJER por INTEGER si el lugar en donde aparece el primer lexema no es el indicado para un identificador) Intercambiar dos caracteres, ó tokens, adyacentes (e.g. I INTEGER por INTEGER I).

La recuperación de errores durante el AL puede producir otros en las siguientes fases. var numero : integer; begin num?ero:=10; end el compilador podría producir los siguientes mensajes de error: ERROR LÉXICO: carácter no reconocido (?) ERROR SEMÁNTICO: identificador no declarado (num) ERROR SINTÁCTICO: falta operador entre identificadores ERROR SEMÁNTICO: identificador no declarado (ero)

Otras veces no: var i,j: integer; begin i:=1; ? j:=2; end

Page 11: Actividad 2 Analizador léxico, sintáctico y semántico

 3. Que es el análisis sintáctico en cuanto a:

a) Diagramas de sintaxis.

Es una forma que los árboles de derivación, su principal característica es que permite ver ls derivaciones al instante de que ocurren

Ejemplo 1:Dado el siguiente ejemplo de código en C: superficie = base * altura / 2; La sintaxis del lenguaje C indica que las expresiones se

pueden formar con un conjunto de operadores y un conjunto de elementos básicos. Entre los operadores, con sintaxis binaria infija, se encuentran la asignación, el producto y la división. Entre los elementos básicos de una expresión existen los identificadores y las constantes enteras sin signo (entre otros).

Su semántica identifica que en el registro asociado al identificador superficie se le va a asociar el valor resultante del producto de los valores asociados a base y altura, divididos por dos (la superficie de un triángulo).

Page 12: Actividad 2 Analizador léxico, sintáctico y semántico

b) Precedencia de operadores.

La precedencia de operadores es de vital importancia en el proceso de análisis sintáctico ya que nos representará la forma en que debe construirse el árbol de derivación.

En aritmética existen prioridades, por ejemplo: * y / tienen preferencia sobre + y -. () indican la máxima prioridad.

Prioridad de operadores • La instrucción a = b + c / 2 en la mayoría de los

lenguajes no se evalúa de la forma a = (b + c) /2, sino de la forma a = b + (c/2)

• La forma de evaluación depende de cómo se construyan los operadores, ya sea en infijo, postfijo o prefijo.

• Las operaciones se realizan de abajo hacia arriba.

Page 13: Actividad 2 Analizador léxico, sintáctico y semántico

Analizador sintáctico: analizador descendente (LL), analizador ascendente (LR, LALR). Un analizador sintáctico ( Parser ) es un programa

que reconoce si una o varias cadenas de caracteres forman parte de un determinado lenguaje. Los lenguajes habitualmente reconocidos por los analizadores sintácticos son los lenguajes libres de contexto. Cabe notar que existe una justificación formal que establece que los lenguajes libres de contexto son aquellos reconocibles por un autómata de pila, de modo que todo analizador sintáctico que reconozca un lenguaje libre de contexto es equivalente en capacidad computacional a un autómata de pila. 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áticos a partir de una especificación de la sintaxis del lenguaje, tales y como yacc, GNU bison y javacc.

Page 14: Actividad 2 Analizador léxico, sintáctico y semántico

Analizador Descendente Ll Análisis Sintáctico Predictivo Recursivo La siguiente fase en la construcción del analizador

es la fase de análisis sintáctico. Esta toma como entrada el flujo de terminales y construye como salida el árbol de análisis sintáctico abstracto.

El árbol de análisis sintáctico abstracto es una representación compactada del árbol de análisis sintáctico concreto que contiene la misma información que éste.

Existen diferentes métodos de análisis sintáctico. La mayoría caen en una de dos categorías: ascendentes y descendentes. Los ascendentes construyen el árbol desde las hojas hacia la raíz. Los descendentes lo hacen en modo inverso. El que usaremos aqui es uno de los más sencillos: se denomina método de análisis predictivo descendente recursivo.

Page 15: Actividad 2 Analizador léxico, sintáctico y semántico

Analizador Ascendente Lr Lalr

Analizador Ascendente LR Intenta construir un árbol de análisis

sintáctico, empezando desde la raíz y descendiendo hacia las hojas. Lo que es lo mismo que intentar obtener una derivación por la izquierda para una cadena de entrada, comenzando desde la raíz y creando los nodos del árbol en orden previo.

LL (left to left) leen la cadena de izquierda a derecha y derivan por la izquierda

Page 16: Actividad 2 Analizador léxico, sintáctico y semántico

 Administración de tablas de símbolos. Ésta poseerá una entrada por cada identificador declarado en

el contexto que se esté analizando. Con este tipo de estructuras de datos adicionales, los desarrolladores de compiladores acostumbran a suplir las carencias de las gramáticas libres de contexto.

La tabla de símbolos registra información acerca de cada nombre de símbolo en un programa. Históricamente, los nombres se llamaron símbolos, más que de una tabla de nombres. En este capítulo, la palabra símbolo significa nombre. La fase de análisis semántico crea la tabla de símbolos, puesto que no es sino hasta este análisis que se tiene la suficiente información sobre un nombre para describirlo. La generación de código usa la tabla de símbolos para extraer las directivas del ensamblador, para el tipo y para el tamaño apropiados.”

Una tabla de símbolos es una estructura de datos que contiene un registro por cada identificador. El registro incluye los campos para los atributos del identificador.

El administrador de la tabla de símbolos se encarga de manejar los accesos a la tabla de símbolos, en cada una de las etapas de compilación de un programa.

Page 17: Actividad 2 Analizador léxico, sintáctico y semántico

Análisis sintáctico. Como se muestra en la Figura 1, la entrada del analizador semántico es la salida generada por el analizador sintáctico. La estructura empleada para intercambiar la información entre estas dos fases es lo que se conoce como árbol sintáctico –o una simplificación del mismo, denominada árbol sintáctico abstracto (§ 2.2). Una vez validada la sintaxis de un programa, el análisis semántico aplicará reglas semánticas para validar dicho árbol.

e) Manejador de errores. Si la validación del árbol sintáctico descrita en el párrafo anterior no fuese satisfactoria, es decir, existiese un error semántico, la fase de análisis semántico debería notificar dicho error al manejador de errores para que éste se encargase de su gestión. El proceso de análisis podría seguir ejecutándose o no, en función de si el procesador de lenguaje implementa algún mecanismo de recuperación de errores.

− Generación de código (intermedio). La salida del análisis semántico se suele emplear como entrada para la generación de código12. La estructura de datos empleada para intercambiar información entre las dos fases mencionadas es un árbol sintáctico decorado (§ 2.2). Este árbol posee información adicional al árbol generado por el analizador sintáctico, como por ejemplo la información relativa al tipo de cada una de las expresiones del programa. El empleo de dicha información es útil para llevar a cabo el proceso de generación de código (a bajo nivel, el tipo de una expresión es necesario, por ejemplo, para saber el número de bytes que ocupa su valor).

− Tabla de símbolos. Como hemos mencionado previamente, la utilización de gramáticas libres de contexto (de tipo 2) no permite expresar características representables con gramáticas sensibles al contexto –como la necesidad de que la utilización de una variable en el lenguaje Pascal requiera la declaración previa de la variable utilizada. Para poder implementar un procesador del lenguaje Pascal empleando gramáticas de tipo 2 e implementaciones de autómatas de pila, es necesario emplear una estructura de datos auxiliar denominada tabla de símbolos. Esta estructura de datos, a su nivel más básico, es un diccionario (memoria asociativa) que asocia identificadores a la información requerida por el compilador. Sus dos operaciones básicas son insertar y buscar. En nuestro ejemplo, la declaración de un identificador en Pascal requerirá una inserción del mismo en la tabla de símbolos; cada vez que se utilice un identificador en una sentencia, el analizador semántico buscará éste en la tabla de símbolos (llamando al manejador de errores si no existiere).

Page 18: Actividad 2 Analizador léxico, sintáctico y semántico

4. Que es el análisis semántico en cuanto a: Se compone de un conjunto de rutinas independientes,

llamadas por los analizadores morfológico y sintáctico. El análisis semántico utiliza como entrada el árbol

sintáctico detectado por el análisis sintáctico para comprobar restricciones de tipo y otras limitaciones semánticas y preparar la generación de código.

En compiladores de un solo paso, las llamadas a las rutinas semánticas se realizan directamente desde el analizador sintáctico y son dichas rutinas las que llaman al generador de código. El instrumento más utilizado para conseguirlo es la gramática de atributos.

En compiladores de dos o más pasos, el análisis semántico se realiza independientemente de la generación de código, pasándose información a través de un archivo intermedio, que normalmente contiene información sobre el árbol sintáctico en forma linealizada (para facilitar su manejo y hacer posible su almacenamiento en memoria auxiliar).

Page 19: Actividad 2 Analizador léxico, sintáctico y semántico

En cualquier caso, las rutinas semánticas suelen hacer uso de una pila (la pila semántica) que contiene la información semántica asociada a los operandos (y a veces a los operadores) en forma de registros semánticos.

            a) Verificación de tipos en expresiones.             b) Conversión de tipos. Acciones agregadas en un analizador sintáctico descendente

(top-down). Un analizador sintáctico (Parser) es un programa que reconoce

si una o varias cadenas de caracteres forman parte de un determinado lenguaje. Los lenguajes habitualmente reconocidos por los analizadores sintácticos s

on los lenguajes libres de contexto. Los analizadores pueden clasificarse dependiendo de la forma

en como se construyen los nodos del árbol de derivación sintáctico: ascendentes y descendentes.

LL (left to left) leen la cadena de izquierda a derecha y derivan por la izquierda

• LR (left to right) • SàaA • AàaBbC • Bàb • Càc

Page 20: Actividad 2 Analizador léxico, sintáctico y semántico

Pila semántica en un analizador sintáctico ascendente (bottom-up).

Los ascendentes construyen el árbol desde las hojas hacia la raíz.

Los descendentes lo hacen en modo inverso. • Un analizador ampliamente utilizado se denomina método

de análisis predictivo descendente recursivo que es muy sencillo.

Derivación izquierda: • SàAaàaaBbCàaabbCàaabbc (1234) • SàaAàaaBbCàaaBbcàaabbc (3421) • LL(k) traductores “top-down” • Un análisis anticipado de k caracteres .SàaS|cA • AàbA|cB|vacia • BàcB|a| vacia Construir cadena acbb • SàaS o SàcA; al anticipar el primer símbolo

Page 21: Actividad 2 Analizador léxico, sintáctico y semántico

Administración de la tabla de símbolos. La tabla de símbolos se crea durante la fase de análisis

léxico a través de los componentes léxicos, pero en el proceso de análisis sintáctico sufren algunas modificaciones.

• Generalmente se agregan valores de tipo y significado para el análisis sintáctico

Es una estructura de datos que usa el proceso de traducción de un lenguaje de programación, por un compilador o un intérprete, donde cada símbolo en el código fuente de un programa está asociado con información tal como la ubicación, el tipo de datos, y el ámbito de cada variable, constante o procedimiento.

Los símbolos en la tabla de símbolos pueden referirse a constantes, a funciones o a tipos de datos en el código fuente de un programa.

El administrador de la tabla de símbolos se encarga de manejar los accesos a l tabla de símbolos, en cada una de las etapas de compilación de un programa.

Page 22: Actividad 2 Analizador léxico, sintáctico y semántico

Manejo de errores semánticos Los errores semánticos son más sútiles. Un error semántico se produce cuando la sintaxis del código es correcta pero la semántica o significado no es le que se pretendía. La construcción obedece las reglas del lenguaje y por ellos el compilado o interprete no detectan los errores semánticos, los copiladores o interpretes sólo se ocupan de la estructura del código que se escribe, y no de su significado, Un error semántic puede hacer que el programa termine de forma anormal, cn o sin un mensaje de error.

  Si los traductores tuvieran que procesar programas correctas el

proceso de implantación se simplificaría mucho. • ¿Cómo debe de responder un compilador de pascal a un código

Fortran? • Ningún método de recuperación de errores resuelve todos los

problemas Tipos de errores • Léxicos: como escribir mal un identificador, palabra clave u

operador. • Sintácticos: como una expresión aritmética con paréntesis no

equilibrados. • Semánticos: como un operador aplicado a un operadorando

incompatible. • Lógicos: como una llamada infinitamente recursiva • La mayoría de los errores se centra en la fase de análisis sintáctico.

Page 23: Actividad 2 Analizador léxico, sintáctico y semántico

El manejador de errores debe: • Informar la presencia de errores con claridad y exactitud., recuperar de cada error con la

suficiente rapidez como para detectar errores posibles., No debe retrasar de manera significativa el procesamiento de programas correctos., Debe indicar la línea del error y algún mensaje informativo, estrategias de recuperación de errores, modo Pánico, nivel de Frase, Producciones de error, Corrección global, recuperación en modo pánico, es el más sencillo de implantar.

• El analizador sintáctico desecha componentes léxicos hasta encontrar un carácter de sincronización. Estos caracteres son el punto y como (;) entre otros.

Recuperación en modo pánico int a.b,c; struct c { …. } main() { int a; }

Recuperación a nivel de frase • Esta técnica utiliza una corrección de caracteres adyacentes, ya sea por inserción, eliminación o

intercambio. • Esta técnica permite sustituir , por ;, etc. Son traductores que corrigen errores.

Desafortunadamente para muchos casos no aplican por lo que no se utilizan demasiados. Producciones de error • Se pueden generar gramáticas para generar producciones de error y así de esta forma seguir

con el proceso. • La dificultad radica en el sentido de encontrar esas reglas gramaticales para generar error. En

algunos casos sería inclusiva más extensa que la gramática del propio lenguaje. • Corrección global • Idealmente, sería recomendable que un traductor hiciera el mínimo de cambios para procesar

una entrada inválida. Este algoritmo genera menores costos globales para realizar cambios. • El problema radica en que el implementar estas estrategias son muy costosas en tiempo y

espacio.