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

1

Pgina 1
ANLISIS SINTCTICO ASCENDENTE
ANLISIS SINTCTICO EN PROCESADORES DE LENGUAJE
Universidad de Oviedo - Departamento de Informtica
Escuela Politcnica Superior de Ingeniera
TEMA 6
Pgina 2
Objetivos
Introducir las gramticas LR
Conocer la estrategia de Reduccin-Desplazamiento
Estudiar la arquitectura de un analizador LR
Comprender el algoritmo de anlisis LR
Tratar los errores sintcticos
Conocer los diferentes generadores de analizadores sintcticos
2
Pgina 3
Analizadores LR
Estructura y funcionamiento de un analizador LR
Clasificacin de gramticas
Tratamiento de errores sintcticos
Generadores de Procesadores de Lenguajes
Contenido
Introduccin
Pgina 4
Introduccin
Clasificacin mtodos anlisis sintctico
A. Descendentes A. Ascendentes
No Direccionales
Direccionales
No deterministas
Predice / Concuerda
1 Profundidad
1 Anchura
Desplaza / Reduce
1 Profundidad
1 Anchura
Deterministas
Predice / Concuerda
Gramtica LL(k) - LL(1)
Algoritmo de Unger Algoritmo CYK
Desplaza / Reduce
Gramtica LR(k)
LR(0), SLR(1), LALR(1)
3
Pgina 5
Introduccin
Caractersticas
El anlisis sintctico descendente construye el rbol a partir del
nodo raz, llegando a los nodos hoja en un recorrido preorden en
profundidad.
Un analizador sintctico ascendente (botton-up parser) es
aqul que construye el rbol sintctico comenzando por los nodos
hoja, hasta llegar al nodo raz.
Si se alcanza el smbolo no terminal inicial, el programa ser
sintcticamente correcto.
Un analizador sintctico (parser) descendente decide qu
produccin expandir (produccin)
Un analizador sintctico (parser) ascendente analiza la aparicin de
la parte derecha de una produccin y la sustituye por su parte
izquierda (reduccin).
Pgina 6
Introduccin
Derivaciones
Una derivacin es una sustitucin de un no terminal de una cadena
por la parte derecha de una de sus producciones.
Si la sustitucin se realiza del no terminal ms a la derecha
(rightmost) ser una derivacin por la derecha. Si la sustitucin se
realiza del no terminal ms a la izquierda (leftmost) se dice que es
una derivacin por la izquierda.
El orden en el que se expanden las producciones de un analizador
sintctico descendente es el descrito por las derivaciones por la
izquierda.
Un analizador sintctico ascendente realiza sus reducciones en el
orden inverso al descrito por las derivaciones por la derecha.
4
Pgina 7
Introduccin
Derivaciones: Ejemplo
(1) exp exp + term
(2) | exp - term
(3) | term
(4) term CTE-ENTERA
Sentencia: 3 + 45
exp
L
exp + term
exp
exp + term
term
CTE-ENTERA
CTE-ENTERA

L
term + term

L
CTE-ENTERA + term

