You are on page 1of 4

ANÁLISIS LÉXICO UNIDAD III 3.1.

- INTRODUCCIÓN A LOS AUTÓMATAS FINITOS Y EXPRESIONES REGULARES  INTRODUCCIÓN Al iniciarse el proceso de compilación, el código fuente de un programa no es más que un flujo de caracteres, así que la tarea del análisis léxico es reconocer símbolos en este flujo de caracteres y presentarlos en una representación más útil para el análisis sintáctico. Este proceso se ilustra en la siguiente figura.  Si éstas conforman el lexema mínimo, la expresión regular puede ser {/} {*} {*} {/}. Ahora bien, entre Ay B puede venir o no C. La representación de C es:

El análisis léxico es efectuado por una parte del compilador llamada analizador léxico. Entre las funciones principales que realiza un analizador léxico se pueden mencionar las siguientes:  Eliminar espacios en blanco  Eliminar comentarios  Reconocer identificadores  Reconocer palabras clave  Reconocer constantes y numerales  Generar un listado para el compilador. EXPRESIONES ARITMÉTICAS Y EXPRESIONES REGULARES  Una expresión aritmética se representa con un conjunto de secuencias de símbolos válidos que se construyen en base al alfabeto de un lenguaje. Generalmente estos conjuntos se representan como:  Ф; { }  Una expresión aritmética también es una expresión regular que denota el conjunto vacío (el conjunto no contiene secuencias de símbolos). Ó Ɛ Una expresión regular que denota al conjunto que contiene la secuencia vacía, es decir, una cadena que no contiene símbolos, pero que se debe representar dentro de un alfabeto y al mismo tiempo también debe de estar representada dentro de una expresión aritmética dentro de cualquier lenguaje de programación es representada de la siguiente manera: S Una secuencia de símbolos S es una expresión regular que denota a un conjunto de símbolos que contiene a S, estos símbolos deben estar contemplados dentro del alfabeto. Operaciones sobre Lenguajes  Construir una expresión regular es realizar operaciones sobre el alfabeto de un lenguaje. Podemos decir que el lenguaje, en su aspecto de léxico, está conformado por las operaciones que realizamos sobre su alfabeto para definir cada unidad de léxico. Las operaciones posibles son:  Unión de L y M  LUM = { S | S está en L ó S está en M}  Concatenación de L y M.  LM= { st | s está en L y t está en M}  Cerradura Kleene.         i=0 ( L se repite de 0 a infinito) Cerradura positiva.

Si incluimos esto en lo anterior, la expresión resultante será: {/}{*}{no*}+{*}{*}{/}. En B, el estado 4 tiene dos opciones: la parte representada por D y la parte representada por E. Cada una de ellas tiene representaciones como expresiones regulares.

Ambas expresiones tienen en común que terminan con asterisco, que es la situación con la cual se llega al estado 4 (de hecho el estado 4 está cubierto por E). Si unimos las dos opciones tendremos:  {*}* | {no*/}{no*}*{*}, que puede simplificarse como:  ({*} | {no*/}{no*}*{*})* Ya que la elección puede realizarse cero o más veces. Al incluir las expresiones regulares anteriores a ésta última, se obtiene:  {/}{*}({no*} + {*} | {*}) ({*} | {no*/}{no*}*{*}) * {/} que puede ser una representación válida para el patrón del comentario.  Una expresión regular es un método formal para describir un patrón y puede ser empleada para construir un analizador de léxico que lo reconozca en una computadora.  El proceso que se sigue es el inverso al planteado en el apartado anterior donde se partía de un diagrama de transición para llegar a la expresión regular: la expresión se convierte a un diagrama de transición y éste se puede representar como una tabla que conduzca el análisis de léxico.  De hecho, la expresión sufre una serie de transformaciones para llegar a ser explotada como tabla:

i=1 ( L se repite de 1 a infinito) Como se ve, las expresiones regulares son operaciones que se realizan sobre conjuntos de símbolos que pertenecen al alfabeto de un lenguaje. Estos conjuntos de símbolos se describen colocando a los símbolos que pertenecen a ellos entre llaves. Por ejemplo, los caracteres que ubicamos como letras podrían conformar el conjunto {ABCDEFGHIJKLMNOPQRSTUVWXYZ, abcdefghijklmnopqrstuvwxyz}  Si los elementos del conjunto tienen un orden conocido o asociado al código ASCII se puede describir colocando el primer símbolo y el último separados por un guión (-) como por ejemplo:  {A-Z, a-z} ; {0-9; 2,4,6,8}  Para hacer más simple la construcción de una expresión regular, se designa bajo un nombre al conjunto con el cual se trabajará, así que el conjunto letra se puede describir como:  L = {A-Za-z}  D= {0, 1, 2, 3, …, 9}  Cuando a un conjunto o a una expresión regular se le da nombre, ésta recibe el nombre de definición regular. Así, la expresión regular par aun identificador será:  ID= L ( L | D | G ) * Si las definiciones sobre las cuales está construida son:  L = {A-Z, a-z}  D = {0123456789}  G = {_} Por ejemplo, en Ada los identificadores están formados por letras, dígitos o guiones, pero el guión no puede ser el último carácter. La expresión regular con la cual se puede conformar el patrón para esta unidad de léxico puede ser:  ID_ada = L (L | D ) * (G (L | D ) + ) * Transición del Diagrama a la Expresión Regular  Lo que se ha presentado es la notación que permite una expresión regular pero ¿Cómo pasamos de un diagrama de transición a una expresión regular? Dentro de un diagrama de transición podemos reconocer ciertas situaciones:  Secuencias de caracteres (concatenación).  Elegir entre dos o más caminos (selección).  Repeticiones de elementos (cero o una vez, cerradura Kleene, cerradura positiva). Generalmente el reconocimiento de tales situaciones pueden ayudarnos en la construcción de la expresión regular equivalente. Por ejemplo: consideremos el patrón de los comentarios en C, donde se espera encontrar cualquier secuencia de caracteres (incluso vacía) entre el par /* */. Su diagrama

