Un analizador sintáctico reconoce si cadenas de caracteres forman parte de un lenguaje y agrupa tokens en frases gramaticales. Construye una representación interna del programa y detecta errores de sintaxis. El análisis sintáctico descendente parte del axioma y aplica reglas a no terminales izquierdos, mientras que el ascendente construye el árbol desde tokens hacia el axioma. Ambos pueden usarse con o sin retroceso.
1. Pontificia Universidad Católica del Ecuador
Sede Ibarra
Byron Chicaiza
5to Sistemas
8 de Mayo de 2014
¿QUE ES EL ANALIZADOR SINTACTICO?
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.
Agrupa los tokens del programa fuente en frases gramaticales que el compilador usará
en las siguientes etapas.
La principal tarea del analizador sintáctico no es comprobar que la sintaxis del
programa fuente sea correcta, sino construir una representación interna de ese
programa y en el caso en que sea un programa incorrecto, dar un mensaje de error.
Para ello, el analizador sintáctico (A.S.) comprueba que el orden en que el analizador
léxico le va entregando los tokens es válido. Si esto es así significará que la sucesión
de símbolos que representan dichos tokens puede ser generada por la gramática
correspondiente al lenguaje del código fuente.
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.
CARÁCTERISTICAS
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:
- ELIMINAR AMBIGUEDAD: Para eliminar la ambigüedad se debe reescribir la
gramática.
- 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.
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
2. la opción correcta.
Ejemplo:
• Análisis Sintáctico Descendente Con Retroceso.
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:
• Análisis Sintáctico Descendente Predictivo (Asdp)
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
3. 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)).
• Análisis ascendente con retroceso
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.
• Análisis Ascendente sin Retroceso
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.
Análisis semántico y tratamiento de errores.
La fase de análisis semántico revisa el programa fuente para tratar de encontrar
errores semánticos y reúne la información sobre los tipos para la fase posterior de
generación de código. En ella se utiliza la estructura jerárquica determinada por la fase
de análisis sintáctico para identificador los operadores y operandos de expresiones y
proposiciones.
Clasificación
Dinámica: Aspectos que solo pueden ser conocidos en tiempos de ejecución.
Estática: Aspectos que solo pueden ser controlados en tiempo de compilación.
Verificación de Tipos: el compilador verifica si cada operador tiene operandos
permitidos por la especificación del lenguaje fuente.
4. TRATAMIENTO DE ERRORES
Los errores encontrados en las distintas fases de análisis se envían a un módulo
denominado manejo de errores. En el caso más sencillo puede ser un subprograma al
que se le invoca enviándole el código de error, y que se encarga de escribir un
mensaje con el error correspondiente, y el número de línea donde se ha producido, así
como de cortar el proceso de traducción.
TIPOS DE ERRORES
Tipos de errores que suelen ocurrir (dependiendo de la fase):
Léxicos: No concuerda con ninguna ER.
Ejemplo: escribir mal una palabra clave
Sintácticos: La estructura que se ha seguido no es correcta.
Ejemplo: expresión con paréntesis no emparejados
Semánticos: La estructura está bien pero hay errores de significado Ejemplo:
operador y operandos incompatibles.
Lógicos: Los comete el programador
Ejemplo: una llamada infinitamente recursiva
Árboles Sintácticos
Es una clase especial de árbol, se le denomina Árbol Sintáctico es una forma
condensada de un árbol de análisis sintáctico, útil para representar instrucciones de
lenguajes. Un árbol sintáctico permite demostrar que una secuencia de caracteres es
una determinada categoría sintáctica. Llevan su nombre en base a la función que
cumple, donde cada nodo representa una operación y los hijos de un nodo son los
argumentos de la operación.
Un árbol sintáctico puede cumplir tres funciones diferentes:
Comprobar el orden en que llegan los tokens (componente léxico es una cadena de
caracteres)
Construir una representación del programa fuente.
Si es sintácticamente correcto generar el error
Un Árbol Sintáctico bien realizado se caracteriza por ser una representación abstracta
que va desde las sub-categorías hasta la categoría general, por constar de operadores
en nodos no terminales y operandos en nodos terminales.