Вы находитесь на странице: 1из 4

INTRODUCCIÓN AL ANÁLISIS SINTÁCTICO

Analizador sintáctico (parser)


Tratamiento de los errores sintácticos
Gramáticas libres de contexto (GLC)
Conceptos relacionados

Analizador Sintáctico (parser)


Dada una cadena de entrada, como una secuencia de tokens, podemos preguntarnos: ¿Es esta
cadena sintácticamente válida? Esto es, ¿puede generarse mediante la gramática que define el
lenguaje? Un algoritmo que responde “sí” o “no” a estas preguntas se llama reconocedor.
Podemos pedirle mas al algoritmo y preguntar: ¿Es esta cadena sintácticamente válida?, y si lo
es, ¿cuál es su estructura o árbol de derivación? Un algoritmo que responde a esta pregunta más
general se llama parser.
El objetivo del parser o analizador sintáctico en el compilador es determinar si la secuencia de
tokens generada por el analizador léxico está correctamente desde el punto de vista sintáctico.
Para ello determina si la cadena que representa la secuencia de tokens puede ser generada por la
gramática que define sintácticamente al lenguaje.

Programa token Árbol sintáctico


Scanner Parser
Fuente
obtener
prox. token

Tablade
Símbolos

Existen tres tipos generales de parsers para las gramáticas. Los métodos de análisis universales
como los algoritmos de Cocke-Younger-Kasami y de Earley son capaces de analizar cualquier
gramática, pero son demasiados ineficientes como para ser usados en los compiladores. Los
métodos mas empleados por los compiladores se clasifican en top-down (de arriba hacia abajo) y
bottom-up (de abajo hacia arriba). Como lo indican sus nombres, el método top-down construye
el árbol de derivación de arriba hacia abajo, o sea, de la raíz a las hojas, mientras que el bottom-
up lo construye de las hojas a la raíz.
Los métodos top-down y bottom-up mas eficientes trabajan bien solo para subclases de
gramáticas, pero algunas de estas subclases, como las gramáticas LL y LR, son lo
suficientemente expresivas para describir la mayoría de las construcciones sintácticas de los
lenguajes de programación.

Tratamiento de los errores sintácticos


Si los compiladores tuvieran que procesar solamente programas correctos, su diseño e
implementación se simplificaría en buena medida. Pero los programadores escriben programas
incorrectos frecuentemente, y un buen compilador debe ayudar al programador a localizar e
identificar los errores.
Los errores en un programa pueden clasificarse en 4 grandes grupos:
- lexicológicos (ej. escribir mal un número, un símbolo no permitido, etc.)
- sintácticos (ej. expresión aritmética con paréntesis no balanceados)
- semánticos (ej. aplicar un operador a un operando incompatible)
- lógicos o de programación (ej. ciclo infinito)
El tratamiento de los errores durante el proceso de análisis sintáctico debe cumplir al menos los
siguientes requisitos:
- reportar la presencia de los errores clara y precisamente
- recuperarse de los errores lo suficientemente rápido como para ser capaz de detectar los
errores siguientes
- no demorar significativamente el procesamiento de los programas correctos

El cumplimiento efectivo de estos requisitos presenta algunas dificultades. Afortunadamente, la


mayoría de los errores son simples y un mecanismo sencillo de tratamiento de errores es
suficiente. Sin embargo, en algunos casos el error puede haber ocurrido mucho antes del punto
donde se detectó su presencia, y la naturaleza precisa del error puede ser muy difícil de deducir.
Algunos métodos de análisis sintáctico, como los métodos LL y LR, detectan el error tan pronto
como es posible. Ellos utilizan una propiedad conocida como prefijo viable, que significa que
detectan que ha ocurrido un error tan pronto como ven que un prefijo de la cadena de entrada no
es prefijo de ninguna de las subcadenas del lenguaje.
¿Cómo debe reportar la presencia de un error el manipulador de errores? Debe indicar, por lo
menos, el lugar del programa fuente donde se detectó el error, puesto que es muy probable que
haya ocurrido cerca, en alguno de los tokens anteriores. Una estrategia común que se emplea por
muchos compiladores es mostrar la línea donde se detectó el error, indicando la posición exacta
donde ocurrió.
Una vez que se ha detectado el error, ¿cómo debe recuperarse el parser? Existen algunas
estrategias para la recuperación de los errores durante el análisis sintáctico:
- Modo pánico
- Nivel de frase
- Producciones de error
- Corrección global
(Estudiar en el libro del “Dragón”)

Gramáticas Libres de Contexto (GLC)