AUTÓMATAS FINITOS Un autómata finito es un conjunto de nodos y aristas que representan trayectorias para generar una expresión bajo alfabeto. Un diagrama de transición es un autómata finito. Los autómatas finitos se clasifican en:  Autómatas Finitos No Determinísticos. NFA  Autómatas Finitos Determinísticos. DFA.

Autómata Finito No Determinístico Un NFA es un modelo matemático que consiste de:  1.- Un conjunto de estados.  2.-Un conjunto de símbolos de entrada.  3.- Una función de transición que corresponde pares de estado-símbolo a conjuntos de estados.  4.- Un estado So que denota como el estado inicial.  5.- Un conjunto de estados F que denotan los estados de aceptación o finales.  Un NFA no tiene restricciones para que exista más de una transición con el mismo nombre a diferentes estados, por lo que en una representación tabular no es posible determinar de manera única el estado destino para un símbolo determinado. Por ejemplo, el siguiente diagrama representa la expresión (a | b)* a b b

No podemos determinar a qué estado se avanza del estado 0 con el símbolo a; puede ser al propio 0 o al 1 pero ¿bajo qué condición? No está determinado incluso si podrían existir transiciones sin ninguna restricción, que se denominan transiciones porque no requieren que un símbolo determinado ocurra para realizarse. Nuevamente, esto se vuelve indeterminado en una representación tabular. Autómata Finito Determinístico  Un DFA es un caso especial de NFA en l que ningún estado tiene transiciones para diversos estados bajo el mismo símbolo y no se permiten transiciones epsilon. Los diagramas de transición son autómatas determinísticos. Por ejemplo, el DFA de (a | b)* a b b puede ser:

de transición es el siguiente: La premisa que se tiene es que el diagrama representa la interpretación del patrón que queremos reconocer por medio de una expresión regular. Consideramos que el diagrama se pueda particionar como se muestra a continuación:

Que podría ser muy aproximado al diagrama de transiciones que construiríamos para la expresión mostrada anteriormente. Para llegar de la expresión regular al autómata determinístico se emplea el método de Thompson que permite hacer la transición y preparar la construcción del analizador léxico.  Posteriormente el analizador léxico formado pasará al analizador sintáctico para que éste haga su análisis y forme la representación intermedia con las expresiones regulares. 3.2.- ANALIZADOR LÉXICO  La función del analizador léxico es leer caracteres de entrada del código fuente y produce secuencias de símbolos y valores de símbolos que son analizados sintácticamente por el analizador sintáctico. Por consiguiente, el proceso de análisis léxico ó de reconocimiento puede entenderse como la transformación de un flujo de caracteres en un flujo de símbolos “reducido”. En este caso, el término “reducido” significa que la entrada es filtrada para eliminar aquellos elementos del texto del programa fuente que sólo sirven para hacer legible el programa. 

El comentario más corto está formado por A y B, cuyo lexema será /* */. Tanto C, D y E son partes opcionales que pueden aparecer o no dentro de la expresión regular. Consideremos cada parte:

