El documento describe el papel del analizador sintáctico en un compilador. Explica que el analizador sintáctico recibe una cadena de tokens del analizador léxico y verifica si la cadena se genera por la gramática del lenguaje. También debe detectar y recuperarse de errores sintácticos de manera inteligible.
1. Pontificia Universidad Católica del Ecuador
Sede Ibarra
Nombre: Carlos Marcelo Rivadeneira Velasco
El papel del analizador sintáctico
En este modelo de compilador, el analizador sintáctico obtiene una cadena de
componentes léxicos del analizador léxico, y comprueba si la cadena puede
ser generada por la gramática del lenguaje fuente. Se supone que el
analizador sintáctico informará de cualquier error de sintaxis de manera
inteligible. También debería recuperarse de los errores que ocurren
frecuentemente para poder continuar procesando el resto de su entrada.
Los métodos empleados generalmente en los compiladores se clasifican como
descendentes o ascendentes. Como sus nombres indican, los analizadores
sintácticos descendentes construyen árboles de análisis sintáctico desde
arriba (la raíz) hasta abajo (las hojas), mientras que los analizadores
sintácticos ascendentes comienzan en las hojas y suben hacia la raíz. En
ambos casos, se examina la entrada al analizador sintáctico de izquierda a
derecha, un símbolo a la vez.
Análisis sintáctico descendente.
En éste analizador las entradas son de izquierda a derecha, y construcciones
de derivaciones por la izquierda de una sentencia o enunciado.
Características
El análisis sintáctico descendente (ASD) intenta encontrar entre las
producciones de la gramática la derivación por la izquierda del símbolo
inicial para una cadena de entrada.
Parte del axioma de la gramática.
Procesa la entrada de izquierda a derecha.
Escoge reglas gramaticales.
Bueno primeramente para trabajar el análisis sintáctico descendente
se debe realizar primeramente algunas operaciones para que la
gramática sea LL1 las cuales son:
o ELIMINAR AMBIGUEDAD: Para eliminar la ambigüedad se debe
reescribir la gramática.
o ELIMINAR RECURSIVIDAD POR LA IZQUIERDA: Una
gramática es recursiva por la izquierda si tiene un nodo Terminal
a tal que existe una derivación A->Aα para alguna cadena. Es
decir por simple observación podemos identificar.
2. Para eliminar la recursividad por la izquierda se utiliza la siguiente formula
Factorizar
Se trata de rescribir las producciones de la gramática con igual comienzo
para retrasar la decisión hasta haber visto lo suficiente de la entrada como
para elegir la opción correcta.
Ejemplo:
El método parte del axioma inicial y aplica todas las posibles reglas al no
terminal más a la izquierda.
Se usa el retroceso para resolver la incertidumbre.
Sencillo de implementar.
Muy eficiente.
Ejemplo:
3. El analizador debe realizar la previsión de la regla a aplicar sólo con ver el
primer símbolo que produce para que el algoritmo tenga una complejidad
lineal. Las gramáticas que son susceptibles de ser analizadas sintácticamente
de forma descendente mediante un análisis predictivo y consultando un
únicamente un símbolo de entrada pertenecen al grupo LL(1). A partir de
gramáticas LL(1) se pueden construir analizadores sintácticos descendentes
predictivos (ASDP), que son ASD sin retroceso.
Ejemplo:
Análisis sintáctico ascendente
El objetivo de un análisis ascendente consiste en construir el árbol sintáctico
desde abajo hacia arriba, esto es, desde los tokens hacia el axioma inicial, lo
cual disminuye el número de reglas mal aplicadas con respecto al caso
descendente (si hablamos del caso con retroceso) o amplía el número de
gramáticas susceptibles de ser analizadas (si hablamos del caso LL(1)).
Al igual que ocurría con el caso descendente, este tipo de análisis intenta
probar todas las posibles operaciones (reducciones y desplazamientos)
mediante un método de fuerza bruta, hasta llegar al árbol sintáctico, o bien
agotar todas las opciones, en cuyo caso la cadena se rechaza.
En el análisis con retroceso no se permiten las reglas ԑ, puesto que estas se
podrán aplicar de forma indefinida.
4. El análisis ascendente sin retroceso busca una derivación derecha de la
cadena de entrada de forma determinista.
Este se sustenta en su aplicación a las gramáticas LR(K).
La L viene de la lectura de la cadena de entrada de izquierda a derecha.
La R de producir un árbol de derivación derecho.
La k indica el número de símbolos que es necesario leer a la entrada
para tomar la decisión de qué producción emplear.
Un parser del tipo shift-reduce puede verse como un autómata de pila
determinista extendido que realiza el análisis de abajo hacia arriba.
Dada una cadena de entrada w, simula una derivación más a la derecha.
¿Cuál es su diferencia?
En el análisis sintáctico descendente: se construye el árbol sintáctico
arriba hacia abajo y se utiliza más reglas.
En el análisis sintáctico ascendente: se construye el árbol sintáctico
de abajo hacia arriba, lo cual disminuye el número de reglas mal
aplicadas con respecto al caso descendente.
Tratamiento de errores
Exige que el programa pueda ejecutarse. Suele utilizarse en sistemas que
generan .EXE directamente, pues ahorra tiempo (permite encontrar errores
de ejecución a la vez que los de compilación).
Sólo trata de evitar que el número de mensajes de error sea demasiado
grande y que el compilador/intérprete pueda seguir ejecutándose
correctamente en instrucciones sucesivas.
Errores ortográficos típicos:
Un carácter por otro.
Un carácter perdido.
Un carácter añadido.
Dos caracteres intercambiados.
5. Si se detecta al analizar la cadena
XUy
Donde x,y en A* y U en A es el próximo símbolo a analizar, podemos intentar
lo siguiente:
Borrar U e intentarlo de nuevo.
Insertar una cadena de terminales z entre x, U y empezar a analizar a
partir de z.
Insertar una cadena de terminales z entre x, U y empezar a analizar a
partir de U, poniendo z en la pila (si es análisis bottom-up).
Borrar símbolos del final de x e intentar de nuevo.
No hacer nunca los dos últimos. Deshace la información semántica
asociada.
Conviene tener una sola rutina de recuperación de errores separada del resto
del compilador.
Evitar que un solo error produzca varios mensajes.
Hay que señalar el error y detener la ejecución, permitiendo al
programador
Revisar las variables
Revisar el código
Modificar el código
Reanudar la ejecución
Saltarse líneas
Abandonar la ejecución del último programa
Abandonar totalmente la ejecución
Y asegurarse de que todo sigue correctamente. En lenguaje simbólico
se puede manipular la pila de ejecución, salir automáticamente de
rutinas pendientes, sin continuar la ejecución, etc.
6. Árboles sintácticos
Árbol sintáctico, para una oración compuesta (el niño que me saludó me odia)
en la que la oración subordinada es una oración de relativo. Dada una oración
o construcción compleja esta puede dividirse en constituyentes sintácticos,
cada uno de los cuales a su vez podría ser divisible o analizable en otros
constituyentes. El conjunto de constituyentes sintácticos admite con la
relación binaria de inclusión (o "ser parte de") es un conjunto parcialmente
ordenado.
Un árbol sintáctico es una representación de las relaciones jerárquicas entre
los constituyentes sintácticos. Más formalmente, un árbol sintáctico es una
grafo que representa esta relación de orden parcial.
EJEMPLO: