TECNOLOGICO NACIONAL DE MÉXICO
Instituto Tecnológico de Mexicali
CARRERA:
INGENIERIA EN SISTEMAS COMPUTACIONALES.
MATERIA:
Lenguajes y Autómatas I
TEMA:
Trabajo de investigación de análisis sintáctico
ALUMNO:
ALMADA VARGAS JESUS ALFONSO
NO. DE CONTROL:
17490425
PROFESOR:
CAROLINA RUIZ FLORES.
MAXICALI BAJA CALIFORNIA DE 22 DE ABRIL DEL 2020
INDICE
Análisis Sintáctico
Introducción……………………………………………………………………………………3
5.1 Definición y clasificación de gramáticas……………………………………………………3
Gramática tipo 0…………………………………………………………………………6
Gramática tipo 1…………………………………………………………………………6
Gramática tipo 2…………………………………………………………………………7
Gramática tipo 3…………………………………………………………………………7
5.2 Gramáticas Libres de Contexto (GLC) …………………………………………………8
5.3 Árboles de derivación. ……………………………………………………………………9
5.4 Formas normales de Chomsky. …………………………………………………………11
5.5 Diagramas de sintaxis ……………………………………………………………………14
5.6 Eliminación de la ambigüedad……………………………………………………………15
Tipos de Ambigüedad…………………………………………………………………15
5.7 Tipos de analizadores sintácticos ………………………………………………………...16
5.8 Generación de matriz predictiva (cálculo first y follow) ………………………………18
5.9 Manejo de errores. …………………………………………………………………………19
Fuentes Bibliográficas……………………………………………………………………………22
2
Introducción.
Todo lenguaje de programación tiene reglas que describen la estructura sintáctica de
programas bien formados. En Pascal, por ejemplo, un programa se compone de bloques, un
bloque de proposiciones, una proposición de expresiones, una expresión de componentes
léxicos, y así sucesivamente. Se puede describir la sintaxis de las construcciones de los
lenguajes de programación por medio de gramáticas de contexto libre o notación BNF (
Backus-Naur Form).
Las gramáticas ofrecen ventajas significativas a los diseñadores de lenguajes y a los
desarrolladores de compiladores.
• Las gramáticas son especificaciones sintácticas y precisas de lenguajes de
programación.
• A partir de una gramática se puede generar automáticamente un analizador
sintáctico.
• El proceso de construcción puede llevar a descubrir ambigüedades.
• Una gramática proporciona una estructura a un lenguaje de programación,
siendo más fácil generar código y detectar errores.
• Es más fácil ampliar/modificar el lenguaje si está descrito con una gramática.
La mayor parte de este tema está dedicada a los métodos de análisis sintáctico de uso típico
en compiladores. Primero se introducen los conceptos básicos, después las técnicas
adecuadas paran la aplicación manual. Además, como los programas pueden contener errores
sintácticos, los métodos de análisis sintáctico se pueden ampliar para que se recuperen de los
errores sintácticos más frecuentes.
5.1 Definición y clasificación de gramáticas.
El análisis sintáctico (parser en lengua inglesa) toma los tokens que le envía el
analizador léxico y comprueba si con ellos se puede formar alguna sentencia válida del
lenguaje. Recuérdese que se entiende por sintaxis como el conjunto de reglas formales que
especifican como se construyen las sentencias de un determinado lenguaje.
Es decir, es la fase del analizador que se encarga de chequear el texto de entrada en
base a una gramática dada. Y en caso de que el programa de entrada sea válido, suministra
el árbol sintáctico que lo reconoce. En teoría, se supone que la salida del analizador sintáctico
es alguna representación de árbol sintáctico que reconoce la secuencia de tokens suministrada
por el analizador léxico.
En la práctica, el analizador sintáctico también hace:
• Acceder a la tabla de símbolos (para hacer parte del trabajo del analizador
semántico).
3
• Chequeo de tipos (del analizador semántico).
• Generar código intermedio.
• Generar errores cuando se producen.
En definitiva, realiza casi todas las operaciones de la compilación. Este método de
trabajo da lugar a los métodos de compilación dirigidos por sintaxis.
Por otro lado, La gramática es un ente formal para especificar, de una manera finita,
el conjunto de cadenas de símbolos que constituyen un lenguaje.
La sintaxis de los lenguajes de programación habitualmente se describe mediante gramáticas
libres de contexto (o gramáticas tipo 2) utilizando algún tipo de notación. Por ejemplo, las
notaciones BNF y EBNF. Esta especificación ofrece numerosas ventajas a los diseñadores
de lenguajes y a los escritores de compiladores:
• Una gramática da una especificación sintáctica precisa, y fácil de comprender, de
un lenguaje de programación.
4
• Para ciertas clases de gramáticas se puede construir automáticamente un analizador
sintáctico, que determina si un programa está bien construido. Como beneficio adicional, el
proceso de construcción del analizador sintáctico puede revelar ambigüedades sintácticas y
otras dificultades para la construcción del parser que de otra forma no serían detectadas en la
fase de diseño inicial de un lenguaje y que podrían se arrastradas a su compilador.
• Una gramática bien diseñada da una estructura al lenguaje de programación que se
utiliza en la traducción del programa fuente en código objeto correcto y para la detección de
errores. Existen herramientas que a partir de la descripción de la gramática se puede generar
el código del analizador sintáctico.
• Los lenguajes de programación después de un periodo de tiempo, adquieren nuevas
construcciones y llevan a cabo tareas adicionales. Estas nuevas construcciones se pueden
añadir más fácilmente al lenguaje cuando hay una implementación basada en una descripción
gramatical del lenguaje
Chomsky clasificó las gramáticas formales (y los lenguajes que éstas generan) de
acuerdo a una jerarquía de cuatro niveles. Sorprendentemente, es posible establecer una
relación entre los diferentes niveles de la jerarquía de Chomsky y cuatro niveles de una
jerarquía definida entre los diferentes tipos de autómatas.
A cada nivel de gramática se le puede asociar de forma natural un conjunto de
lenguajes que serán los que esas gramáticas generan, pero, además, se le puede asociar una
clase de autómatas formada por aquellos que podrán reconocer a dichos lenguajes.
5
Cada nivel de lenguaje se corresponde con un tipo de autómata. Por ejemplo, dado un
lenguaje de tipo 3 siempre será posible encontrar un Autómata Finito que reconozca dicho
lenguaje, es decir, que permita determinar si una determinada palabra pertenece o no al
lenguaje. Si el lenguaje es de tipo 2 será necesario utilizar un autómata más complejo,
concretamente un Autómata de Pila.
La diferencia entre cada grupo se centra en la forma de las producciones. La misma
clasificación puede ser aplicada a los lenguajes, es decir, los lenguajes de tipo 0 son los
generados por las gramáticas de tipo 0 y así sucesivamente.
Gramática de tipo 0.
También se las llama gramáticas sin restricciones o gramáticas recursivamente enumerables,
Sin embargo, es posible demostrar que cualquier lenguaje de tipo
0 puede ser también generado por una gramática que pertenece a
un grupo algo más restringido, las gramáticas con estructura de
frase. Podemos decir, por tanto, que las gr. de tipo 0 y las gr. con
estructura de frase tienen el mismo poder generativo.
“x puede ser sustituido por y si x está, ya sea, en los símbolos No Terminales o los
símbolos Terminales, sin incluir la cadena vacía e y está en los símbolos No Terminales o
Terminales, incluyendo la cadena vacía.”
"+" significa "sin incluir la cadena vacía" y "*" significa "incluyendo la cadena vacía". "/"
significa "o"
Estos lenguajes también son denominados "recursivamente enumerables"
Gramática de tipo 1.
A este tipo de gramáticas también se las llama gramáticas dependientes del contexto.
No existen reglas compresoras en toda la teoría, salvo, opcionalmente, la que deriva el
axioma a la palabra vacía. Existen reglas en las que un símbolo no terminal puede derivar a
formas senténciales distintas, según los símbolos que aparezcan a su alrededor.
Es obvio que todas las gramáticas de tipo 1 son también gramáticas con estructura de
frase, pero en este caso hay una restricción añadida y es que la longitud de la parte derecha
de las producciones es siempre mayor o igual que la de la parte izquierda, es decir, no hay
producciones compresoras. El nombre de gramáticas dependientes del contexto se debe a que
las producciones se pueden interpretar como que “A se convierte en v, siempre que se
encuentre en un determinado contexto, es decir, precedida por x y seguida por y”. Por lo
tanto, es necesario conocer el contexto en el que se encuentra A para poder aplicar la
producción.
6
Ejemplos:
Gramática de tipo 2.
Son también llamadas gramáticas independientes del contexto. Sus producciones
son aún más restrictivas. En este caso, la parte izquierda de la producción
está formada por un ´único símbolo no terminal.
En este tipo de gramáticas, la conversión de A en v se realiza independientemente del
contexto en el que se encuentre A, de ahí su nombre. Son especialmente adecuadas
para representar los aspectos sintácticos de cualquier lenguaje de programación.
La mayoría de los lenguajes de programación entran en ésta categoría en cuanto su
forma sintáctica, aunque en realidad los lenguajes de programación son dependientes del
contexto, se reconocen a través de lenguajes de tipo 2 porque su reconocimiento es de O(n)
mientras que los de tipo 1 tienen un orden de reconocimiento O(n^3) en el peor caso. Por
este motivo se ejecuta un análisis semántico para reconocer si el programa es correcto.
Ejemplo:
Gramática de tipo 3.
Es el grupo más restringido de gramáticas y también son llamadas regulares. En este caso
también se le imponen restricciones a la parte derecha de las producciones, que tendrán
como máximo dos símbolos
7
Existen 2 tipos: lineales por la derecha y lineales por la izquierda.
Ejemplo:
Cualquier lenguaje de tipo 3 puede ser generado tanto por una gramática lineal por la
derecha como por una lineal por la izquierda. Es decir, estos dos grupos de gramáticas
tienen el mismo poder generativo
5.2 Gramáticas Libres de Contexto (GLC)
La gramática que acepta el analizador sintáctico es una gramática de contexto libre.
Una Gramática Libre de Contexto es una tupla con 4 parámetros:
G = (V, T, P, S)
• V – conjunto de símbolos variables
• T – conjunto de símbolos terminales
• S ∈ V, símbolo inicial
• P – conjunto de reglas de producción:
A → α , con α sucesión de símbolos de V ∪ T, eventualmente vacía (α = ε)
Este tipo de gramática, capturan la noción de constituyente sintáctico y la noción de orden.
Es una herramienta formal que puede ser vista tanto desde un punto de vista generador como
estructurador.
Una GLC es un dispositivo generador.
• Definimos el lenguaje LG generado por una gramática G del siguiente modo:
8
LG = { w / S ⇒* w } , siendo ⇒* una “especie” de clausura transitiva de → y w una tira de
terminales
Ejemplo:
Usando las siguientes palabras:
El avión
Un perro andaluz
París
5.3 Árboles de derivación.
La cadena de caracteres que resulta de concatenar los caracteres terminales encontrados en las
etiquetas de los nodos hoja, en un recorrido en orden del árbol de derivación, se llama el producto del
árbol. Es decir, al efectuar un recorrido en orden del árbol de derivación recuperamos la cadena a
partir de la cual se construyó dicho árbol. Así, el problema de “compilar” una cadena de caracteres
consiste en construir el árbol de derivación a partir del producto de éste.
Formalmente, un árbol de derivación es un grafo dirigido arborescente; definido de la manera
siguiente:
Sea G = (V, ∑,R, S) una GLC. Entonces un árbol de derivación cumple las siguientes propiedades:
1. Cada nodo tiene una etiqueta.
2. La raíz tiene etiqueta S.
3. La etiqueta de los nodos que no son hojas debe estar en V, y las de las hojas en ∑ U { ξ }.
4. Si un nodo n tiene etiqueta A, y los nodos n1 , . . . , nm son sus hijos (de izquierda a derecha), con
etiquetas respectivamente A1 , . . . ,Am, entonces A A1 , . . . ,Am ∈ R.
Las GLC tienen la propiedad de que las derivaciones pueden ser representadas en forma arborescente.
Por ejemplo, considérese la gramática siguiente para producir el lenguaje de los paréntesis bien
balanceados, que tiene palabras como (()), ()(), (()())(), pero no a (() ni )(:
Por ejemplo, tenido las siguientes reglas gramaticales:
1. S -> aSb
2. S -> ab
La gramática de {anbn} que presentamos antes se representa formalmente como:
9
({S}, {a, b}, {(S, aSb), (S, ab)}, S)
Donde:
V es un alfabeto de variables
Σ es un alfabeto de constantes
R, el conjunto de reglas
S, el símbolo inicial, es un elemento de V.
Continuando con el ejemplo anterior, es decir:
Usando la gramática anterior como podemos derivar la palabra
(()())():
Usando esta gramatical, la palabra (()())() puede ser derivada de la
siguiente manera:
En dicha figura se puede apreciar la estructura
que se encuentra implícita en la palabra (()())().
A estas estructuras se les llama árboles
de derivación, o también árboles de
compilación (por usarse extensivamente en los
compiladores) y son de vital importancia para
la teoría de los compiladores de los lenguajes
de programación.
10
Para el árbol de la figura anterior, hay al menos las derivaciones siguientes (anotamos
como subíndice de ⇒ el número de regla aplicado):
5.4 Formas normales de Chomsky.
Una gramática libre de contexto G=(V,T,P,S) se dice estar en forma normal de Chomsky si
sus producciones son de cualquiera de las dos formas
Toda gramática libre de contexto G=(V,T,P,S) que no genere a la palabra vacía se puede
transformar en una gramática libre de contexto G'=(V',T,P',S') en forma normal de Chomsky.
En efecto, dada una gramática G, apliquemos el último procedimiento de la sección anterior
para transformar a G en una gramática G'' sin variables inútiles ni producciones vacías ni
producciones unitarias equivalente a G. A las producciones que quedasen de la forma con y
las dejamos sin cambio alguno.
A cada producción de la forma, con y, la transformamos en una sucesión de
producciones de la forma siguiente: A cada símbolo terminal que aparezca en la palabra le
asociamos una variable nueva Xa e incorporamos la producción. Así pues, las producciones
que no sean de la forma con X variable y a terminal, han de ser de la forma, con todos
variables. Para cada una de estas últimas producciones introducimos k-2 nuevas variables e
incorporamos la sucesión de producciones.
Pasos para la la transformación de una gramática a la FNC
1º Eliminamos reglas unitarias.
A -> Ac
A -> w
Primero verificamos si en la gramática no hay reglas unitarias que obstruyan el desarrollo de
FNC. Un ejemplo de una regla unitaria seria:
A -> X
11
X -> z
Como se puede observar, un No Terminal A deriva en otro No Terminal X, que a su
vez deriva en un Terminal z, esto es redundante y por lo tanto se procede a eliminar el No
Terminal X y pasando el Terminal z al No Terminal A
2º Eliminamos reglas no productivas
Una regla no productiva consiste en un No Terminal que nunca es accesible desde el
No Terminal principal y sus respectivas derivaciones, del mismo o de las que provoquen sus
No Terminal que se encuentren en su propia derivación. Un ejemplo de una regla no
productiva seria:
A -> AZ
W -> X
Z -> c
En el ejemplo anterior el No Terminal W es una regla no productiva porque no es
accesible desde el No Terminal principal que es A, ni de su derivación correspondiente que
es AZ.
Nota: Este paso es omitido en el programa, por lo que no se verifica si un No Terminal
es improductivo, por lo tanto, el usuario debe asegurarse de no introducir éste tipos de reglas.
3º Se procede a dar el formato correspondiente de la FNC.
La FNC (Forma Normal de Chomsky) sigue dos reglas básicas y únicas para tener el formato
debido, estás son:
1) Un No Terminal solo puede derivarse en otros dos No Terminales.
2) Un No Terminal solo puede derivar en un Terminal.
Para la explicación de estas dos propiedades procedemos con un ejemplo:
Tenemos la siguiente gramática:
A -> cB+
B -> q
Nota: La gramática anterior no tiene ni reglas unitarias ni reglas no productivas, con
lo que procedemos con el paso 3º.
Observemos la primera producción:
A -> cB+
12
1) Este No Terminal A deriva en cB+ donde como primer elemento de esta es un elemento
Terminal, entonces procedemos a crear un No Terminal nuevo con este elemento y se lo
agregamos al no Terminal A, respetando el orden de la gramática.
A -> ZB+
Z -> c
2) Una de las propiedades para la FNC es que un No Terminal solo puede derivar en otros
dos No terminales, en nuestro ejemplo, tenemos el No Terminal A que deriva en ZB+, este
contiene otro elemento terminal más, creamos un nuevo No Terminal para respetar la
propiedad anteriormente descrita.
A -> ZY
Z -> c
Y -> B+
3) Podemos ver que los No Terminal A y Z cumplen con las propiedades de la FNC, excepto
el No Terminal Y que deriva en un No Terminal y un Terminal, por lo que procedemos a
crear el último No Terminal que cumpla la FNC.
A -> ZY
Z -> c
Y -> BX
X -> +
Por lo que se obtiene como resultado:
A -> ZY
Z -> c
Y -> BX
X -> +
B -> q
Toda gramática libre de contexto es equivalente a una gramática libre de contexto en Forma
Normal de Chomsky. Además, esta equivalencia es algorítmicamente computable.
13
5.5 Diagramas de sintaxis
Un diagrama de sintaxis (también llamados diagramas de Conway) es un grafo dirigido donde
los elementos no terminales aparecen como rectángulos, y los terminales como círculos o
elipses.
Todo diagrama de sintaxis posee un origen y un destino, que no se suelen representar
explícitamente, sino que se asume que el origen se encuentra a la izquierda del diagrama y el
destino a la derecha.
Cada arco con origen en α y destino en β representa que el símbolo α puede ir seguido
del β (pudiendo ser α y β tanto terminales como no terminales). De esta forma todos los
posibles caminos desde el inicio del grafo hasta el final, representan formas senténciales
válidas.
Potencia de los diagramas de sintaxis
Demostraremos que los diagramas de sintaxis permiten representar las mismas gramáticas
que la notación BNF, por inducción sobre las operaciones básicas de BNF:
Siguiendo la «piedra de Rosetta» que supone la tabla anterior, es posible convertir cualquier
expresión BNF en su correspondiente diagrama de sintaxis, ya que A y B pueden ser, a su
vez, expresiones o diagramas complejos.
Por otro lado, quien haya trabajado con profusión los diagramas de sintaxis habrá
observado que no siempre resulta fácil convertir un diagrama de sintaxis cualquiera a su
correspondiente en notación BNF.
14
Ahora vamos a establecer una correspondencia entre el flujo de ejecución de un
programa y el camino que se puede seguir en uno o varios diagramas de sintaxis para
reconocer una sentencia válida. Por ejemplo, la yuxtaposición quedaría como ilustra la figura:
5.6 Eliminación de la ambigüedad
Una gramática ambigua permite más de una derivación para la misma forma sentencia por
lo que también habrá más de un [árbol de derivación] para la misma. Por ello basta con
encontrar dos [árboles derivación] distintos para la misma forma sentencial para demostrar
que una gramática es ambigua.
Sea G = { N , T , P , S } una gramática libre de contexto y sea L(G) el lenguaje
generado por esa gramática.
Si existe un string w (donde w oe L(G) ) para el cual existen dos ó más formas de realizar la
derivación de más a la izquierda (S ˘ w) ó existen dos ó más formas de realizar la derivación
de más a la derecha (S ¿ w), entonces se dice que: G es una gramática ambigua.
Tipos de Ambigüedad.
Dentro del estudio de gramáticas existen dos tipos fundamentales de ambigüedad, los cuales
son:
Ambigüedad Inherente: Las gramáticas que presentan este tipo de ambigüedad no pueden
utilizarse para lenguajes de programación, ya que por más transformaciones que se realicen
sobre ellas, NUNCA se podrá eliminar completamente la ambigüedad que presentan.
Ambigüedad Transitoria: Este tipo de ambigüedad puede llegar a ser eliminada realizando
una serie de transformaciones sobre la gramática original. Una vez que se logra lo anterior,
la gramática queda lista para ser reconocida por la mayor parte de los analizadores sintácticos.
(Se le considera "ambigüedad" porque existen métodos para realizar análisis sintáctico que
no aceptan gramáticas con estas características)
Generalmente la ambigüedad se presenta cuando existen producciones con factores
comunes (distintas alternativas para un símbolo no-terminal que inician de la misma forma);
ó cuando existen producciones que son recursivas izquierdas (producciones para un símbolo
15
no-terminal en las cuales el primer símbolo de su forma sentencial es ese mismo símbolo no-
terminal).
Para eliminar este tipo de ambigüedad, es necesario, primero elimina:
1. Factores comunes izquierdos inmediatos y No-inmediatos
2. Recursividad izquierda inmediata y No-inmediata.
NOTA:
1- No existe un algoritmo que nos indique si una GIC es ambigua
2- Existen LIC que sólo tienen GIC ambiguas: inherentemente ambiguos
3- No se respeta la precedencia de operadores
4- Una secuencia de operadores idénticos puede agruparse desde la izquierda y
desde la derecha. Lo convencional es agrupar desde la izquierda.
5.7 Tipos de analizadores sintácticos
Hay dos categorías generales de algoritmos para construir analizadores sintácticos: los
analizadores sintácticos descendentes y los analizadores sintácticos ascendentes.
Estos a su vez se subdividen en varios métodos, cada uno de los cuales tiene distintas
capacidades y propiedades.
Tipos de analizadores sintácticos descendentes (top-down)
Definición. Construyen el árbol de análisis sintáctico desde arriba (raíz o axioma de la
gramática) hacia abajo (hojas con los terminales):
Análisis sintáctico predictivo: Es un analizador que no necesita retroceso. Para poder utilizarlos
la gramática no puede ser ambigua y se determina qué regla aplicar a partir de un análisis previo
de los tokens de la entrada (símbolo de pre análisis, que es el componente léxico de la cadena de
entrada situado más a la izquierda). Un ejemplo de este tipo de analizadores sintácticos
descendentes es el método LL.
16
Análisis sintáctico por descenso recursivo (o con retroceso): Este analizador hace una búsqueda
en profundidad retroceso, es decir, que puede hacer varios exámenes de la entrada. Presenta el
inconveniente de que no es eficiente.
Algoritmo de construcción del árbol de análisis sintáctico descendente:
1. Colocar el axioma como raíz del árbol de derivación.
2. Hasta que solo haya terminales en las hojas derivar por el símbolo que está más a la izquierda y
hacia la derecha.
Tipos de analizadores sintácticos ascendentes (bottom-up)
Construye el árbol de análisis sintáctico desde
Definición. abajo hacia arriba
Analizadores de precedencia de operador: Se Analizadores LR: Hacen un examen de la
utiliza para un pequeño conjunto de entrada de izquierda a derecha (left-to right, L)
gramáticas, denominadas gramáticas de y construyen las derivaciones por la producción
operadores (una propiedad que deben cumplir más a la derecha (rightmost derivation, R).
estas gramáticas es que no pueden tener dos Estos analizadores también son denominados
terminales seguidos). Se definen relaciones de analizadores por desplazamiento y reducción.
precedencia disjuntos entre los terminales, que Hay varios autómatas LR y todos llevan un
son los operadores, y estas relaciones guían la símbolo de análisis por anticipado de la entrada
selección de las producciones. (símbolo de lookahead).
Hay varios tipos de analizadores sintácticos LR, dependiendo de cómo se construya el AFD y la tabla
de análisis en el que están basados. El significado del acrónimo LR(k) es el siguiente:
Hay dos tipos, pero puesto que los analizadores por precedencia de operador se dedican a
unas gramáticas muy restringidas, como las gramáticas de operador, nos vamos a centrar en los
17
analizadores LR que son los más extendidos por la amplitud de gramáticas que manejan. Los
analizadores LR se subdividen a su vez en los siguientes tipos:
Análisis sintáctico por desplazamiento: porque va desplazando el símbolo de la entrada a la pila de
análisis.
Análisis sintáctico por reducción: porque se trata de sustituir una producción de la gramática por la
frase que la ha generado. Esta sustitución se produce cuando el autómata ha reconocido la parte
derecha de una producción, y se sustituye por la parte izquierda, reduciéndose el número de términos
en la pila.
¿Cuál elegir?
Depende de lo que queramos hacer: los analizadores sintácticos descendentes del tipo
predictivo permiten construir analizadores sintácticos eficientes con mayor facilidad, mientras que
los analizadores sintácticos ascendentes pueden manejar una mayor clase de gramáticas, siendo
estos últimos los más utilizados por los compiladores actuales.
5.8 Generación de matriz predictiva (cálculo first y follow)
Cuando tienes una Gramática LL1 entonces utilizas lo que denominas como First y Follow,
lo que haces es sacar con una serie de reglas (siguiendo un algoritmo) todos los first y follows
de la gramática. Los First y Follows son significados para verificar y construir parsers
predictivos, son un conjunto de tokens que se encuentran en el tope de la pila.
18
FIRST: Sea G := (V; ∑; Q0; P) una gramática libre de contexto. Para cada forma sentencial
α ϵ (V U ∑)* y para cada k ϵ N definiremos la función.
En otras palabras, el operador FIRST k asocia a cada forma sentencial los primeros k
símbolos de cualquier forma terminal alcanzable desde a mediante derivaciones “masa la
izquierda".
FOLLOW: Con las mismas notaciones anteriores, para cada forma α ϵ (V U ∑)* definiremos
la función FOLLOWG GK (a) del modo siguiente:
De nuevo nos ocuparemos solamente de FOLLOW: = FOLLOW1. Obsérvese que FOLLOW
k (α) ⊂ ∑* y que para cada x ∑ FOLLOW (α), Ixl < k. Obsérvese que para cada variable A
∑ V, FOLLOW(A) son todos los símbolos terminales que pueden aparecer a la derecha de A
en alguna forma sentencial de la gramática.
5.9 Manejo de errores.
Un compilador es un sistema que en la mayoría de los casos tiene que manejar una entrada
incorrecta. Sobre todo en las primeras etapas de la creación de un programa, es probable que
el compilador se utiliza para efectuar las características que debería proporcionar un buen
sistema de edición dirigido por la sintaxis, es decir, para determinar si las variables han sido
declaradas antes de usarla, o si faltan corchetes o algo así. Por lo tanto, el manejo de errores
es parte importante de un compilador y el escritor del compilador siempre debe tener esto
presente durante su diseño.
Hay que señalar que los posibles errores ya deben estar considerados al diseñar un
lenguaje de programación. Por ejemplo, considerar si cada proposición del lenguaje de
programación comienza con una palabra clave diferente (excepto la proposición de
asignación, por supuesto). Sin embargo, es indispensable lo siguiente:
19
El compilador debe ser capaz de detectar errores en la entrada;
1- El compilador debe recuperarse de los errores sin perder demasiada información.
2- Y sobre todo, el compilador debe producir un mensaje de error que permita
corregir fácilmente los elementos programador (sintácticamente) incorrectos de
su programa. Al encontrar y corregir fácilmente los elementos.
Errores Sintácticos.
Muchos errores de naturaleza sintáctica Recuperación: Al producirse un error el compilador
debe ser capaz de informar del error y seguir compilando. (Ideal).
El manejo de errores de sintaxis es el más complicado desde el punto de vista de la
creación de compiladores. Nos interesa que cuando el compilador encuentre un error, se
recupere y siga buscando errores. Por lo tanto, el manejador de errores de un analizador
sintáctico debe tener como objetivos:
Indicar los errores de forma clara y precisa. Aclarar el tipo de error y su localización.
Recuperarse del error, para poder seguir examinando la entrada.
No ralentizar significativamente la compilación.
Un buen compilador debe hacerse siempre teniendo también en mente los errores
que se pueden producir; con ello se consigue:
Simplificar la estructura del compilador.
Mejorar la respuesta ante los errores.
Algunas estrategias son las siguientes:
Ignorar el problema: Esta estrategia (denominada panic mode en inglés) consiste en
ignorar el resto de la entrada hasta llegar a una condición de seguridad. Una condición tal se
produce cuando nos encontramos un token especial (por ejemplo un ‘;’ o un ‘END’). A partir
de este punto se sigue analizando normalmente. Los tokens encontrados desde la detección
del error hasta la condición del error son desechados, así como la secuencia de tokens previa
al error que se estime oportuna (normalmente hasta la anterior condición de seguridad).
Recuperación a nivel de frase.: Intenta corregir el error una vez descubierto. [Link]., en el
caso propuesto en el punto anterior, podría haber sido lo suficientemente inteligente como
para insertar el token ‘;’ . Hay que tener cuidado con este método, pues puede dar lugar a
recuperaciones infinitas, esto es, situaciones en las que el intento de corrección no es el
20
acertado, sino que introduce un nuevo error que, a su vez, se intenta corregir de la misma
manera equivocada y así sucesivamente
Reglas de producción adicionales: Este mecanismo añade a la gramática formal que
describe el lenguaje reglas de producción para reconocer los errores más comunes. Siguiendo
con el caso del punto 3.3.1 , se podría haber puesto algo como:
sent_errónea › sent_sin_acabar sent_acabada
sent_acabada › sentencia ‘;’
sent_sin_acabar › sentencia
Lo cual nos da mayor control, e incluso permite recuperar y corregir el problema y emitir un
mensaje de advertencia en lugar de uno de error.
Corrección global: Este método trata por todos los medios de obtener un árbol sintáctico
para una secuencia de tokens. Si hay algún error y la secuencia no se puede reconocer,
entonces este método infiere una secuencia de tokens sintácticamente correcta lo más
parecida a la original y genera el árbol para dicha secuencia. Es decir, el analizador sintáctico
le pide toda la secuencia de tokens al léxico, y lo que hace es devolver lo más parecido a la
cadena de entrada, pero sin errores, así como el árbol que lo reconoce
21
Fuentes Bibliográficas.
1- J Ezpleata. (S/F). Lección 3: Fundamentos para el análisis sintáctico. 21
de Abril 2020, de Compiladores I. C.P.S. Universidad de Zaragoza Sitio
web:
[Link]
mpi:[Link]
2- Cueva J.. (1995). Análisis Sintáctico en Procesadores de Lenguaje. Abril 22,
2020, de Universidad de Oviedo Sitio web:
[Link]
[Link]
3- Olivares J. (S/A). Análisis Sintáctico. Abril 23, 2020, de Instituto Tecnológico
de Morelia Sitio web:
[Link]
4- Cueva J.. (2001). Lenguas Gramáticas y Autómatas. Abril 22, 2020, de
Universidad de Oviedo Sitio web:
[Link]
5- Departamento de Sistemas. (s/f). Gramáticas. Abril 20 del 2020, de Benemérita
Universidad Autónoma de Puebla Sitio web:
[Link]
6- Vázquez J. (2008). Unidad de competencia IV: “Conocer, utilizar y diseñar
gramáticas de libre contexto”. Abril 22 del 2020, de Universidad Autónoma del
Estado de México Centro Universitario UAEM Texcoco Sitio web:
[Link]
7- Departamento de Sistemas Computacionales. (s/f). La Forma Normal de
Chomsky Algoritmos Polinomiales para el Problema de la Palabra en CFL. Abril
22 del 2020, de Universidad de Cantabria Sitio web:
[Link]
8- Aguilera M, Gálvez S. (2011). Análisis Sintáctico. Abril 20 del 2020, de
Universidad de Malaga Sitio web:
[Link]
9- Gálvez Rojas S. (2011). Análisis sintáctico. Abril 22 del 2020, de Universidad
de Malaga Sitio web:
[Link]
10- Universidad Europea de Madrid. (2011). ANALIZADORES SINTÁCTICOS.
ANÁLISIS SINTÁCTICO I, Volumen I, 21. URL:
[Link]
pdf
22