*Cadena sobre un alfabeto.Salto de Separadores  Los espacios. <= y >. por ejemplo:  +.  Un apuntador al lugar donde se declara y referencia un nombre. si los nombres pueden ser de longitud arbitraria o si la longitud máxima es relativamente grande (32 caracteres). 2.. también son utilizadas para la detección de errores dentro de un programa fuente. Es decir. una gramática G define a un lenguaje L(G) mediante la definición de una manera para derivar todas las cadenas de dicho lenguaje. el analizador léxico tiene que examinar o analizar un carácter por anticipado ( a esto se le llama preanálisis) para determinar el símbolo en forma correcta. La tarea no trivial que tiene que efectuar el analizador léxico en este caso es distinguir entre identificadores y palabras clave. 3.4.3.  Luego de reconocer una expresión regular. *Operaciones de lenguaje. el salto de separadores es una tarea trivial. estas pueden ser: a)· Utilidades de caracteres y manejo de líneas. como es el uso de centinelas que sirven para marcar el final de buffer. 3.  Algún tipo de dirección. verlas como un movimiento a través del diagrama. representado por el conjunto de cadenas sobre un alfabeto. son representados por el conjunto finitos de símbolos. Funcionamiento  El analizador léxico es la primera fase de un compilador. excepto cuando hay comentarios anidados. Se utiliza durante la comprobación semántica o dependiente del contexto. En estos casos. debido a que es imposible enumerar todas las cadenas de caracteres en un lenguaje ya sea en español o en inglés. Aquí debemos tener en mente que probablemente ya se haya leído el primer carácter del símbolo siguiente. y palabras reservadas) del código fuente.2 Diagramas de sintaxis  Un método alternativo para desplegar las producciones de ciertas gramáticas de tipo 2 es el diagrama de sintaxis. * Lenguaje. que son.INTRODUCCIÓN A LAS GRAMÁTICAS LIBRES DE CONTEXTO Y ÁRBOLES DE DERIVACIÓN. la información que se incluirá en una tabla de símbolos depende del lenguaje y del diseñador del compilador. *. en cuyo caso se trata de un proceso recursivo. 4. Algunas limitaciones como el requisito de que los identificadores se declaren antes de ser utilizados no pueden describirse mediante una gramática dependiente del contexto.  Si existieran múltiples cadenas (de la misma longitud). *Cadena vacía (representada por un ε).1. . una tabla de símbolos consta de nombres y atributos. que es la secuencia finita de los símbolos de este alfabeto.  El reconocimiento de identificadores o de palabras clave (o reservadas) en un flujo de caracteres es una tarea trivial. • Escribir el analizador léxico en un lenguaje convencional de programación. Inmediatamente ejecutadas las acciones. Manejo de buffer de entrada  Utiliza 2 buffer de entrada resultara útil cuando es necesario un pre-análisis en la entrada para identificar los componentes léxicos. c)· Acciones (Lo que hace cada palabra). el token manager cambia su estado al estado de la necesidades del usuario. Ésta es una imagen de las producciones que permite al usuario ver las sustituciones en forma dinámica. los sujetos. Todas las variables y métodos declarados en el token manager están disponibles para ser usadas. 3. además del proceso de generación de código. basada en un autómata finito. Si el lenguaje de programación que se considera sólo permite nombres con máximo de caracteres.  Sin embrago. Los lenguajes naturales como el español o el inglés son descritos por una gramática que agrupa a las palabras en categorías sintácticas como: las expresiones. lo cual puede hacerse con tablas de símbolos o con una estructura de datos adicional que contenga todas las palabras reservadas. es decir cuando se encuentran solos.. comentarios y saltos de línea.  Hay muchas formas de manejar nombres de longitud variable. Reconocimiento de los Componentes Léxicos Todas las expresiones se consideran posibles tokens.  Información del alcance en caso de un lenguaje orientado a procedimientos. las fases posteriores deben analizar la salida del analizador sintáctico para garantizar la obediencia a las reglas que el analizador sintáctico no prueba. Se harán unos comentarios generales sobre el contenido de una tabla de símbolos. esto dependerá de que complejo sea el programa fuente.CREACIÓN DE TABLAS DE SÍMBOLOS  Una tabla de símbolos es una estructura de información para manejar los nombres (identificadores. se denominan separadores. y pueden ser muy numerosos en un código fuente. /. es fácil el manejo de los nombres: pueden almacenarse en un campo de nombres de longitud máxima de tamaño fijo. VENTAJAS EN LA UTILIZACIÓN DE GRAMÁTICAS La utilización de una gramática diseñada adecuadamente ofrece ventajas significativas a los diseñadores de lenguajes y de sus compiladores y entre ellas se pueden mencionar las siguientes: 1. Esto también sucede cuando se analiza un numeral o un símbolo de palabra.  Existen 4 funciones principales para un analizador léxico gráfico que va hallando cada token en forma consecutiva. En general. En un lenguaje de programación un analizador léxico efectúa una cantidad limitada de análisis sintáctico conforme produce la secuencia de componentes léxicos a partir de los caracteres de entrada. se ejecuta la acción léxica asociada. es decir. > Porque cada uno de ellos consta de un solo carácter que no aparece al comienzo de ningún otro símbolo.  Expresado en forma matemática una gramática es un dispositivo formal para especificar un lenguaje potencialmente infinito en una manera finita.  En la siguiente figura se ilustrarán los diagramas que resultan de la traducción de conjuntos de producciones típicos.. b)· Prueba de predicado. si es que la hay.  Los lenguajes evolucionan con el tiempo adquiriendo nuevas construcciones y realizando tareas adicionales que al inicio del diseño de un compilador no estaban contempladas. se elige la expresión regular que ocurre antes en el archivo de la gramática. uno que contiene un apuntador a una tabla aparte de cadenas y otro que contiene la longitud del nombre.  A partir de un lenguaje de programación se puede construir automáticamente un analizador sintáctico eficiente que determine si un programa fuente está sintácticamente bien formado. ESCRITURA DE UNA GRAMÁTICA  Las gramáticas son capaces de describir las sintaxis de los lenguajes. Una estrategia muy popular para manejar tales nombres en una tabla de símbolos consiste en dividir el campo de nombres en dos subcampos.. los verbos y otras frases preposicionales. ANÁLISIS SINTÁCTICO UNIDAD IV 4.Otra ventaja es que el proceso de construcción del analizador sintáctico puede revelar ambigüedades sintácticas y otras construcciones difíciles de analizar. símbolos como :  <. El apuntador especifica la posición del primer carácter del nombre en la tabla de cadenas.Una gramática diseñada adecuadamente impone una estructura a un lenguaje de programación. Al mismo tiempo una gramática impone una estructura a las sentencias en el lenguaje. mientras que los atributos pueden ser los siguientes:  Tipo o valor de un nombre. hay tres métodos generales de implantar un léxico: • Utilizar un generador de analizadores léxicos. Esto se muestra en el sig. *Operacionales con cadenas. d)· Errores léxicos. concatenación y exponenciación. GRAMÁTICA DE UN LENGUAJE  Las gramáticas se utilizan para describir a los lenguajes. esta posición se ilustra en la anterior.. es decir. Ejemplo:  Salto de Separadores La información almacenada en la tabla será completada durante las fases de análisis y luego se empleará en la generación de código. por lo general. el token manager prefiere la cadena mas larga que sea posible. >=  Comienzan con el mismo carácter. Esto es. <.  En general. su funcionamiento consiste en llevar los primeros caracteres de entrada y elaborar como salida una secuencia de caracteres léxicos que utilizan un analizador sintáctico pasar hacer el análisis.  La información almacenada en la tabla de símbolos varía de un compilador a otro. El token manager consume el número máximo de caracteres de la cadena de entrada que coincida con alguna de las expresiones regulares. tabuladores. la unión. -. útil para la construcción de programas fuente a un código objeto correcto.Una gramática ofrece una especificación sintáctica precisa y fácil de entender un lenguaje de programación. este método de captura o de almacenamiento directo no es aplicable o puede ser muy ineficiente. Por lo tanto las secuencias de componentes léxicos aceptadas por un analizador sintáctico forman un superconjunto de un lenguaje de programación.  La dimensión o el número de parámetros de un procedimiento. Sin embargo.MANEJO DE LOCALIDADES TEMPORALES DE MEMORIA (BUFFERS)  Especificaciones De Componentes Léxicos Las especificaciones del componente léxico son: *El alfabeto. Por supuesto. después se introducen algunas técnicas básicas para encontrar la velocidad del analizador léxico. concatenación. • Escribir léxico en lenguaje ensamblador y manejarlo explícitamente lectura de entrada. como el compilador LEX. el campo nombres contiene los nombres (identificadores). además de otras variables y métodos adicionales.. cerradura de Kleene (cierre*) y cerradura positiva (Cierre +).  Estas nuevas construcciones se pueden añadir con más facilidad a un lenguaje cuando existe una aplicación basada en una descripción gramatical del lenguaje. que de otro modo podrían pasar sin ser detectados en la fase inicial de diseño de un lenguaje y de su compilador.   Reconocimiento de Operadores y Nombres Para el compilador es fácil reconocer operadores cuando éstos son únicos en el programa. todas las producciones que aparecen en el lado derecho de algún enunciado BNF.