L
CTE-ENTERA + CTE-ENTERA
Orden en el que se crean los nodos del rbol sintctico
Analizador
Sintctico
Descendente
Pgina 8
Introduccin
Derivaciones: Ejemplo
(1) exp exp + term
(2) | exp - term
(3) | term
(4) term CTE-ENTERA
Sentencia: 3 + 45
Orden en el que se crean los nodos del rbol sintctico
Analizador
Sintctico
Ascendente
exp + term
R
term + CTE-ENTERA
R
CTE-ENTERA + CTE-ENTERA
exp + CTE-ENTERA
R
exp
R
exp
exp + term
term
CTE-ENTERA
CTE-ENTERA
5
Pgina 9
Anlisis Sintctico Ascendente
Analizadores LR
Los analizadores sintcticos ascendentes son comnmente
denominados (parsers) LR.
La L indica que la entrada se procesa de izquierda a derecha (left).
La R indica que el analizador se basa en derivaciones ms a las
derecha (rightmost) en orden inverso.
El modo en el que se reducen las producciones es menos intuitivo,
pero:
Permiten procesar un mayor nmero de lenguajes que los
analizadores LL.
Su construccin manual es mucho ms compleja que la de los LL, por
lo que es necesario el empleo de herramientas.
Pgina 10
Anlisis Sintctico Ascendente
Analizadores LR
Los analizadores sintcticos ascendentes reconocen un lenguaje
realizando dos operaciones sobre una pila.
Cargar (shift): Insertan en la pila el terminal de la entrada que se
est analizando.
Reducir (reduce): Si la cadena de la parte superior de la pila
coincide con la parte derecha de una produccin, sustituye todos los
smbolos de la parte derecha por el no terminal de la izquierda
(reduccin).
Con este proceso
La pila inicialmente est vaca.
El programa ser correcto si se alcanza el final del programa
(fichero) y el nico smbolo gramatical de la pila es el smbolo
inicial.
6
Pgina 11
Anlisis Sintctico Ascendente
Analizadores LR: Ejemplo
(1) exp exp + term
(2) | term
(3) term term * factor
(4) | factor
(5) factor CTE-ENTERA
Sentencia: 3 * 45
exp
CTE-ENTERA
Reduccin
(5)
factor
Reduccin
(4)
term
*
CTE-ENTERA
Reduccin
(5)
term
*
factor
term
Reduccin
(3)
Reduccin
(2)
Pgina 12
Analizadores LR
Anlisis ascendente con retroceso
En un punto del proceso de anlisis sintctico ascendente pueden ser
factibles la ambas acciones de cargar y reducir.
El no saber optar por la accin correcta, obliga a implementar un
mecanismo de retroceso (backtraking).
El implementar un parser con retroceso tiene una serie de
inconvenientes:
La utilizacin de backtracking introduce una elevada prdida de
rendimiento.
En los compiladores de una nica pasada, el retroceso afecta
gravemente a otras fases (semntico, tabla de smbolos o generacin
de cdigo intermedio).
7
Pgina 13
Analizadores LR
Anlisis ascendente con retroceso
(1) exp exp + term
(2) | term
(3) term term * factor
(4) | factor
(5) factor CTE-ENTERA
Sentencia: 3 * 45
term
Carga
Reduccin
(1)
Esta opcin
requiere
retroceso
exp
term
*
Ejemplo
Reduccin
(5)
Reduccin
(4)
CTE-ENTERA factor
Pgina 14
Analizadores LR
Anlisis ascendente sin retroceso
Debido a los problemas causados por el retroceso, se emplean distintas
tcnicas de procesamiento ascendente para evitarlo.
Los analizadores capaces de evitar el retroceso se basan en analizar un
conjunto determinado de tokens por leer (lookahead) de la entrada.
En el contexto en el que un parser tenga las dos opciones vlidas de
carga y reduccin, analizar un conjunto de los tokens siguientes
para tomar dicha decisin de un modo determinista.
Si un analizador ascendente reconoce cualquier programa de un
lenguaje sin emplear retroceso, leyendo como mximo los k tokens
siguientes de la entrada, denomina LR(k).
Si no se especifica un lookahead, por omisin es 1, es decir un
parser LR es LR(1)
8
Pgina 15
Analizadores LR
Gramticas LR
Las gramticas LR son un subconjunto de las gramticas libres de
contexto.
Una gramtica LR(k) es aquella que
1. Puede ser procesada por un parser ascendente.
2. De un modo predictivo, sin utilizar retroceso.
3. Empleando para ello un mximo de k tokens por leer de la entrada
(lookahead).
Las ventajas de las gramticas LR son
Pueden ser procesadas de un modo eficiente
Evitan la necesidad de tener que deshacer acciones realizadas con
otras fases, por culpa del retroceso
Reconocen un nmero mayor de lenguajes que las gramticas LL
Pgina 16
Analizadores LR
Recursividad en gramticas LR
Una gramtica LR puede ser recursiva a izquierdas o a derechas.
Sin embargo, la recursividad a izquierdas requiere un menor
tamao de la pila (menor memoria en compilacin).
(1) l l ID
(2) | ID
ID l
ID
l l
ID
l l
ID
l l
ID
ID
ID
ID
ID
ID
ID
ID
ID
ID
l
ID
ID
ID
l
ID
ID
l
ID l
(1) l ID l
(2) | ID
Entrada: ID ID ID ID
9
Pgina 17
Estructura y funcionamiento de un analizador LR
Estructura de un parser LR
Un analizador sintctico LR consta de:
Una pila de anlisis en la que se van cargando los estados por los
que pasa el analizador y los smbolos de la gramtica que se van
leyendo.
Una tabla de anlisis que indica cmo modificar los valores de la
pila ante los tokens de entrada.
Un programa analizador, encargado de reconocer el programa de
entrada ante las indicaciones de la tabla.
La nica parte de variable de un tipo a otro de analizador es la tabla de
anlisis.
Programa
Analizador
3 * 45 $
Pila:
Entrada:
Tabla de
Anlisis
Final de Fichero
Pgina 18
Estructura y funcionamiento de un analizador LR
Tabla de anlisis
Las tablas de anlisis varan en funcin del tipo de analizador.
Las ms completas poseen
Tantas filas como estados requiera el autmata de pila.
Columnas de accin: una por cada smbolo terminal de la
gramtica (contando $)
Columnas de salto: una por cada smbolo no terminal
Las celdas de las columnas de accin pueden tener los siguientes
valores:
Cargar (shift) S: Siendo S un estado
Reducir (reduce) N: Siendo N una produccin
Aceptar: Programa reconocido
Error: Programa errneo
Las celdas de las columnas de salto poseen un nmero de estado
10
Pgina 19
Estructura y funcionamiento de un analizador LR
Algoritmo de anlisis
Inicialmente la pila posee el estado 0
Repetir
Se consulta accin[s,t] siendo s el estado en el tope de la pila y t el
token actual
Si es Cargar X, se introduce en la pila X seguido de t
avanzando un token de la entrada
Si es Reducir R,
Se desapilan la parte derecha de la produccin R (y sus
estados)
Se apila A, siendo A la parte izquierda de la produccin R
Se apila el estado salto[u,A], siendo u el ltimo estado de
la pila
Si es aceptar, el programa ha sido reconocido
Si es error, existe un error sintctico en el programa
Pgina 20
Estructura y funcionamiento de un analizador LR
Ejemplo
a+b$
(0) S exp $
(1) exp exp + term
(2) | exp - term
(3) | term
(4) term ID
R2 R2 R2 R2 7
R1 R1 R1 R1 6
7 S3 5
6 S3 4
R4 R4 R4 R4 3
R3 R3 R3 R3 2
Aceptar S5 S4 1
2 1 S3 0
term exp $ - + ID Estado
Salto Accin
0
Sentencia: a + b $
3
ID
0
2
term
0
1
exp
0
4
+
1
exp
0
3
ID
4
+
1
exp
0
6
term
4
+
1
exp
0
1
exp
0
$
+b$ +b$ +b$ b$ $ $
Pila del Parser:
s3
Tabla del Parser:
r4 r3 s4 s3 r4 s3
aceptar
Gramtica:
11
Pgina 21
Clasificacin de gramticas
Gramticas LR frente a LL
Analizaremos las diferencias entre las gramticas LR y LL en funcin
de un conjunto de criterios
1. Sencillez de implementacin: Los analizadores LL son sencillos
de implementar manualmente; los LR son complejos y requieren
herramientas.
2. Sencillez de funcionamiento (depuracin): El proceso
descendente (derivaciones por la izquierda) es ms intuitivo y fcil
de depurar que el ascendente (en orden inverso, derivaciones por
la derecha).
3. Expresividad: Toda gramtica LL(1) es LR(1). La expresividad
de las gramticas LR es, en cualquier caso, mayor que las LL.
Pgina 22
Clasificacin de gramticas
Gramticas LR frente a LL
4. Reglas semnticas: Los esquemas de traduccin, gramticas
atribuidas y definiciones dirigidas por sintaxis de gramticas LR
emplean atributos sintetizados; las que emplean LL ofrecen tanto
sintetizados como heredados.
5. Recuperacin de errores: Los analizadores LL poseen
informacin de las construcciones que se estn reconociendo,
mientas que los LR poseen aqullas ya reconocidas; la
informacin de los LL es ms til para recuperar los errores.
6. Tamao de las tablas de anlisis: Ficher y Leblanc demostraron
que los compiladores LR requieren tablas de aproximadamente el
doble tamao que los compiladores LL.
12
Pgina 23
Clasificacin de gramticas
Gramticas LR
Existe una clasificacin de analizadores ascendentes LR
LR(0): Una gramtica LR(0) es aqulla gramtica LR (que se
puede procesar ascendentemente sin retroceso) sin emplear ningn
lookahead.
Las gramticas anteriores representan un subconjunto muy
pequeo de las gramticas libres de contexto.
LR(1): Gramticas LR que realizan las cargas y reducciones en la
pila del procesador, teniendo en cuenta el token actual de la
entrada (1 lookahead) y almacenando la historia de las reducciones
previas.
Las gramticas LR(1) describen la mayora de los lenguajes de
programacin, pero poseen una tabla de anlisis de un tamao
elevado.
Pgina 24
Clasificacin de gramticas
Gramticas LR
SLR(1), Simple LR: Gramticas LR que realizan las cargas y
reducciones en la pila del procesador, teniendo nicamente en
cuenta el token actual de la entrada (1 lookahead).
Los analizadores SLR(1) se construyen de igual que los LR(0),
pero teniendo en cuenta la informacin del token de la entrada
para realizar las reducciones.
LALR(1) Look Ahead LR: Subconjunto (muy cercano) de las
gramticas LR(1) en el que se simplifican, en su reconocimiento,
el nmero de estados que nicamente varan en su lookahead.
Las gramticas LALR(1) son muy cercanas a las LR(1), pero
reducen sensiblemente el tamao de su tabla de anlisis.
13
Pgina 25
Clasificacin de gramticas
Clasificacin de gramticas LR
Como se ha visto, las gramticas LL(1) son un subconjunto de
las LR(1)
Del mismo modo, LR(0) SLR(1) LALR(1) LR(1)
LR(0)
SLR(1)
LALR(1)
LR(1)
LL(1)
Pgina 26
Clasificacin de gramticas
Gramticas LR(k)
Las gramticas y reconocedores SLR(1), LALR(1) y LR(1)
pueden ser extendidas a gramticas y reconocedores SLR(k),
LALR(k) y LR(k).
Utilizarn k tokens seguidores e iniciales de los smbolos
gramaticales, comparndolos con los k tokens por leer
(lookahead).
Los analizadores SLR(k) tomarn las decisiones de cargar o
reducir en funcin de los k tokens por leer en la entrada
(lookahead)
Los analizadores LR(k) crearn adems sus estados teniendo en
cuenta los k tokens directores de los smbolos reducidos.
Los analizadores LALR(k) sern el resultado de simplificar el
reconocedor LR(k).
14
Pgina 27
Clasificacin de gramticas
Clasificacin y uso de gramticas LR(k)
Ampliando el valor de k se amplia la expresividad de la
gramtica:
LR(0) LR(1) LR(k) LR(k+1)
SLR(1) SLR(2) SLR(k) SLR(k+1)
LALR(1) LALR(2) LALR(k) LALR(k+1)
Recordemos que, adicionalmente, se cumple que
SLR(k) LALR(k) LR(k)
A nivel prctico, estas tcnicas no han sido empleadas:
Elevado incremento del tamao.
Prdida de rendimiento en el procesamiento.
Poca ampliacin de su expresividad, no necesaria.
Pgina 28
Tratamiento de errores sintcticos
Recuperacin de errores
Un error sintctico se produce cuando la secuencia de tokens
recibidos del analizador lxico no son reconocidos por la
gramtica del lenguaje.
En este caso, se dice que el programa no es sintcticamente
correcto.
Existen otro tipo de errores de un programa como los errores
semnticos y lxicos.
Ante un error sintctico, un procesador de lenguaje deber:
Indicar la posicin del error (archivo, lnea y carcter).
Proporcionar un mensaje de error al programador, indicando
informacin para corregirlo.
Reanudar el proceso de anlisis.
15
Pgina 29
Tratamiento de errores sintcticos
Recuperacin de errores
Cuando se detecta un error sintctico, es deseable que se pueda
continuar procesando el programa.
Esta necesidad conlleva la implementacin de algn mecanismo de
recuperacin o reparacin de errores.
Recuperacin de errores: El analizador debe seguir
procesando el programa descartando la entrada errnea.
Dependiendo de la calidad de la recuperacin, los siguientes
errores podrn ser reales o errores en cascada (producidos por una
mala recuperacin) f(a,b c) Puede producir un error en cascada
analizando )
Pgina 30
Tratamiento de errores sintcticos
Reparacin de errores
Reparacin de errores: Modificar la entrada para conseguir
construcciones vlidas donde exista un error sintctico.
Este proceso suele estar basado en modificar o aadir la secuencia
de tokens bajo anlisis, para poder seguir procesando.
Por ejemplo, f(a,b c)
Puede aadirse una , y seguir el proceso correctamente.
O puede aadirse un ; y generar errores en cascada.
Los procesadores que implementan reparacin de errores.
Informan al programador de los errores detectados.
Generan un programa con el resultado de las reparaciones
efectuadas.
Permiten ejecutar ste, aunque pueda suponer un comportamiento
no deseado.
16
Pgina 31
Tratamiento de errores sintcticos
Recuperacin en modo de alarma
En un analizador ascendente, un error sintctico es detectado
cuando se llega a una entrada error (en blanco) de la tabla de
anlisis.
La recuperacin de errores en modo de alarma consiste en ir
eliminando smbolos gramaticales.
De la pila del analizador y/o
De la entrada
Una implementacin muy empleada en parsers LR es
Extraer estados de la pila hasta encontrar uno que tenga un valor
en la tabla salto (existe una reduccin).
Si existe una accin legal con el token de entrada, reanudar el
proceso.
Si no existe una accin legal, eliminar tokens hasta que se d esta
situacin (y reanudar el proceso).
Pgina 32
Tratamiento de errores sintcticos
Producciones de error
El manejo de errores mediante la especificacin de
producciones de error implica aumentar la gramtica del
lenguaje con producciones que reconocen los errores ms
comunes.
Este mecanismo se puede utilizar tanto para recuperacin como
para reparacin de errores
Este es el mecanismo empleado por la herramienta Yacc
Ejemplo: produccin de error que repara la supresin de
un punto y coma
sentencias sentencias sentencia puntoycoma
puntoycoma ;
|
17
Pgina 33
Generadores de Procesadores de Lenguajes
Un generador de procesadores es un programa que transforma una
especificacin en un procesador para el lenguaje de programacin
descrito en la especificacin.
Metalenguaje
Generador de
Procesadores
Procesador
de Lenguaje
Programa Fuente
Programa
Objeto
Tiempo de generacin
Tiempo de compilacin
Tiempo de ejecucin
Cuando el generador es ejecutado. A partir de una entrada
(metalenguaje), se obtiene como salida el procesador
de lenguaje correspondiente.
Cuando la salida obtenida en tiempo de generacin
es ejecutada. Se parte de un programa fuente y se traduce a un programa objeto .
Cuando el cdigo generado por el procesador de lenguaje es ejecutado
por el sistema operativo de la mquina .
Pgina 34
Generadores de procesadores de lenguajes
Clasificacin

