YACC (Yet Another Compiler Compiler)

Anuncio
YACC
(Yet Another Compiler Compiler)
LALR(1) Parser Generator
1
INDICE
1. Introducción
2. Especificaciones
3. Ambigüedad y conflictos
4. Tratamiento de errores
5. Uso con el LEX
6. Algunos consejos prácticos
2
1. Introducción
E s p e c ific a c io n e s
YA C C
-d
y.ta b .h
-v
y.ta b . c
y.o u tp u t
y.ta b .h : c o n tie n e la c o d ific a c ió n d e lo s s ím b o lo s te rm in a le s d e la g ra m á tic a
y.ta b .h : c o n tie n e la ru tin a y y p a rs e () p a ra e l a n á lis is sin tá c tic o
y.o u tp u t: c o n tie n e e l a u tó m a ta d e re c o n o c im ie n to s in tá c tic o
3
2. Especificaciones
Declaraciones (opcional)
%%
Reglas de la gramática
%%
Funciones C (opcional)
4
Declaraciones
%{
Declaraciones C
%}
%token token_1 … token_N
%left operadores
%start S
(No es necesario, ya que por defecto se toma el símbolo
no-terminal de la primera regla)
5
Reglas
N Æ α1 | α2 | ε
MÆβ
N:
| α1
| α2
;
/ la opción ε debe ser la primera /
/ finalizador de reglas /
M: β
;
6
Código en las reglas
NÆABC
MÆXY
N: A B C { Código C }
;
N: X { Código C } Y { Código C }
;
7
3. Ambigüedad y conflictos
Una gramática ambigua genera conflictos.
EJEMPLO
EXPR : EXPR ‘+’ EXPR
| EXPR ‘*’ EXPR
| id
Se generan conflictos por la indefinición de
PRECEDENCIA y ASOCIATIVIDAD de los operadores
8
Conflictos por Precedencia y Asociatividad
Precedencia
( (id + id) * id )
id + id * id
( id + (id * id) )
Asociatividad
( (id + id) + id )
id + id + id
( id + (id + id) )
9
Solución de los conflictos (por defecto)
Por defecto, YACC soluciona los conflictos de la
siguiente forma:
shift/reduce
Æ
reduce/reduce Æ
shift
escoge la primera regla
declarada
En cualquier caso, YACC siempre informa de
los conflictos solucionados de esta forma.
10
Ejemplo (I)
STMT : if ‘(‘ EXPR ‘)’ then STMT
| if ‘(‘ EXPR ‘)’ then STMT else STMT
| id ‘=‘ EXPR
if (a+b) then if (x+y) then a=b else b=c
1.
if (a+b) then [ if (x+y) then a=b ] else b=c
2.
if (a+b) then [ if (x+y) then a=b else b=c ]
Conflicto SHIFT/REDUCE ante el símbolo: else
11
Ejemplo (II)
Por defecto, YACC resuelve el conflicto a favor del
SHIFT, y nos informa de la existencia del conflicto.
¿ Es correcta esta solución por defecto ?
SI, ya que la ambigüedad ha sido resuelta tal como
indican los lenguajes de programación, es decir, el
ELSE se asocia al IF más interno.
Sin embargo, no siempre son correctas las soluciones
por defecto.
12
Soluciones por defecto no satisfactorias
EXPR : EXPR ‘+’ EXPR
| EXPR ‘*’ EXPR
| id
a+b+c
a*b+c
Æ
Æ
(a + (b + c) )
(a * (b + c) )
Podemos eliminar la solución por defecto en los
conflictos mediante la especificación de la precedencia y
asociatividad de los operadores
13
Especificando Precedencia y Asociatividad
Operadores binarios: precedencia y asociatividad
Operadores unarios: solamente la precedencia
En la sección de declaraciones del fichero de especificaciones
podríamos indicar:
%left ‘+’ ‘-’
%left ‘*’ ‘/’
%nonassoc ‘-’
(tienen igual precedencia entre ellos)
(mayor precedencia que los anteriores)
(mayor precedencia que los anteriores)
Problema: el ‘-’ unario y el ‘-’ binario utilizan el mismo símbolo. La
solución pasa por utilizar un símbolo auxiliar.
14
Ejemplo (I)
%token id
%left ‘+’ ‘-’
%left ‘*’ ‘/’
%nonassoc UMINUS
%%
EXPR : EXPR ‘+’ EXPR
| EXPR ‘-’ EXPR
| EXPR ‘*’ EXPR
| EXPR ‘/’ EXPR
| ‘-’ EXPR
| id
-a*b
Æ
Símbolo auxiliar
-( a * b)
incorrecto
15
Ejemplo (II)
Cada regla de la gramática tiene la precedencia y asociatividad del último
token de la regla, aunque se puede modificar utilizando %prec.
Por tanto, el problema del ‘-’ unario puede quedar resuelto de la siguiente
forma:
%token id
%left ‘+’ ‘-’
%left ‘*’ ‘/’
%nonassoc UMINUS
%%
EXPR : EXPR ‘+’ EXPR
| EXPR ‘-’ EXPR
| EXPR ‘*’ EXPR
| EXPR ‘/’ EXPR
| ‘-’ EXPR %prec UMINUS
| id
%%
Símbolo auxiliar
16
Resumen de resolución de conflictos en YACC
1
Si no especificamos la precedencia y/o asociatividad de un
operador, YACC utiliza la solución por defecto.
shift/reduce
Æ
reduce/reduce Æ
2
shift
la primera regla declarada
Si especificamos la precedencia y/o asociatividad de todos los
operadores, YACC utiliza esa información para solucionar los
conflictos.
Veamos un ejemplo en el que se mezclan ambas situaciones
17
Resolución de conflictos: Ejemplo genérico (I)
%left ‘+’
%left ‘*’
%nonassoc UMINUS
%%
EXPR : EXPR ‘+’ EXPR
| EXPR ‘-’ EXPR
| EXPR ‘*’ EXPR
| EXPR ‘/’ EXPR
| ‘-’ EXPR %prec UMINUS
| id
%%
18
Resolución de conflictos: Ejemplo genérico (II)
Expresiones
Interpretación
-a*b+c
-a-b+c
-a*b/c
-a-b/c
Æ
Æ
Æ
Æ
[ (- a) * b ] + c
- [ a - (b + c) ]
(- a) * (b / c)
- [ a - (b / c) ]
a-b+c+d
a-b*c/d
a-b/c+d
a-b/c/d
Æ
Æ
Æ
Æ
a - [ (b + c) +d ]
a - [ b * (c / d) ]
a - [ b / (c + d) ]
a - [ b / (c / d) ]
19
4. TRATAMIENTO DE ERRORES
Cuando el analizador sintáctico encuentra un error, simplemente
hace la siguiente llamada:
yyerror (“syntax error”);
La rutina yyerror() de la librería es la siguiente:
yyerror (s)
char * s;
{
fprintf(stderr, “%s\n”, s);
}
20
Modificando yyerror()
yyerror (s)
char * s;
{
fprintf(stderr, “%s\n”, s);
fprintf(stderr, “Numero de linea %d\n”, n_linea);
fprintf(stderr, “Simbolo terminal %s\n”, yytext);
}
n_linea: variable que controla el número actual de línea.
yytext: cadena que almacena el último token reconocido.
21
5. Uso con el LEX
%token cte
%left ‘+’ ‘-’
%left ‘*’ ‘/’
%nonassoc UMINUS
%%
EXPR
: EXPR ‘+’ EXPR
| EXPR ‘-’ EXPR
| EXPR ‘*’ EXPR
| EXPR ‘/’ EXPR
| ‘-’ EXPR %prec UMINUS
| ‘(‘ EXPR ‘)’
| cte
%%
main()
{ yyparse();
}
22
Especificación LEX
%{
#include “y.tab.h”
/* Fichero de codificación de tokens */
%}
%%
[ \t\n]
;
[0-9]+
{ return (cte); }
.
{ return (yytext[0]); }
%%
----------------------------------------------------------------------------------------------Fichero y.tab.h
#define cte
257
23
Comandos para generar el parser
$ yacc -d -v file.y
se generan: y.tab.h , y.tab.c , y.output
$ lex file.l
se genera: lex.yy.c
$ cc y.tab.c lex.yy.c -ly -ll -o parser
$ parser < fichero_fuente
24
6. Algunos consejos prácticos
1.
Utilizar reglas recursivas por la izquierda siempre que
sea posible (favorece el reconocimiento ascendente,
ya que los árboles sintácticos crecen hacia la
izquierda)
2.
Cuidado con los tokens que son palabras claves en el
lenguaje C. Véase lo que sucede en la página
siguiente:
25
Problema con las palabras claves en C
LEX
#include y.tab.h Æ DESASTRE
“if”
“else”
{ return (if); }
{ return (else); }
YACC
STMT : if ‘(‘ EXPR ‘)’ STMT else STMT
Fichero y.tab.h
#define if
#define else
257
258
26
Solución: utilizar otros nombres lógicos
LEX
#include y.tab.h
“if”
“else”
{ return (if_t); }
{ return (else_t); }
YACC
STMT : if_t ‘(‘ EXPR ‘)’ STMT else_t STMT
Fichero y.tab.h
#define if_t
#define else_t
257
258
27
Descargar