La asociatividad de + y * no resuelve esta ambigüedad. Se obtiene ahora aAde.  Sintaxis de proposiciones..  Sin embargo no hay muchos analizadores sintácticos con retroceso.  En el siguiente modelo de compilador. de acuerdo a las producciones mostradas. por ejemplo. el analizador sintáctico obtiene una cadena de componentes léxicos y comprueba si la cadena puede ser generada por la gramática del lenguaje fuente. b y d concuerdan con el lado derecho de alguna producción. porque casi nunca se necesita el retroceso para analizar sintácticamente las construcciones de los lenguajes de programación.  Los métodos empleados generalmente en los compiladores se clasifican como descendente o ascendente. estos analizadores utilizan el descenso recursivo. mostrando los operadores en orden de precedencia creciente. los analizadores sintácticos descendentes construyen árboles de análisis sintáctico desde arriba (la raíz) hasta abajo (las hojas).  E E + E │ E – E │ E * E │ E / E │ E ↑ E │ ( E ) │. son lo suficiente expresivas para describir la mayoría de las construcciones sintácticas de los lenguajes de programación. Por esta razón se necesita conocer la precedencia relativa de los operadores cuando está pendiente más de una clase de operadores. )  Ahora considérese los operadores binarios.  factor dígito ( exp.  prop id := exp. porque el lado derecho EAE tiene dos no terminales consecutivos.  Se puede considerar este proceso como de reducir una cadena w al símbolo inicial de la gramática. este análisis intenta construir un árbol de análisis sintáctico para una cadena de entrada que comienza por las hojas (el fondo) y avanza hacia la raíz (la cima). es 1.  Sintaxis de expresiones.ANALIZADOR SINTÁCTICO. en este caso un generador sintáctico LR.2. pero varias de estas subclases. que es el lado izquierdo de la producción B d. son demasiados ineficientes para usarlos en la producción de compiladores. Considérese la expresión 9+5*2. y un No Terminal adicional factor para generar unidades básicas en las expresiones.1. se han construido con éxito muchos compiladores que utilizan las técnicas de análisis sintáctico por precedencia de operadores para expresiones. y sin embargo..termino │ termino  termino termino * factor │ termino / factor │ factor  factor digito │ (expr)  Esta gramática considera una expresión como una lista de términos separados por los signos + o -. 5 es considerado por * en 9+5*2 y en 9*5+2. En esta sección se estudiará la mayor clase de gramáticas para las que se pueden construir con éxito analizadores sintácticos por desplazamiento y reducción (gramáticas LR) sin en embargo.  El analizador sintáctico se encarga realizar la segunda fase de la compilación de un programa fuente de un lenguaje de alto nivel. * y /. en la que el componente léxico id representa un identificador. se obtiene aABe. pueden analizar cualquier gramática. estas reducciones trazan la siguiente derivación por la derecha en orden inverso:  ANALIZADORES SINTÁCTICOS LR  A continuación se analiza una técnica de análisis sintáctico ascendente que se puede utilizar para analizar una clase mas amplia de gramáticas independientes del contexto. si * considera sus operandos antes de que lo haga +.21.4 construye analizadores sintácticos para gramáticas LL1. es difícil manejar componentes léxicos. las producciones son similares a las de las listas que asocian por la izquierda.4.  Ahora se puede sustituir toda esta por S. LALR)  Al análisis sintáctico ascendente también es conocido como análisis sintáctico por desplazamiento y reducción. * y /. Por tanto. si se sustituye cada una de sus alternativas por A.  Ejemplo 4. Todas las proposiciones del Lenguaje Pascal. es decir.  Las subcadenas b y d sirven.utilizando una tabla que muestre la asociatividad y precedencia de operadores se puede construir una gramática para expresiones aritméticas. se puede aplicar tan eficientemente como los otros métodos de desplazamiento y reducción. Las unidades básicas de las expresiones son de momento dígitos y expresiones entre paréntesis. denominado por descenso recursivo que puede incluir retrocesos. Se dice que * tiene mayor precedencia que +. <۰.PRECEDENCIA DE OPERADORES. dada su sencillez. Aunque b es la subcadena situada más a la izquierda que concuerda con el lado derecho de una producción. como el algoritmo de Cocke-Younger-Kasami y el Ealey. y un No Terminal adicional factor para generar unidades básicas en las expresiones.  También se puede considerar como un intento de construir un árbol de análisis sintáctico para la entrada iniciando desde la raíz y creando los nodos del árbol en orden previo. 4.  Con este método se puede escribir una gramática independiente del contexto y el generador produce automáticamente al analizador sintáctico para dicha gramática. como el signo menos.  El analizador sintáctico YACC es una herramienta adecuada como generador de analizador sintáctico. realizar varios exámenes de la entrada de la cadena de caracteres. En aritmética elemental. Históricamente. Estas gramáticas tienen la propiedad. Los méritos universales de análisis sintáctico. Sustituyendo después d por B. con los operadores de la misma precedencia en la misma línea:  Asociativos por la izquierda +  Asociativos por la derecha x / Se crean dos no terminales expr y término para los dos niveles de precedencia.ANALIZADOR DESCENDENTE (LL)  El análisis descendente se basa en gramáticas LL que permiten analizar una cadena de entrada sin que existan bloqueos mutuos.  Elíjase la b situada más a la izquierda y sustitúyase por A. genera listas de términos separados por los operadores aditivos. de manera que con los paréntesis se pueden desarrollar expresiones que tengan anidamiento de profundidad arbitraria y árboles de profundidad arbitraria.término │ término Por lo tanto la gramática resultante es:  expr expr + termino │ expr .  Los métodos descendentes y ascendentes más eficientes trabajan sólo con subclases de gramáticas. que tienen mayor precedencia. se traza una derivación por la derecha en sentido inverso.  E EAE│( E )│. de que ningún lado derecho de la producción es ε y no tiene dos no terminales adyacentes. y un término.  También debería de recuperarse de los errores que ocurren frecuentemente para poder continuar procesando el resto de su entrada.3. Como estos operadores asocian por la izquierda. como las gramáticas LL y LR. entre algunos pares de terminales.  Como técnica general de análisis sintáctico. Si la gramática contiene ambigüedades u otras construcciones difíciles de analizar en un . El análisis sintáctico es atractivo por varias razones:  Se pueden construir analizadores sintácticos LR para reconocer prácticamente todas las construcciones de los lenguajes de programación para los que se pueden escribir gramáticas independientes del contexto. │ if expr then prop │ if expr then prop else prop │ while expr do prop │ begin props_opc end El No Terminal props_opc genera una lista de proposiciones posiblemente vacía. eficientes analizadores sintácticos por desplazamiento y reducción. que tiene dos precedencias distintas (dependiendo de si es unario o binario).  término término * factor  │ término / factor  │ factor. Por ejemplo. La técnica se denomina análisis sintáctico LR(k). cuando se termina de construir un analizador sintáctico por precedencia de operadores a partir de una gramática.  bloque begin props_opc end  props_opc lista_props │ ε  lista_props lista_props .4. Se necesita una herramienta especializada. se examina la entrada al analizador sintáctico de izquierda a derecha.. el lado izquierdo de la producción A b. De esta forma la abbcde se reduce a S utilizando las producciones mostradas.E │id  A + │ . para una pequeña e importante clase de gramáticas se pueden construir con facilidad. Por tanto. así se obtiene la cadena aAbcde. Con frecuencia..  La clase de gramáticas que pueden analizarse con los métodos LR es un super conjunto de la clase de gramáticas que se pueden analizar con los analizadores sintácticos predictivos. la técnica se describió primero como una manipulación de componentes léxicos sin hacer referencia a ninguna gramática subyacente. mientras que los analizadores sintácticos ascendentes comienzan en las hojas y suben hacia la raíz. se definen tres relaciones de precedencia disjuntas.Las palabras claves permiten reconocer proposiciones en la mayoría de los lenguajes. y ۰> . el método de la sección 2. Las unidades básicas de las expresiones son de momento dígitos y expresiones entre paréntesis. como una lista de factores separados por los signos x o / .  En ambos casos.E │ id A continuación se describe una técnica de análisis sintáctico fácil de implantar llamada análisis sintáctico por precedencia de operadores.  En este método se considera una forma general de análisis sintáctico descendente.  Los métodos mencionados.  El principal inconveniente del método es que supone demasiado trabajo construir un analizador sintáctico LR a mano para una gramática de un lenguaje de programación típico. la “L” es por el examen de la entrada de izquierda a derecha. Una gramática con esta última propiedad se denomina gramática de operadores. un símbolo a la vez. = . comienzan con una palabra clave.4. Se crean dos no terminales expr y término para los dos niveles de precedencia.  Se puede considerar el análisis sintáctico descendente como un intento de encontrar una derivación por la izquierda para una cadena de entrada. es decir.  En cada caso de reducción se sustituye una subcadena determinada que concuerde con el lado derecho de una producción por el símbolo del lado izquierdo de dicha producción y si en cada paso se elige correctamente la subcadena. Existen tres tipos generales de analizadores sintácticos para gramáticas. La cadena aABe se puede sustituir directamente por S. las producciones son similares a las de las listas que asocian por la izquierda.│ * │ / │↑ Sin embargo.  Un analizador sintáctico LR puede detectar un error sintáctico tan pronto como sea posible hacerlo en un examen de izquierda a derecha de la entrada. las subcadenas Abc.  término término * factor  │ término / factor  │ factor. . la multiplicación y división tienen mayor precedencia que la adición y sustracción. separada por los símbolos de punto y coma utilizando las producciones.  En el análisis por precedencia de operadores. ) Ahora considérese los operadores binarios. se debe considerar que cualquier expresión entre paréntesis es un factor. De hecho. se puede prescindir de la gramática. Considérese la gramática S aABe A Abc | b B d  La frase (0 cadena) abbcde se puede reducir a S por los siguientes pasos: Abbcde aBcde aAde aABe S  Se examina la cadena abbcde mostrada..  expr expr + termino │ exp. Existen dos interpretaciones posibles para estas expresiones: (9+5) * 2 o 9+(5 * 2).4. que tienen mayor precedencia.  Como sus nombres indican.ANALIZADOR ASCENDENTE (LR. respectivamente. utilizando los no terminales de la pila solo como identificadores de los atributos asociados a los no terminales.  A continuación. el análisis por precedencia de operadores tiene varios inconvenientes. se obtiene la siguiente gramática de operadores. que es el lado derecho de la producción A Abc. buscando en las producciones una subcadena que concuerde con el lado derecho de alguna de ellas. se elige sustituir la subcadena Abc por A. 4. y al “R“ por construir una derivación por la derecha en orden inverso.  factor dígito ( exp. Esto es en gran parte. excepto las asignaciones y las llamadas a procedimientos  Algunas proposiciones del Lenguaje Pascal se definen por medio de la siguiente gramática ambigua. y la “k” por el número símbolos de entrada de examen por anticipado utilizados para tomar las decisiones del análisis sintáctico. prop │ prop ANÁLISIS SINTÁCTICO POR PRECEDENCIA DE OPERADORES La siguiente gramática para expresiones no es una gramática de operadores.  Los analizadores sintácticos implantados a mano a menudo trabajan con gramáticas LL1.  En casos como el análisis sintáctico del lenguaje natural. a mano.  Sin embargo.  El método de análisis sintáctico LR es el método de análisis sintáctico por desplazamiento y reducción sin retroceso más general que se conoce. las expresiones son equivalentes a 9+(5*2) y (9*5)+2.  Se supone que el analizador sintáctico informará de cualquier error de sintaxis que ocurra durante la compilación del programa de alto nivel. De hecho. 4.. exp. mediante una secuencia de cuatro producciones se puede reducir a abbcde a S. De manera similar. Como estos operadores asocian por la izquierda. Cuando se omite se asume que k. el retroceso no es muy eficiente.  Se empieza con los cuatro operadores aritméticos básicos y una tabla de precedencias. Los analizadores sintácticos para la clase más grande de gramáticas LR se construyen normalmente con herramientas automatizadas.

cuando se está realizando el análisis semántico y la generación de código intermedio.  YACC convierte esa especificación en una subrutina que maneja el proceso de entrada.Por ejemplo.Al descubrir un error.  Recuperación de corrección global.  La subrutina de entrada producida por YACC llama a la rutina provista por el usuario para devolver el próximo ítem básico de la entrada. es el más fácil de implantar. tiene que ser capaz de reconocer la presencia del lado derecho de una producción. a continuación se mencionan algunos de ellos:  Debe informar de la presencia de errores con claridad y exactitud.  Semánticos. introduce y utiliza información detallada sobre la memoria asignada a los identificadores.Por ejemplo. junto con el código que será invocado en la medida en que cada una de esas estructuras es reconocida.  Esta civilización sería completamente distinta si los lenguajes hablados exigieran tanta exactitud sintáctica como los lenguajes de programación. Por consiguiente. se pueden generar diagnósticos de error apropiados para indicar la construcción errónea conocida o reconocida en la entrada. como el punto y coma o la palabra clave end.ADMINISTRADOR DE LA TABLA DE SÍMBOLOS  La función esencial de un compilador es registrar los identificadores utilizados en el programa fuente y reunir información sobre los distintos atributos de cada identificador. Bison. cosas como el número de parámetros y tipo de argumentos.  Por el contrario. es decir. 4.  Por tanto.  YACC (YET ANOTHER COMPILERCOMPILER): provee una herramienta general para describir la entrada de un programa de computación. es el estado en que estaría el autómata finito reconocedor de los mangos si hubiera leído los símbolos gramaticales de la pila desde abajo hasta la cima. en una declaración de un lenguaje de programación como la siguiente:  float balance. promedio. que está en el tope de la pila.  Es evidente que quien diseña el compilador debe seleccionar los componentes léxicos de sincronización adecuados para el lenguaje fuente. puede aumentar la gramática del lenguaje con producciones que generen las construcciones erróneas.  Al descubrir un error.  GNU Bison tiene compatibilidad con Yacc: todas las gramáticas bien escritas para Yacc.  Sintácticos.  Una corrección local típica seria sustituir una coma por un punto y coma. pero el menos poderoso de los tres. se denomina gramática LR(k).. una llamada recursiva infinitamente o no equilibrar las palabras claves de inicio y fin en un procedimiento  Durante el diseño de un compilador. Sin embargo. en algunos casos un error pudo haber ocurrido mucho antes de la posición en que se detectó su presencia. En los casos más difíciles.  Una tabla de símbolos es una estructura de datos que contiene un registro por cada identificador con los campos apropiados para los atributos del identificador. Puede que no consiga producir una tabla de análisis sintáctico para algunas gramáticas que otros métodos sí consiguen.  La realización efectiva de estos objetivos plantea desafíos importantes. cuando el analizador léxico encuentra las variables durante la traducción de un programa.examen de izquierda a derecha de la entrada. por valor o referencia) y el tipo que devuelve. el analizador sintáctico desecha símbolos de entrada. escribir mal un identificador.  La mayoría de las especificaciones de los lenguajes de programación no describen como debe responder un compilador a los errores que encuentra en un programa al momento de ser compilado.  Cuando el analizador léxico detecta un identificador en el programa fuente. escribir una expresión aritmética con paréntesis no equilibrados o alterar la secuencia de caracteres en una palabra clave.5. el generador puede localizar dichas construcciones e informar al diseñador del compilador de su presencia.  La estructura de datos permite encontrar rápidamente el registro de cada identificador y almacenar o consultar rápidamente datos de ese registro.. cadena. 4.  Existe una diferencia significativa entre las gramáticas LL y las gramáticas LR.  El método se utilizo por primera vez en el análisis sintáctico descendente. así que estas técnicas en la actualidad solo son de interés teórico.  .  Este tipo de sustitución puede corregir cualquier cadena de entrada y ha sido empleado en varios compiladores que corrigen los errores. Sin embargo. gran parte de la detección de errores en un compilador se centra en la fase de análisis sintáctico. como los métodos LL y LR. hasta que encuentra uno perteneciente a un conjunto designado de componentes léxicos de sincronización.  Existen algoritmos para elegir una secuencia mínima de cambios para obtener una corrección global de menor costo de una cadena de entrada incorrecta x y la gramática G. donde tienen que ser capaz de reconocer el uso de una producción viendo solo los primeros k símbolos de los que se deriva su lado derecho.  Es utilizado para crear parsers para muchos lenguajes.  Otra razón es la precisión de los métodos modernos de análisis sintácticos.  Para que una gramáticas sea LR basta con que un analizador sintáctico con desplazamiento y reducción que opere de izquierda a derecha pueda reconocer los mangos cuando aparezcan en la cima de la pila. GNU Bison es un generador de parsers de propósito general que convierte una descripción gramatical desde una gramática libre de contexto LALR en un programa en C para hacer el parser.  El símbolo estado almacenado en la cima de la pila. una palabra clave o un operador. – Idealmente. la implementación de estos métodos es en general demasiado costosa en términos de tiempo y espacio. el método de pasar cada argumento (por ejemplo.  Los programas fuente de un lenguaje de programación de alto nivel puede contener errores de diversos tipos y entre ellos se pueden mencionar los siguientes:  Léxicos.  El tipo de datos (float) de los valores no se conoce.. tal que el número de inserciones superiores y modificaciones de componentes léxicos necesarios para transformar x en y sea el mínimo posible. es el más poderoso y costoso. desde simples calculadoras hasta lenguajes complejos.  Es decir.-Generadores de Código para Analizadores Sintácticos: Yacc.  Aunque la corrección en modo de pánico con frecuencia omite una cantidad considerable de entrada sin comprobar la presencia de errores adicionales. el analizador sintáctico puede realizar una corrección local de la entrada restante. Por supuesto se debe tener cuidado de elegir sustituciones que no conduzcan a lazos infinitos. El manejador de errores en un analizador sintáctico tiene objetivos fáciles de establecer. cuyo papel en el programa fuente está claro. el manejador de errores quizás tenga que adivinar qué tenía en mente el programador cuando escribió el programa.  Si ocurre un error. si se puede reconocer un mango conociendo solo los símbolos gramaticales de la pila. tienen la propiedad del prefijo viable. Es un hecho curioso que.  Cualquier persona que esté familiarizada con Yacc podría utilizar Bison sin problemas. detectan un error lo antes posible. corresponde al diseñador del compilador. el autómata no necesita leer la pila para cada movimiento. Los casos en que k = 0 o k = 1. Existen gramáticas independientes del contexto que no son LR.. pero en general se pueden evitar en las construcciones típicas de los lenguajes de programación. A continuación se mencionan algunas estrategias de recuperación de errores. en el caso de nombres de procedimientos.  Otra fuente de información que puede utilizar un analizador sintáctico LR como ayuda para tomar las decisiones de desplazamiento y reducción son los k símbolos siguientes de entrada. tienen interés práctico.  Una razón es que muchos errores son de naturaleza sintáctica o se manifiestan cuando la cadena de componentes léxicos que proviene del analizador sintáctico desobedece las reglas gramaticales que definen al lenguaje de programación. que pueden detectar la presencia de errores dentro de los programas de una forma muy eficiente. y un buen compilador debería ayudar al programador a identificar y localizar errores. Afortunadamente los errores más comunes son simples y con frecuencia basta con un mecanismo sencillo de manejo de errores..6.  Recuperación de producciones de error.1.  Recuperación de producciones de error. nada más con ver un prefijo de la entrada que no es prefijo de ninguna cadena del lenguaje.  Estos componentes léxicos de sincronización son generalmente delimitadores.Este es el método más sencillo de implantar y pueden utilizarlo la mayoría de los métodos de análisis sintáctico. 4. por lo general. Su principal desventaja es su dificultad para afrontar situaciones en que el error real se produjo antes del punto de detección. a continuación se introducen tres técnicas para construir una tabla de análisis sintáctico LR para una gramática. se necesita saber cuales son los tipos de identificadores. de uno en uno. para poder comprobar que el programa fuente los utiliza de una forma válida y así poder generar las operaciones apropiadas con ellos. No debe retrasar de manera significativa el procesamiento de programas correctos.  Varios métodos de análisis sintáctico. Lógicos.  Considerar desde el principio el manejo de errores puede simplificar la estructura de un compilador y mejorar su respuesta a dichos errores.Por ejemplo.  Si el analizador sintáctico usa una producción de error. se puede implantar en forma eficiente. el símbolo del estado en la cima de la pila contiene toda la información necesaria.  Las fases restantes introducen información sobre los identificadores en la tabla de símbolos y después la utilizan de varias formas. Después de estudiar la operación de un analizador sintáctico LR. El primer método. este identificador se introduce en la tabla de símbolos..  Entonces se usa esta gramática aumentada con las producciones de error para construir el analizador sintáctico. estos algoritmos encontrarán un árbol de análisis sintáctico para cada cadena relacionada y. su tipo. y aquí sólo se considerarán los analizadores sintácticos con k <= 1. Aunque ninguna de ellas ha demostrado ser de aceptación universal. el analizador sintáctico LR puede determinar a partir del estado de la cima de la pila. las gramáticas LR pueden describir más lenguajes que las gramáticas LL. Pero los programadores con frecuencia escriben programas incorrectos. leyendo los símbolos gramaticales de la pila de arriba abajo. llamado LR con examen por anticipado (LALR. y puede ser muy difícil deducir la naturaleza precisa del error. GRAMÁTICAS LR  Una gramática para la que se puede construir una tabla de análisis sintáctico se denomina gramática LR.  Recuperación de corrección global. sería deseable que un compilador hiciera el mínimo de cambios posibles al procesar una cadena de entrada incorrecta.  En situaciones en donde son raros los errores múltiples en la misma posición. El segundo método llamado LR Canónico.. se debe recuperar de ese error con la suficiente rapidez como para detectar los errores posteriores. la respuesta se deja al diseñador del compilador. habiendo visto todo lo que se deriva de dicho lado derecho con k símbolos de examen por anticipado. con un poco de esfuerzo.  Por ejemplo. tiene la ventaja de la sencillez y está garantizada contra lazos infinitos. entonces existe un autómata finto que puede.  Recuperación a nivel de frase.  Estos atributos pueden proporcionar información sobre la memoria asignada a un identificador.  Recuperación en modo de pánico. si siempre se insertara en la entrada por delante del símbolo de entrada en curso.  Es necesaria tener experiencia con lenguaje C para utilizar Bison.  El manejador de errores. si existe. Para que una gramática sea LR(k) . puede sustituir un prefijo de la entrada restante por alguna cadena que permita continuar al analizador sintáctico.  Una gramática que se puede analizar mediante un analizador sintáctico LR que examina hasta k símbolos de entrada en cada movimiento.. funcionan en Bison sin necesidad de ser modificadas. en inglés) está entre los otros dos en cuanto a poder y costo. El tercer método. que se están utilizando.  Recuperación en modo de pánico.  La función ir_a de una tabla de análisis sintáctico LR es esencialmente dicho autómata finito. determinar el mango.6. efectuar una operación aritmética con identificadores de diferentes tipos. su diseño e implantación se simplificarían mucho.. suprimir un punto y coma sobrante o insertar un punto y coma que falta. por ejemplo. El método LALR funciona con las gramáticas de la mayoría de los lenguajes de programación y.  Por desgracia. pocos lenguajes han sido diseñados teniendo en cuenta el manejo de errores. algunos métodos tienen una amplia aplicación.  El usuario de YACC especifica las estructuras de su entrada.  La elección de la corrección local.  Sin embargo. este requisito es mucho menos riguroso que el de las gramáticas LL(K).Por ejemplo. como sería el caso.RECUPERACIÓN DE ERRORES Existen muchas estrategias generales distintas que puede emplear un analizador sintáctico para recuperarse de un error sintáctico. lo cual quiere decir que detectan la presencia de un error. este método puede ser muy adecuado.  Un analizador LR no tiene que examinar la pila completa para saber cuando aparecen los mangos en la cima. si éste existe. Por ejemplo. su ámbito (la parte del programa donde tiene validez) y. llamado LR sencillo (en inglés SLR). todo lo que se necesita saber sobre lo que hay en ella.MANEJO DE ERRORES SINTÁCTICOS Y SU RECUPERACIÓN  Si un compilador tuviera que procesar sólo programas correctos. 4.  Es sorprendente que aunque los errores sean tan frecuentes.  Recuperación a nivel de frase.7. normalmente los atributos de cada identificador no se pueden determinar durante la fase de análisis léxico.  El generador de código. – Si tiene una buena idea de los errores comunes que puedan encontrarse.