Fase de Anlisis
Analizador
Lxico
Analizador
Sintctico
Analizador
Semntico
Optimizador

Generacin de
cdigo
Programa
Fuente
Tokens rbol
Sintctico
Cdigo
Intermedio
Programa
Objeto
Generador de
Analizadores
Sintcticos
Generador de
Analizadores
Lxicos
Generador de
Analizadores
Semnticos
Generador de
Generadores de
Cdigo
Generador Fase de Anlisis
Expresiones
regulares
Gramticas
libres de contexto
Gramticas
Atribuidas
Descripcin del
Cdigo Intermedio y
Cdigo Mquina
Metalenguajes
18
Pgina 35
Generadores de procesadores de lenguajes
Generador de analizadores sintcticos
Un generador de analizadores sintcticos es un programa que
convierte una especificacin sintctica de un lenguaje
(gramtica) en un analizador sintctico del lenguaje especificado
La principal ventaja de las gramticas LR es son capaces de
representar sintcticamente la prctica totalidad de lenguajes de
programacin. Su principal inconveniente es la complejidad de
su implementacin manual.
Su desarrollo requiere el uso de herramientas.
La utilizacin de herramientas de generadores de analizadores
sintcticos.
Reduce la complejidad y tiempo de implementacin.
Aumenta la calidad del analizador.
Facilita la mantenibilidad del procesador.
Pgina 36
Generadores de procesadores de lenguajes
Generador de analizadores sintcticos
El proceso que se sigue para construir un analizador mediante
una herramienta es:
1. Describir su gramtica mediante una notacin especfica.
2. Procesar sta con la herramienta, obteniendo el cdigo fuente del
analizador (parser).
3. Compilar el analizador, junto con el resto de fases del compilador.
Gramtica
Generador
De Parsers
Cdigo Fuente
del Parser
Compilacin
Procesador
de Lenguaje
A. Lxico
A. Semntico
G. Cdigo

19
Pgina 37
Generadores de procesadores de lenguajes
Generador de A. Ascendentes Yacc
Yacc (Yet Another Compiler Compiler) es un generador de
analizadores sintcticos ascendentes LALR(1)
Distintas versiones:
Bison: Versin libre GNU.
PCYacc: Versin comercial de Abraxas.
BYacc/J: Versin libre de la Universidad de Berkeley con
generacin del parser en C y Java.
Su archivo de entrada:
Describe la gramtica en una notacin BNF.
Permite ejecutar acciones (rutinas semnticas) en cada reduccin
realizada por el parser.
El proceso de reconocimiento de un programa de entrada est
encapsulado en la funcin yyparse().
Pgina 38
Generadores de procesadores de lenguajes
Generador de A. Ascendentes Yacc