Las gramáticas libres de contexto constituyen la mejor vía para la descripción sintáctica de los
lenguajes de programación. Existen diversas razones que justifican tal afirmación:
- Las gramáticas brindan una especificación sintáctica precisa de los lenguajes de
programación.
- Para ciertas clases de gramáticas pueden construirse analizadores sintácticos eficientes
que determinan si el programa fuente está bien escrito. El proceso de construcción del
parser puede además, revelar ambigüedades sintácticas no detectadas en la fase de diseño
del lenguaje.
- Una gramática bien diseñada puede ayudar a la traducción del programa fuente en código
objeto y a la detección de errores.
- Nuevas construcciones pueden añadirse al lenguaje de forma fácil si este está descrito a
través de una gramática.
De las cuatro clases de gramáticas de la jerarquía de Chomsky, las GLC son las más importantes
desde el punto de vista de la aplicación en los lenguajes de programación y compiladores. Una
GLC puede ser utilizada para especificar la estructura sintáctica de un lenguaje de
programación y además constituye la base para los diferentes esquemas de traducción.
Durante el proceso de compilación podemos utilizar la estructura sintáctica que una GLC le
impone a un programa para determinar su traducción. Utilizando las reglas sintácticas y la
secuencia de producciones usadas para derivar una cadena podemos obtener la estructura
sintáctica de la cadena (o programa). O sea, el analizador sintáctico de un compilador es un
mecanismo que trata de determinar si una cadena puede ser derivada a través de una GLC.

Definición formal de Gramática Libre de Contexto


Una gramática libre de contexto es un generador de lenguajes.
Se define de la siguiente manera:

G = ( N, T, P, S )

Donde:
N: conjunto de símbolos No-terminales. Utilizados para generar las palabras o cadenas
del lenguaje
T: Conjunto de símbolos terminales. Representan el alfabeto del lenguaje, aunque en la
práctica, en la implementación de un compilador, son los Tokens del lenguaje.
P: Conjunto de reglas de producción. Es el centro de la gramática. Son reglas que
describen cómo son generadas las cadenas. Una regla de producción es un par de cadenas
tomadas de:
N x (N ∪ T)*
Al primer elemento se le llama símbolo de la parte izquierda y es un símbolo No
terminal. El segundo elemento, llamado, parte derecha, es una cadena formada por
símbolos terminales y/o no terminales ó incluso vacía (ε ). Usualmente las reglas de
producción se representan:
A -> α Y se lee: A produce α
S: Es el símbolo inicial ó axioma de la gramática. Es un símbolo No terminal.

Ejemplo 1:
G = ( { S }, { 0, 1 }, P, S )
P:
S -> 0 S 1
S -> ε
En este ejemplo:
-Hay un solo símbolo no terminal (S)
-Hay dos símbolos terminales (0 y 1)
-Hay dos reglas de producción
-El símbolo inicial es S.

Conceptos relacionados

Forma sentencial: Una forma sentencial es un tipo especial de cadena y se define como
sigue:
a) S (el símbolo inicial) es una Forma Sentencial (FS)
b) Si αBγ es una FS y además B -> β ∈ P entonces αβγ es también una FS

Sentencia: Es una una forma sentencial que sólo contiene símbolos terminales.

Derivación: Aplicación sucesiva del concepto de Forma Sentencial, partiendo de S hasta


llegar a una Sentencia.
S ⇒k w
Donde k es la cantidad de elementos en la secuencia

Para el ejemplo ejemplo 1, podríamos realizar la siguiente derivación:


S ⇒ 0S1 ⇒ 00S11 ⇒ 000S111 ⇒ 000111

Las tres primeras sustituciones se realizaron aplicando la primera regla de producción.


Para la última sustitución se aplicó la regla dos.
Podemos decir entonces:
S ⇒4 000111 lo cual significa que la cadena 000111 es generada por la gramática y por
tanto pertenece al lenguaje que esa gramática define.

Lenguaje generado por una gramática:

L(G) = { w | S ⇒* w donde w ∈ T* }

En otras palabras, el lenguaje generado por una gramática es el conjunto de todas las
cadenas que se pueden obtener a partir de derivaciones.

Ejemplo2:
G = ( { E, T, F }, { +, *, (, ), id }, P, E )
P:
E -> E + T
E -> T
T -> T * F
T -> F
F -> ( E )
F -> id

En esta gramática hay tres símbolos no terminales, cinco terminales, seis reglas de
producción y el símbolo inicial es E.

Ejercicios
1) Realizar la derivación de las cadenas:
a) id + id
b) id * id
c) id
d) id + id * id
e) ( id + id ) * id
f) ( id * id )

Вам также может понравиться