Generador de
Analizadores Sintcticos
Yacc
Prog := fun {S$..
fun := ID {$1... }
stmt := ...
Anlisis Ascendente
Tablas de anlisis
LALR(1)
Especificacin
Gramatical
+
Cdigo de Usuario
Fichero
(Cdigo en C)
s,x
s,x
Especificacin
Lxica
Generador de
Analizadores Lxicos
Lex
Programa.c
(AFD)
Arquitectura de Lex /Yacc
20
Pgina 39
Generador de analizadores ascendentes Yacc
Interaccin con el Analizador Lxico
El generador de analizadores sintcticos Yacc fue diseado para
interactuar con el generador de analizadores lxicos Lex
La interaccin se realiza del siguiente modo
El lex crea un analizador lxico encapsulado en la funcin yylex()
Yacc pedir al analizador lxico el siguiente token invocando a
yylex()
El analizador lxico deber escribir en la variable global yylval el
atributo (si existe) del token antes de devolverlo.
El analizador sintctico acceder a la variable global yylval
cuando requiera conocer el atributo del token devuelto por el
lxico.
Analizador
Lxico
Analizador
Sintctico
yylval
1) yylex()
2) yylval=lexema 4) Acceso a yylval
3) return TOKEN
Pgina 40
Generador de analizadores ascendentes Yacc
Interaccin con el Analizador Lxico
La generacin del interfaz con el analizador lxico (lex) se obtiene con
la opcin d
Se crea un archivo yytab.h (sintac.h) con
Los valores numricos de los tokens.
La definicin de los valores de la unin del lexema.
La declaracin de la variable global yylval (atributo).
El lxico deber incluir este archivo cabecera.
Yacc genera una funcin yyparse() que devuelve un int (0 si el
anlisis fue correcto).
sintac.y YACC
sintac.c
(yytab.c)
sintac.h
(yytab.h)
lexico.l LEX
lexyy.c
(lexico.c)
Compilador
C/C++
Opcin -d
include
yylex()
yyparse()
Programa
Fuente
Programa
Objeto
Mi-ejecutable
21
Pgina 41
Generador de analizadores ascendentes Yacc
Estructura del fichero de entrada
Un fichero Yacc consta de tres partes separadas por %%
1. Declaraciones: Permite incluir cdigo C y las declaraciones
yacc de tokens y otros elementos.
2. Descripcin de la Gramtica: Se describe la gramtica en
notacin BNF y las acciones a realizar en cada reduccin
(rutinas semnticas), expresadas en C.
3. Cdigo auxiliar: Cualquier cdigo C adicional necesario.
Declaraciones
%%
Descripcin de la Gramtica
%%
Cdigo Auxiliar
Pgina 42
Generador de analizadores ascendentes Yacc
Seccin de declaraciones
La seccin de declaraciones se subdivide en dos secciones.
Declaraciones de cdigo C: Cualquier declaracin de
funcin, variable o tipo necesaria posteriormente.
Se incluye entre %{ y %}
Declaracin de Yacc: Descripcin de los smbolos
gramaticales del Yacc.
%{
#include <stdio.h>
#include <string.h>
int contador=0;
%}
...
%%
...
22
Pgina 43
Generador de analizadores ascendentes Yacc
Declaraciones de Yacc
La seccin de declaraciones yacc consta de los siguientes
apartados:
Seccin %union: Indica el conjunto de posibles valores de
los atributos de los smbolos gramaticales.
Generar una union en C
Si slo hay un posible valor, se puede definir mediante la
macro YYSTYPE. #define YYSTYPE tipo
Si no se define ningn tipo, por defecto el tipo de la pila ser entero
%union {
int entero;
float real;
char caracter;
char cadena[80];
}
typedef union {
int entero;
float real;
char caracter;
char cadena[80];
}YYSTYPE;
Pasa al fichero
de salida
Pgina 44
Generador de analizadores ascendentes Yacc
Declaraciones de Yacc
Seccin %token: Identifica el conjunto de tokens empleados en la gramtica
Pueden ir separados por blancos o comas
Es posible indicar el tipo de su atributo, anteponiendo <tipo> a los tokens
%token MAYORIGUAL MENORIGUAL DISTINTO IGUALDAD
%token IF ELSE WHILE INTEGER REAL CHAR
%token <entero> CTE_ENTERA
%token <caracter> CTE_CARACTER
%token <real> CTE_REAL
%token <cadena> IDENTIFICADOR CTE_STRING
Seccin %type: Indica, cuando sea necesario, el tipo de los atributos de los
smbolos no terminales
La sintaxis es igual que la de %token
%type <entero> expresion termino factor
23
Pgina 45
Generador de analizadores ascendentes Yacc
Declaraciones de Yacc
Seccin %start: Esta seccin es opcional. Indica cul es el
smbolo inicial de la gramtica.
Por omisin se asumir que el no terminal inicial es el primero
que aparezca en la descripcin de la gramtica
%start programa
Seccin de precedencia y asociatividades (%left, %right y
%nonassoc): En esta seccin se establece la precendencia y
asociatividad de los tokens del lenguaje.
Evita las ambigedades de las gramticas.
No est restringida nicamente a los operadores.
%right '='
%left '+' '-'
%left '*' '/' '%'
%nonassoc IF
%nonassoc ELSE
Pgina 46
Generador de analizadores ascendentes Yacc
Descripcin de la gramtica
La notacin que se utiliza es BNF (Backus-Naur Form) con la
siguiente sintaxis:
Los smbolos terminales (tokens) son enteros. stos pueden venir
representados como
Macros creadas por su declaracin en %token
(MAYORIGUAL MENORIGUAL DISTINTO IGUALDAD IF
ELSE)
Constantes carcter en C, ya que promocionan a entero: '+' '-'
'*' '/' '%' '='
Los smbolos no terminales son identificadores
El smbolo de la produccin () ser :
Al finalizar la produccin deber escribirse un punto y coma
Produccin:
<expr> <expr> + <expr>
| <expr> >= <expr>
| CTE
|
Sintaxis Yacc:
expr: expr '+' expr
| expr MAYORIGUAL expr
| CTE
| /* vacio */
;
24
Pgina 47
Generador de analizadores ascendentes Yacc
Cdigo Auxiliar
Es la ltima seccin de un archivo Yacc.
Se puede introducir todo el cdigo C que el programador estime
oportuno para implementar el analizador sintctico.
Este cdigo se copia al final del archivo C que genera el yacc.
Si posee errores, stos sern detectados por el compilador de C, y
no por Yacc.
Una de las funciones que ha de implementarse, es la funcin que
controla los mensajes de error.
void yyerror(char *)
Esta funcin ser llamada por Yacc cada vez que se detecte un
error sintctico.
Se deber informar al programador de la naturaleza del error y la
lnea en la que se produjo.
El parmetro recibido es una descripcin del error.
Pgina 48
Generador de analizadores ascendentes Yacc
Recursividad
Como se mostr anteriormente, una gramtica LR puede ser
recursiva a izquierdas o a derechas.
Sin embargo, la recursividad a izquierdas requiere un menor
tamao de la pila (menor memoria en compilacin).
suma : NUM
|suma '+' NUM
;
Entrada: NUM + NUM + NUM
NUM
+
8 7 6 5 4 3 2 1
suma suma
+
suma
NUM
+
suma suma
+
suma
NUM
suma
suma : NUM
|NUM '+' suma
;
NUM
+
8 7 6 5 4 3 2 1
NUM
+
NUM
NUM
NUM NUM
+
NUM
suma
suma
+
NUM
NUM
+
+
NUM
+
NUM
+
NUM
+
suma
25
Pgina 49
Generador de analizadores ascendentes Yacc
Ejemplo
%{
#include <stdio.h>
#include <string.h>
%}
%token CTE_ENTERA
%%
programa: programa exp ';'
| exp ';'
;
exp: exp '+' termino
| termino
;
termino: termino '*' factor
| factor
;
factor: '(' exp ')'
| CTE_ENTERA
;
%%
extern unsigned yylineno;
void yyerror(char *s) {
printf("Error en linea %d: %s",
yylineno,s);
}
int main() {
return yyparse();
}
%{
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "yytab.h"
unsigned yylineno=1;
%}
digito [0-9]
%%
[ \t]+ ; // * Se ignora
[\n] yylineno++;
{digito}+ return CTE_ENTERA;
"+"|"-"|"*"|
"/"|"%"|";" return yytext[0];
. ;
%%
Descripcin en Yacc de un reconocedor sintctico de expresiones
aritmticas (separadas por ;) con los operadores +, * y parntesis
Lxico: Sintctico:
Pgina 50
Generador de analizadores ascendentes Yacc
Ambigedad
Dada la siguiente gramtica en yacc:
exp: exp '+' exp
| exp '*' exp
| '(' exp ')'
| CTE_ENTERA
;
Obtenemos cuatro conflictos carga/reduccin (shift/reduce) al
procesarla en Yacc
Esto se produce porque la gramtica es ambigua
Por ejemplo, ante la entrada 1 + 2 + 3 tenemos
exp
exp + exp
exp
CTE-ENTERA
CTE-ENTERA + exp
CTE-ENTERA
exp
exp + exp
exp
CTE-ENTERA
CTE-ENTERA + exp
CTE-ENTERA
26
Pgina 51
Generador de analizadores ascendentes Yacc
Ambigedad
exp: exp '+' exp
| exp '*' exp
| '(' exp ')'
| CTE_ENTERA
;
La descripcin de dicho conflicto se especifica en el archivo
yy.lrt (con la opcin v ):
+ CONFLICTS:
? sft/red (shift & new state 4, rule 1) on +
exp : exp + exp^ (rule 1)
La ambigedad se produce porque la gramtica no especifica la
precedencia y asociatividad de los operadores:
Si se realiza una reduccin, es asociativo a izquierdas
Si se realiza una carga, es asociativo a derechas
Pgina 52
Generador de analizadores ascendentes Yacc
Ambigedad
Yacc permite utilizar la gramtica ambigua, puesto que es ms
compacta que la que explicita la asociatividad:
El mecanismo que emplea Yacc es poder definir la asocitividad
y precedencia de los operadores.
Gramtica Ambigua:
exp: exp '+' exp
| exp '*' exp
| '(' exp ')'
| CTE_ENTERA
;
Gramtica No Ambigua:
exp: exp '+' termino
| termino
;
termino: termino '*' factor
| factor
;
factor: '(' exp ')'
| CTE_ENTERA
;
27
Pgina 53
Generador de analizadores ascendentes Yacc
Ambigedad
La asociatividad de operadores se especifica empleando %left o
%right:
%token CTE_ENTERA
%left '+'
%%
exp: exp '+' exp
| exp '*' exp
| '(' exp ')'
| CTE_ENTERA
;
Se elimina as uno de los conflictos, pero todava tenemos
El conflicto de asociatividad del operador *
Dos conflictos de precedencia: La entrada 1+2*3 genera dos
posibles rboles:
exp
exp * exp
exp
CTE-ENTERA
+ exp
CTE-ENTERA
exp
exp + exp
exp
CTE-ENTERA
CTE-ENTERA * exp
CTE-ENTERA
CTE-ENTERA
Pgina 54
Generador de analizadores ascendentes Yacc
Ambigedad
La precedencia de operadores se especifica en funcin del
orden en el que pongamos su asociatividad.
Una aparicin posterior en el archivo implica una mayor
precedencia.
Por tanto, en el ejemplo debemos:
Describir la asociatividad del producto (left)
Describir la precedencia menor de la suma (anterior) respecto al
producto (posterior)
%token CTE_ENTERA
%left '+'
%left *'
%%
exp: exp '+' exp
| exp '*' exp
| '(' exp ')'
| CTE_ENTERA
;
Con esta especificacin, la gramtica no poseer ningn
conflicto carga / reduccin
28
Pgina 55
Generador de analizadores ascendentes Yacc
Semntica
Las reglas de produccin slo representan la sintaxis del
lenguaje.
Sin embargo, en el desarrollo de un procesador de lenguaje ser
necesario asociar valores semnticos a las construcciones del
lenguaje
Estos valores estarn en funcin del tipo de procesador de
lenguaje que estemos desarrollando.
Intrprete / Compilador / Traductor
Su tipo (anlisis semntico)
Su subrbol sintctico (procesador de ms de una pasada)
Informacin relativa a la generacin de cdigo.
Yacc permite asociar valores semnticos (atributos) a los
smbolos gramaticales (gramtica atribuida).
Pgina 56
Generador de analizadores ascendentes Yacc
Semntica
Los valores semnticos de los elementos terminales se
describen con la declaracin %token
Este valor semntico es lo que se conoce en el anlisis lxico como
atributo de un token
%token <entero> CTE_ENTERA
%token <caracter> CTE_CARACTER
%token <real> CTE_REAL
%token <cadena> IDENTIFICADOR CTE_STRING
Los valores semnticos de los elementos no terminales se
describen con la declaracin %type
Por ejemplo, si queremos crear un intrprete de expresiones,
deberemos asignar al no terminal expresin su valor numrico.
%union {
int entero;

}
%type <entero> exp
29
Pgina 57
Generador de analizadores ascendentes Yacc
Semntica
El modo en el que se asignan los valores a cada smbolo no
terminal, es mediante acciones yacc (rutinas semnticas).
Al final de cada produccin, se puede especificar cdigo C entre
llaves.
Cuando el analizador realice una reduccin de la produccin, se
ejecutar su accin asociada.
En este cdigo, se puede acceder a los valores de los smbolos
gramaticales de la produccin.
$$ representar el valor semntico del no terminal de la izquierda.
$n representar cada uno de los valores semnticos de los
smbolos gramaticales de la parte derecha, ordenados
consecutivamente de izquierda a derecha.
exp: exp '+' exp { $$= $1 + $3; }
Si el no terminal de la izquierda aparece en %type, se le deber
asociar valor semntico en la accin.
Pgina 58
Generador de analizadores ascendentes Yacc
Intrprete de expresiones
Especificacin en Yacc para crear un intrprete de expresiones enteras
%{

%}
%union { int entero; }
%token <entero> CTE_ENTERA
%left '+' '-'
%left '*' '/
%right '^'
%type <entero> exp
%%
programa: programa exp ';'
{ printf("Valor expresion: %d \n", $2); }
| exp ';' { printf("Valor expresion: %d \n", $1); }
;
exp: exp '+' exp { $$=$1+$3; }
| exp '-' exp { $$=$1-$3; }
| exp '*' exp { $$=$1*$3; }
| exp '/' exp { $$=$1/$3; }
| exp '^' exp { $$=(int)pow($1,$3); }
| '(' exp ')' { $$=$2; }
| CTE_ENTERA { $$=$1; }
;
%%
30
Pgina 59
Generador de analizadores ascendentes Yacc
Especificacin de precedencia
Siguiendo con nuestro ejemplo de intrprete de expresiones,
queremos introducir el operador unario de negacin (-)
Sintcticamente sera sencillo:
exp: exp '+' exp { $$=$1+$3; }
| ...
| '-' exp { $$=-$2; }
Sin embargo, qu sucedera en la siguiente evaluacin?
-2^2;
Puesto que el operador - tiene una precedencia menor, se
evala posteriormente a la potencia (el resultado sera -4)
El problema es que el mismo token - debera tener distintas
precedencias
La menor si es binario
La mayor si es unario
Pgina 60
Generador de analizadores ascendentes Yacc
Especificacin de precedencia
En Yacc es posible asociar una precedencia a una regla.
1. Creamos un nuevo token, que no aparecer en ningn punto de la
gramtica.
2. Se le asigna a este token la precedencia deseada.
3. Al final de la produccin, antes de la accin, se especifica la
precedencia de la regla mediante %prec, seguido del nombre del
token creado.
%token <entero> CTE_ENTERA
%token UNARIO
%left '+' '-'
%left '*' '/'
%right '^'
%nonassoc UNARIO
%%
exp: exp '+' exp { $$=$1+$3; }
| ...
| '-' exp %prec UNARIO { $$=-$2; }
;
%%
31
Pgina 61
Generador de analizadores ascendentes Yacc
Resolucin de conflictos
Reglas generales:
1. En un conflicto s/r por defecto se elige cargar.
2. En un conflicto r/r por defecto se reduce aplicando la regla
gramatical anterior en la secuencia de entrada.
La precedencia y asociatividad asociada a una regla gramatical,
es la precedencia y asociatividad del ltimo token de la regla.
Si se produce un conflicto s /r, lo que se hace es comparar la
precedencia de la regla con la del token que va a ser ledo
1. Si es mayor la del token se carga, si es mayor la de la regla se
reduce.
2. Si tienen la misma precedencia, la eleccin se basa en la
asociatividad: si el token es asociativo de izquierda a derecha
(%left) se elige reducir, si lo es de derecha a izquierda (%right) se
carga y si no es asociativo se producir un error.
3. Si la regla o el token que se va a leer no tienen precedencia, por
defecto se elige cargar.
Pgina 62
Generador de analizadores ascendentes Yacc
Intrprete de expresiones
Especificacin en Yacc para crear un intrprete de expresiones enteras
%{

%}
%union { int entero; }
%token <entero> CTE_ENTERA
%token UNARIO
%left '+' '-'
%left '*' '/
%right '^
%nonassoc UNARIO
%type <entero> exp
%%
programa: programa exp ';'
{ printf("Valor expresion: %d \n", $2); }
| exp ';' { printf("Valor expresion: %d \n", $1); }
;
exp: exp '+' exp { $$=$1+$3; }
| exp '-' exp { $$=$1-$3; }
| exp '*' exp { $$=$1*$3; }
| exp '/' exp { $$=$1/$3; }
| exp '^' exp { $$=(int)pow($1,$3); }
| '-' exp %prec UNARIO { $$=-$2; }
| '(' exp ')' { $$=$2; }
| CTE_ENTERA { $$=$1; }
;
%%
32
Pgina 63
Generador de analizadores ascendentes Yacc
Acciones en mitad de las reglas
En ocasiones es til escribir una accin en un punto intermedio
de la parte derecha de una produccin.
exp: factor *' factor { /* accin intermedia */ }
+ sumando { /* accin reduccin */ }
Yacc permite realizar este proceso.
La accin intermedia se ejecutar una vez se hayan reconocido los
smbolos gramaticales situados a su izquierda (factor *' factor)
Sin embargo, es necesario tener en cuenta una serie de
consideraciones adicionales:
1. La accin intermedia posee un valor semntico ($4 segn el
ejemplo)
2. En la accin intermedia, $$ no constituye el no terminal de la
izquierda, sino l mismo
3. Puesto que no puede incluirse su tipo en %type, la sintaxis de
acceso es $<tipo>$
exp: factor *' factor { $<entero>$=$1*$3; }
+ sumando { $$=$<entero>4+$6; }
Pgina 64
Generador de analizadores ascendentes Yacc
Recuperacin de errores
Yacc implementa un mecanismo de recuperacin de errores basado
en:
Producciones de error
Recuperacin en modo de alarma
Por omisin, si se produce un error se llama a yyerror y se finaliza
el procesamiento
Sin embargo, es posible recuperarse de los errores incluyendo
producciones de error, empleando el no terminal error
programa: programa exp ';'
| exp ';'
| error ';'
En este caso, si se produce un error en una expresin, Yacc seguira
los siguientes pasos:
1. Llamar a yyerror.
2. Descartar de la pila los estados y smbolos gramaticales de la parte
derecha de la produccin hasta que el token error sea aceptable.
3. Descartar la entrada hasta el token ;
4. Seguir procesando la entrada.
33
Pgina 65
Generador de analizadores ascendentes Yacc
Recuperacin de errores
En el ejemplo anterior, el parser entra en modo de alarma y no
reconoce el programa hasta encontrar un ;
Para la entrada
1+2
3+4;
5+6;
Se recupera en la tercera sentencia, ya que descarta hasta el ;
Para los errores de omisin, se puede utilizar en Yacc:
La macro yyerrok para salir del modo de alarma.
La macro yyclearin para descartar el token actual de la entrada.
programa: programa exp ';'
| exp ';'
| error { yyclearin; yyerrok; }
Pgina 66
Generador de analizadores ascendentes Yacc
Ejercicio 1 - Intrprete
En el lenguaje PROLOG son vlidas las siguientes listas:
[ ] [a1]
[a1, a2, a3] [a1, a2, [ ]]
[[a1,a2], a3, [a4,[a5,a6],[a7],a8]]
Cada uno de los trminos de una lista puede ser un identificador o una lista. Los
identificadores son secuencias de caracteres alfanumricos (con letras en
minscula) que comienzan por una letra.
Se define longitud de una lista como el nmero de trminos que la componen. Se
define extensin de una lista, como la longitud mxima de cualquier sublista
contenida en ella.
Lista Longitud Extensin
[[a1,a2], a3, [a4,[a5,a6],[a7],a8]] 3 4
[ ] 0 0
[a1, a2] 2 2
[[a1, a2]] 1 2
[[a1, [a2, a3, a4]]] 1 3
34
Pgina 67
Generador de analizadores ascendentes Yacc
Ejercicio 2 - Intrprete
Se desea construir un intrprete de un lenguaje de programacin que permite
utilizar variables de dos tipos diferentes: Cadena de caracteres y Lgico
En este lenguaje no hay zona de declaraciones, de manera que el tipo de una
variable se sabe por un carcter especial que se aade al final de su identificador.
Los caracteres especiales son los siguientes:
Cadena de caracteres: $. Ej. a$, h0$, auxiliar_000_$, aux$, etc.
Lgico: @. Ej. j@, jjj__jj@, j0igge6@, etc.
Se tienen las siguientes consideraciones
1. No se permiten variables con el mismo nombre y diferente terminador
(ej. aux$ y aux@).
2. Las constantes de tipo cadena van siempre entre comillas dobles,
mientras que las de tipo lgico son los literales TRUE y FALSE.
3. Se permiten asignaciones mltiples mediante el operador =
Pgina 68
Generador de analizadores ascendentes Yacc
Ejercicio 2 - Intrprete
4. Hay dos tipos de sentencias: las que son expresiones, que simplemente
calculan valores pero no sacan por pantalla ningn resultado, y la
sentencia PRINT que saca el valor de una expresin por pantalla.
La semntica de las diferentes operaciones es la siguiente:
Los operandos sern de tipo lgico. Tiene como resultado un valor lgico.
AND y NOT
Los operandos pueden ser de tipo lgico o cadena. Tiene como resultado
un valor lgico.
>
Si el ID es de tipo cadena se le puede asignar tanto una expresin de tipo
cadena como de tipo lgico, el lgico se traduce a la cadena FALSE o
TRUE segn su valor. Si el ID es de tipo lgico, slo puede asignrsele
otro valor de tipo lgico.
=
Si los dos operandos son de tipo lgico, se produce el OR lgico. Si
son del tipo cadena, se produce una concatenacin.
+
Semntica Operador
35
Pgina 69
Generador de analizadores ascendentes Yacc
Ejercicio 2 - Intrprete
a$ vale
pepe
d@ vale
TRUE
d@+TRUE+FALSE vale
TRUE
' Adios'+'Hola' vale
AdiosHola
NOT d@ vale
FALSE
v@ = w@ = d@ vale
TRUE
...
a$="pepe";
d@=TRUE;
PRINT "a$ vale ";
PRINT a$;
PRINT "d@ vale ";
PRINT d@;
PRINT "d@+TRUE+FALSE vale";
PRINT d@+TRUE+FALSE;
PRINT "' Adios'+'Hola' vale ";
PRINT " Adios"+"Hola";
PRINT "NOT d@ vale ";
PRINT NOT d@;
PRINT "v@ = w@ = d@ vale ";
PRINT v@ = w@ = d@;
...
Salida Entrada
Pgina 70
Generador de analizadores ascendentes Yacc
Ejercicio Resuelto Traductor (I)
Escribir los ficheros completos de especificaciones Lex/Yacc para
traducir la declaracin de variables en C a su correspondiente
declaracin en Pascal, con las siguientes limitaciones:
a) Slo se utilizar el tipo int y los constructores de tipo puntero (*)
y matriz ([ ])
b) Se generar una sentencia declarativa para cada variable.
c) Se usara obligatoriamenete $$, $1, $2, .... en las acciones de yacc.
alfa: integer;
beta: integer;
b: array [0..9] of integer;
p: ^integer;
q: array [0..5] of array [0..2] of integer;
int alfa,beta;
int b[10];
int *p,q[6][3];
Pascal C
36
Pgina 71
Generador de analizadores ascendentes Yacc
Ejercicio Traductor (II)
%{
unsigned yylineno=1;
%}
letra [A-Za-z]
digito [0-9]
alfanumerico [a-zA-Z0-9]
identificador {letra}{alfanumerico}*
num {digito}+
%%
\n {yylineno++;}
[ \t] ;
"*" return '*';
"[" return '[';
"]" return ']';
";" return ';';
"," return ',';
"/" return '/';
{identificador} {if (strcmp (yytext, "int")==0)
return INT;
else {
strcpy (yylval.nombre, yytext);
return ID;
}
}
{num} {
yylval.num = atoi (yytext);
return NUM;
}
Especificaciones
LEX
Pgina 72
Generador de analizadores ascendentes Yacc
Ejercicio Traductor (III)
%{
#include <stdio.h>
#include <string.h>
/* Enlace con lex */
extern unsigned yylineno;
extern FILE *yyout;
%}
%union {
int num;
char nombre[100];
}
%token INT
%token <nombre> ID
%token <num> NUM
%type <nombre> array
%%
decla: INT listadevariables ';'
| decla INT listadevariables ';'
;
Especificaciones
YACC
37
Pgina 73
Generador de analizadores ascendentes Yacc
Ejercicio Traductor (IV)
listadevariables: '*' ID
{fprintf (yyout, "%s: ^integer;\n",$2);}
|ID
{fprintf (yyout, "%s: integer;\n",$1);}
|ID array
{fprintf (yyout, "%s:%s integer;\n",$1,$2);}
|listadevariables ',' ID
{fprintf (yyout, "%s: integer;\n",$3);}
|listadevariables ',' '*' ID
{fprintf (yyout, "%s: ^integer;\n",$4);}
|listadevariables ',' ID array
{fprintf (yyout, "%s:%s integer;\n",$3,$4);}
;
array:
'[' NUM ']' {sprintf($$, "Array [0..%d] of", $2-1);}
| array '[' NUM ']' {sprintf($$,"%s Array [0..%d]
of",$1,$3-1);}
;
%%
void yyerror(char *s) {
printf("Error sintactico en linea %d.\n",yylineno);
}
Pgina 74
Generador de analizadores ascendentes Yacc
Ejercicio Resuelto Intrprete (I)
<programa> ::= <bloque>.
<bloque> ::= <sentencia> {;<bloque>} | <vacio>
<sentencia> ::= <asignacion> | <lectura> | <escritura>
<asignacion> ::= <variable>=<expresion>
<expresion> ::= <termino><mas terminos>
<mas terminos> ::= + <termino><mas terminos> |
- <termino><mas terminos> |
<vacio>
<termino> ::= <potencia><mas factores>
<mas factores> ::= * <potencia><mas factores> |
/ <potencia><mas factores> |
% <potencia><mas factores> |
<vacio>
<potencia> ::= <base><exponente>
<base> ::= <signo><factor>
<exponente> ::= ^<factor><exponente> | <vacio>
Intrprete del lenguaje MUSIM descrito mediante la siguiente gramtica
38
Pgina 75
Generador de analizadores ascendentes Yacc
Ejercicio Intrprete (II)
<factor> ::= (<expresion>) | <variable> | <constante>
<escritura> ::= write <variable>
<lectura> ::= read <variable>
<variable> ::= identificador
<constante> ::= entero
<signo> ::= + | - | <vacio>
typedef struct TablaSim {
char *nomvar; /* nombre de variable */
short tipo; /* IDENTIF (variable ya definida)*/
/* UNDEF (variable an no definida) */
int valor; /* valor de la variable */
struct TablaSim *sig; /* enlace de lista */
} TablaSim;
TablaSim *insertar(char *, int, int), *buscar(char *);
Estructura de la Tabla de
Smbolos ( intmus.h )
Pgina 76
Generador de analizadores ascendentes Yacc
Ejercicio Intrprete (III)
%{
#include "math.h"
#include "string.h"
#include "intmus.h"
/* Enlace con lex */
extern unsigned yylineno;
extern FILE *yyout;
%}
%union {
int valor; /* valor actual */
TablaSim *sim; /* puntero a tabla de simbolos */
}
%token READ WRITE MAS MENOS POR DIV PUNTO_Y_COMA MENOS_UNARIO
%token ABRE_PARENT CIERRA_PARENT PUNTO POTENCIA IGUAL MOD
%token <valor> ENTERO
%token <sim> IDENTIF UNDEF
%type <valor> expres
%right IGUAL
%left MAS MENOS
%left POR DIV MOD
%right POTENCIA
%nonassoc MENOS_UNARIO
39
Pgina 77
Generador de analizadores ascendentes Yacc
Ejercicio Intrprete (IV)
%%
programa : /* vacio */
| bloque PUNTO
;
bloque : sentencia PUNTO_Y_COMA
| bloque sentencia PUNTO_Y_COMA
;
sentencia: /* vacio */
| read
| write
| asigna
| expres
;
read : READ IDENTIF { read_var($2); }
;
write : WRITE IDENTIF { write_var($2); }
;
asigna : IDENTIF IGUAL expres {$1->valor = $3;
$1->tipo=IDENTIF;}
;
Pgina 78
expres : ENTERO {$$ = $1;}
| IDENTIF {if ($1->tipo==UNDEF)
execerror("Variable indefinida)
$$ = $1->valor;}
| expres MAS expres { $$ = $1 + $3; }
| expres MENOS expres { $$ = $1 - $3; }
| expres POR expres { $$ = $1 * $3; }
| expres DIV expres { if ($3 == 0)
execerror("Division por cero");
$$ = $1 / $3;}
| expres MOD expres {if ($3 == 0)
execerror("Division por cero");
$$ = $1 % $3;}
| expres POTENCIA expres { $$ = (int) pow($1,$3); }
| ABRE_PARENT expres CIERRA_PARENT { $$ = $2; }
| MENOS expres %prec MENOS_UNARIO { $$ = -$2; }
;
%%
void yyerror(char *s) {
printf("Error sintactico en linea %d.\n",yylineno);
}
Generador de analizadores ascendentes Yacc
Ejercicio Intrprete (V)
40
Pgina 79
Generador de analizadores ascendentes Yacc
Ejercicio Intrprete (VI)
void execerror(char *literal)
{
fprintf(stderr, "%s en linea %d.\n" literal, yylineno);
} /* fin execerror() */
/* lectura de variable por pantalla */
void read_var(TablaSim *p)
{
printf("%s ? : ",p->nomvar);
scanf("%d",&p->valor);
p->tipo=IDENTIF;
} /* fin read_var() */
/* muestra variable por pantalla */
void write_var(TablaSim *p)
{
printf("%s = %d\n",p->nomvar,p->valor);
}/* fin write_var() */
Pgina 80
Generador de analizadores ascendentes Yacc
Ejercicio Resuelto Compilador (I)
Compilador del leguaje JMUSIM/10 (simplificacin del lenguaje
Java Java MUy SIMple) a JENSAMPOCO/10
Tiene slo un tipo de datos simple Integer
Todo se organiza en clases
Slo tiene constructores, no hay destructores, pues utiliza
gestin de memoria automtica.
Todos los mtodos son procedimientos sin parmetros.
El cdigo de los mtodos se escribe en un bloque despus de
su declaracin y dentro de la clase (vase ejemplo).
En la seccin de datos todo es pblico.
El mtodo Main se encarga del inicio de la ejecucin de la
clase.
En esta versin slo se puede hacer una clase.
41
Pgina 81
Generador de analizadores ascendentes Yacc
Ejercicio Compilador (II)
Existen los mtodos estndar: Read, Write para la entrada y
la salida.
Hay un mtodo estndar New para invocar al constructor.
Es obligatorio el mtodo constructor y debe aparecer el
primero de los mtodos (en caso de que no aparezca o que
no sea el primero se emitir un error).
Es obligatorio el mtodo Main y debe aparecer el ltimo de
los mtodos (en caso de que no aparezca o no sea el ltimo
se emitir un error).
Los comentarios empiezan por // y van hasta el final de
lnea.
Se permiten los operadores enteros: +,-,*,/ y menos unario.
Se permite el uso de parntesis en expresiones enteras.
La asignacin es una sentencia (no un operador).
Pgina 82
Generador de analizadores ascendentes Yacc
Ejercicio Compilador (III)
No se permite la sobrecarga (ni de mtodos ni de
operadores).
En el cuerpo de un bloque entre llaves es obligatorio que las
declaraciones estn en cabeza.
El punto y coma (;) es un separador de instrucciones (por
eso no es obligatorio antes de cerrar llaves).
JENSAMPOCO/10 es un lenguaje intermedio que se ejecuta en la mquina de pila
JENSAMPOCO VIRTUAL MACHINE/10.
En la pila de dicha mquina solo se pueden meter nmeros
enteros.
Las direcciones de memoria se representan por enteros.
Las caractersticas bsicas de esta mquina son
42
Pgina 83
Generador de analizadores ascendentes Yacc
Ejercicio Compilador (IV)
Entrada JMUSIM10:
CLASS Triangulo {
// Seccin de datos
Integer a, b, c; // Declaracin de datos
//Seccin de mtodos
Triangulo() { Read a, b, c} // Constructor
Visualizar() { Write a, b, c}
Perimetro() {Integer p; p=a+b+c; Write p}
Main() {
Triangulo x,y; // Declaracin de objetos
Integer p; //Variable local
x=New Triangulo();
x.Visualizar()
x.Perimetro();
y=New Triangulo();
p=(y.a + y.b + y.c) / 2;
Write y.p
} // Fin de Main
} // Fin de la clase
Pgina 84
Generador de analizadores ascendentes Yacc
Ejercicio Compilador (V)
Salida JENSAMPOCO/10:
.CLASS Triangulo
;Seccin de datos
INT a ;Declaracin de variables globales de la clase
INT b
INT c
;Seccin de mtodos
;Constructor
PROC Triangulo
INPUT [a]; Introduce un dato en la direccin de a
INPUT [b]
INPUT [c]
END ; Fin del constructor
PROC Visualizar
OUTPUT [a] ;Escribe el dato de la direccin a
OUTPUT [b]
OUTPUT [c]
END
PROC Permetro
INT p ;Se declara una variable local al procedimiento
PUSHA p ;Introduce en la pila la direccin de la variable
;local p
PUSHA a ;Introduce en la pila la direccin de la variable
;global a
43
Pgina 85
Generador de analizadores ascendentes Yacc
Ejercicio Compilador (VI)
PUSHA b ;Introduce en la pila la direccin de la variable
;global b
LOAD ;carga el valor de la direccin en el tope de la pila
;en este caso a
ADD
PUSHA c
LOAD
ADD
STORE
OUTPUT [p]
END
PROC Main
Trinagulo x ; Objeto local a Main
Triangulo y ; Objeto local a Main
INT p
New x ;Reserva espacio en memoria dinmica para el objeto x
PUSHA x ;Mete la direccin del objeto x en la pila
CALL Triangulo ; Llamada al constructor
PUSHA x
CALL Visualizar
PUSHA x
CALL Permetro
NEW y
PUSHA y
CALL Triangulo
Pgina 86
Generador de analizadores ascendentes Yacc
Ejercicio Compilador (VII)
PUSHA p
PUSHA y.a
LOAD
PUSHA y.b
LOAD
ADD
PUSHA y.c
LOAD
ADD
PUSHC 2
DIV
STORE
OUTPUT [p]
END ;Fin de main
.END ;Fin de la clase
44
Pgina 87
Generador de analizadores ascendentes Yacc
Ejercicio Compilador (VIII)
%{
#include <stdio.h>
#include <string.h>
/* Enlace con lex */
extern unsigned yylineno;
extern FILE *yyout;
#define TAM_MAX_IDE 32
struct datos
{
char *nombre;
int var;
struct datos *sig;
};
struct metod
{
char *nombre;
struct datos *listalocal;
struct metod *sig;
};
Pgina 88
Generador de analizadores ascendentes Yacc
Ejercicio Compilador (IX)
class Tabla_simbolos
{
private:
char *nomclase; /*nombre de la clase*/
struct datos *listadatos; /* lista de datos*/
struct metod *listmet; /* lista de metodos*/
public:
/* inserta un mtodo en la lista de mtodos */
void insertarmetodo (char *identificador);
/* inserta una variable local a un mtodo o en la seccin
de datos */
void insertarvariable (char *identificador,int nivel);
/* busca el identificador que puede ser: un mtodo, objeto
o variable en las estructuras de datos en su mbito */
int buscar (char *identificador,int tipo);
}
%}
%union
{
char nombre[TA_MAX_IDE];
int num;
}
45
Pgina 89
Generador de analizadores ascendentes Yacc
Ejercicio Compilador (X)
%token CLASS INTEGER NEW WRITE READ MAIN
%token <nombre> IDENTIF
%token <num> NUM
%type <nombre> lista_objetos
%right '='
%left '-' '+'
%left '*' '/'
%right NEG
%%
programa: CLASS IDENTIF { genera2(".CLASS",$2);}
'{' bloque'}'
;
bloque: seccion_datos constructor seccion_metodos metodo_main
;
seccion_datos: /*vacio*/
| INTEGER declara ';'
;
declara: IDENTIF { genera2 ("INT", $1);}
| declara ',' IDENTIF {genera2 ("INT", $3);}
;
seccion_metodos: /*vacio*/
| otros_metodos
;
Pgina 90
Generador de analizadores ascendentes Yacc
Ejercicio Compilador (XI)
constructor: IDENTIF {if (!buscar($1))
yyerror("Nombre del constructor incorrecto")
genera2 ("PROC",$1);}
'(' ')'
'{' seccion_datos sentencias '}' {genera1("END");}
;
sentencias: sentencia
| sentencias ';' sentencia
;
sentencia: IDENTIF '=' NEW IDENTIF '(' ')'
{genera2("NEW", $1); genera2("PUSHA",$1);genera2("CALL",$4);}
| IDENTIF '.' IDENTIF '(' ')'
{genera2("PUSHA",$1);genera2("CALL",$3);}
| IDENTIF '=' {genera2("PUSHA", $1);} exp
{genera1("STORE");}
| WRITE lista_var_escritura
| READ lista_var_lectura
;
exp : NUM {genera2 ("PUSHC", $1);}
| IDENTIF {genera2 ("PUSHA", $1); genera1("LOAD");}
| IDENTIF '.' IDENTIF {genera4 ("PUSHA", $1, ".", $3);
genera1("LOAD");}
46
Pgina 91
Generador de analizadores ascendentes Yacc
Ejercicio Compilador (XII)
| exp '+' exp {genera1("ADD");}
| exp '-' exp {genera1("NEG");genera1("ADD"}
| exp '*' exp {genera1("MUL");}
| exp '/' exp {genera1("DIV");}
| '-' exp %prec NEG {genera1("NEG");}
;
lista_var_escritura: IDENTIF {genera3 ("OUTPUT [",$1,"]");}
| lista_var_escritura ',' IDENTIF
{genera3 ("OUTPUT [",$3,"]");}
;
lista_var_lectura: IDENTIF {genera3 ("INPUT [",$1,"]");}
| lista_var_lectura ',' IDENTIF
{genera3 ("INPUT [",$3,"]");}
;
otros_metodos: metodo
| otros_metodos metodo
;
metodo: IDENTIF {genera2 ("PROC",$1);} '(' ')'
'{' seccion_datos sentencias '}'
{genera1("END");}
;
Pgina 92
Generador de analizadores ascendentes Yacc
Ejercicio Compilador (XIII)
metodo_main: MAIN '(' ')' {genera1("PROC Main");}
'{' seccion_declaraciones sentencias '}'
;
seccion_declaraciones: seccion_objetos seccion_datos
;
seccion_objetos: IDENTIF lista_objetos ';'
{if (!busca($1))
yyerror ("Tipo desconocido"); genera($1,$2);}
;
lista_objetos: IDENTIF {sprintf ($$,$1);}
| lista_objetos ',' IDENTIF
{strcat($$,",");strcat($$,$3);}
;
%%
void yyerror(char *s) {
printf("Error sintactico en linea %d. %s\n",yylineno,s);
}
47
Pgina 93
Generadores de procesadores de lenguajes
Herramientas de generacin de parsers LL(k)
Se est extendiendo el uso de herramientas de generacin de
analizadores sintcticos descendentes.
Las ms conocidas son:
AntLR (ANother Tool for Language Recogintion), anteriormente
llamada PCCTS: Generador de analizadores lxicos y sintcticos
descendentes predictivos recursivos LL(k)
Genera cdigo Java, C++, C#
JavaCC (Java Compiler Compiler): Herramienta de generacin de
analizadores sintcticos descendentes predictivos recursivos LL(k)
y analizadores lxicos basados en expresiones regulares
Slo genera Java
Coco/R (COmpiler COmpiler generating Recursive descent
parsers): Se basa en gramticas L-atribuidas y genera analizadores
sintcticos descendentes predictivos LL(k)
Hay versiones para Java y C#
http://catalog.compilertools.net/kits.html
Pgina 94
Generadores de procesadores de lenguajes
Herramientas de generacin de parsers LL(k)
Las caractersticas comunes a AntLR, JavaCC y Coco/R son:
Generacin de analizadores lxicos
JavaCC y Coco/R mediante expresiones regulares
AntLR mediante gramticas LL(k)
Generacin de analizadores sintcticos descendentes recursivos
Posibilidad de especificar un lookahead (k) variable > 1
Notacin EBNF para la descripcin de la gramtica
Implementacin de un mecanismo de recuperacin de errores
basados en el modo de alarma con tokens de sincronizacin
Dan soporte al resto de fases de un procesador
Coco/R mediante gramticas atribuidas
AntLR y JavaCC mediante visitas de ASTs
48
Pgina 95
Generadores de procesadores de lenguajes
JavaCC y AntLR
Estas dos herramientas poseen un conjunto de
caractersticas adicionales comunes
El cdigo generado es legible por el desarrollador del
compilador (depuracin)
Permiten crear rboles de sintaxis abstracta (AST)
Incorporan una sintaxis para procesar gramticas de ASTs
Generacin automtica de documentacin
AntLR ofrece una serie de caractersticas adicionales
Predicados sintcticos (backtracking selectivo)
Predicados semnticos (resolucin de ambigedades mediante
consideraciones semnticas)
Pgina 96
Generadores de procesadores de lenguajes
Ejemplo AntLR
class LexerCalculadora extends Lexer ;
BLANCO : (' ' | '\t' | "\r\n" {newline();})
{ $setType(Token.SKIP); };
OP_MAS : '+';
OP_MENOS : '-';
OP_PRODUCTO : '*';
OP_COCIENTE : '/';
PARENT_AB : '(';
PARENT_CE : ')';
NUMERO : ('0'..'9')+ ('.' ('0'..'9')+)?;
class ParserCalculadora extends Parser ;
options
{
buildAST = true;
}
expresion : expSuma;
//expresion es la regla base, la primera que se reconoce
expSuma : expResta (OP_MAS^ expResta)*;
Analizador
Lxico
Analizador
Sintctico
49
Pgina 97
Generadores de procesadores de lenguajes
Ejemplo AntLR
expResta : expProducto (OP_MENOS^ expProducto)* ;
expProducto : expCociente (OP_PRODUCTO^ expCociente)* ;
expCociente : expBase (OP_COCIENTE^ expBase)* ;
expBase : NUMERO
| PARENT_AB! expresion PARENT_CE!
;
class CalcTreeParser extends TreeParser ;
expresion returns [float result=0]
{ float izq=0, der=0; }
: n:NUMERO
{ result = new Float(n.getText()).floatValue(); }
// se devuelve el float
| #(OP_MAS izq=expresion der=expresion)
{ result = izq + der ; } // se suman izq y der
| #(OP_MENOS izq=expresion der=expresion)
{ result = izq - der ; } // se restan
| #(OP_PRODUCTO izq=expresion der=expresion)
{ result = izq * der ; } // se multiplican
| #(OP_COCIENTE izq=expresion der=expresion)
Nivel
Semntico
Pgina 98
{
if(der==0.0)
throw new ArithmeticException("Divisin por cero");
result = izq / der; // se dividen (o se lanza una excepcin)
};
// Importar clases para leer en la entrada estndar
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.StringReader;
import antlr.collections.AST; // Importar AST
import antlr.ANTLRException; // Importar excepcines de ANTLR
public class Calculadora {
public static void main(String args[]){
try
{
// PASO 1. Obtener una lnea de texto por la entrada estndar
BufferedReader in =
Ejecucin
Generadores de procesadores de lenguajes
Ejemplo AntLR
50
Pgina 99
new BufferedReader(new InputStreamReader(System.in));
StringReader sbr = new StringReader(in.readLine());
// PASO 2. Crear un analizador lxico para dicha entrada
LexerCalculadora lexer = new LexerCalculadora(sbr);
// PASO 3. Crear un analizador sintctico asociado al
lxico
ParserCalculadora parser = new ParserCalculadora(lexer);
// PASO 4. Lanzar el anlisis lxico-sintctico
parser.expresion();
// PASO 5. Obtener el AST
AST ast = parser.getAST();
// PASO 6. Crear el analizador semntico
CalcTreeParser treeParser = new CalcTreeParser();
// PASO 7. Recorrer el AST
float result = treeParser.expresion(ast);
// Imprimimos el resultado
System.out.println("Resultado: " + result);
} catch (Exception e) {
e.printStackTrace(System.err);
} } }
Generadores de procesadores de lenguajes
Ejemplo AntLR
Pgina 100
Generadores de procesadores de lenguajes
Ejemplo JavaCC
// * Aadido de miembros para la clase TokenManager (scanner)
TOKEN_MGR_DECLS: {
int yylval;
}
SKIP: { // * Comentarios de ms de una lnea
"/*": EstadoComentario
}
<EstadoComentario> SKIP: {
<~[]>
}
// * Esta regla prevalece ante la anterior, es ms larga
<EstadoComentario> SKIP: {
"*/":DEFAULT // * Estado inicial
}
// * Otros caracteres y comentarios ignorados
SKIP: {
<"//" (~["\n","\r"])* ["\n","\r"]>
| "\t"
| "\n"
| "\r"
| " "
}
Analizador
Lxico
51
Pgina 101
Generadores de procesadores de lenguajes
Ejemplo JavaCC
// * Especificacin de los componentes lxicos (tokens)
TOKEN: {
<ENTERO: (["0"-"9"])+> // image es el lexema reconocido
{ yylval=Integer.parseInt(image.toString()); }
| <MAS: "+"> | <MENOS: "-">
| <ENTRE: "/"> | <POR: "*"> | <MODULO: "%">
| <POTENCIA: "^">
| <ABRE_PAR: "("> | <CIERRA_PAR: ")">
| <PUNTO: "."> | <PUNTO_Y_COMA: ";">
}
// * Especificacin de la gramtica EBNF
void programa(): {int valor;} {
valor=expresion() {mostrarExpresion(valor);}
( <PUNTO_Y_COMA> valor=expresion()
{mostrarExpresion(valor);} )*
<PUNTO>
}
int expresion(): {int op;} {
op=factor() [op=restoTermino(op)] {return op;}
}
Sintaxis
+
Semntica
Pgina 102
Generadores de procesadores de lenguajes
Ejemplo JavaCC
int restoTermino(int op1): {int op2;} {
<MAS> op2=expresion() {return op1+op2;}
| <MENOS> op2=expresion() {return op1-op2;}
}
int factor(): {int op;} {
op=potencia() [op=restoFactor(op)] {return op;}
}
int restoFactor(int op1): {int op2;} {
<ENTRE> op2=factor() {return op1/op2;}
| <POR> op2=factor() {return op1*op2;}
| <MODULO> op2=factor() {return op1%op2;}
}
int potencia(): {int op;} {
op=unario() [op=restoPotencia(op)] {return op;}
}
52
Pgina 103
Generadores de procesadores de lenguajes
Ejemplo JavaCC
int restoPotencia(int op1): {int op2;} {
<POTENCIA> op2=potencia()
{return (new java.math.BigInteger( new
Integer(op1).toString() )).pow(op2).intValue();}
}
int unario(): {int op;} {
<MENOS> op=unario() {return -op;}
| op=constante() {return op;}
}
int constante(): {int op;} {
<ABRE_PAR> op=expresion() <CIERRA_PAR> {return op;}
// * token_source es la instancia del lxico utilizada
| <ENTERO> {return token_source.yylval;}
}
// * Opciones del Compilador
options {
JAVA_UNICODE_ESCAPE=false; //No carac. de escape Unicode
UNICODE_INPUT=false; // Entrada ASCII
}
Ejecucin
Pgina 104
Generadores de procesadores de lenguajes
Ejemplo JavaCC
PARSER_BEGIN(ExpresionParser)
// * Cdigo aadido a la clase que hace de Parser
public class ExpresionParser {
// * Mtodo que visualiza el resultado
private void mostrarExpresion(int valor) {
System.out.println("Expresion evaluada: "+valor+".");
}
public static void main(String args[]) throws
ParseException {
// * Crear un parser que cree un scanner de teclado
ExpresionParser parser=new ExpresionParser(System.in);
// * Ejecutar la llamada al smbolo inicial de la
// gramtica
parser.programa();
}
}
PARSER_END(ExpresionParser)

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