Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
Yacc es un programa para generar analizadores sintcticos. Las siglas del nombre
significan Yet Another Compiler-Compiler, es decir, "Otro generador de
compiladores ms". Genera un analizador sintctico (la parte de un compilador
que comprueba que la estructura del cdigo fuente se ajusta a la especificacin
sintctica del lenguaje) basado en una gramtica analtica escrita en una notacin
similar a la BNF. Yacc genera el cdigo para el analizador sintctico en el Lenguaje
de programacin C.
Fue desarrollado por Stephen C. Johnson en AT&T para el sistema operativo Unix.
Despus se escribieron programas compatibles, por ejemplo Berkeley Yacc, GNU
visn, MKS yacc y Abraxas yacc (una versin actualizada de la versin original de
AT&T que tambin es software libre como parte del proyecto de OpenSolaris de
Sun). Cada una ofrece mejoras leves y caractersticas adicionales sobre el Yacc
original, pero el concepto ha seguido siendo igual. Yacc tambin se ha reescrito
para otros lenguajes, incluyendo Ratfor, EFL, ML, Ada, Java, y Limbo.
Puesto que el analizador sintctico generado por Yacc requiere un analizador
lxico, se utiliza a menudo conjuntamente con un generador de analizador lxico,
en la mayora de los casos lex o Flex, alternativa del software libre. El estndar de
IEEE POSIX P1003.2 define la funcionalidad y los requisitos a Lex y Yacc.
La versin Yacc de AT&T se convirti en software libre;
VERSIONES
{declaraciones}
%%
{reglas}
%%
{rutinas de apoyo en C}
-Declaraciones
1) Declaraciones ordinarias en C, delimitadas por %{ y %}.
2) Declaraciones de los componentes lxicos de la gramtica (esto se explica
ms adelante).
Podra estar vaca
-Reglas de traduccin
Cada una de ellas consta de una produccin de la gramtica y la accin semntica
asociada.
Es decir,
<lado izquierdo> <alt 1> | <alt 2> | ... | <alt n>
pasara a ser en Yacc:
<lado izquierdo> : <alt 1> {accin semntica 1}
| <alt 2> {accin semntica 2}
...
| <alt n> {accin semntica n}
;
Un carcter simple entre comillas c se considera como el smbolo terminal
c.
Las cadenas sin comillas de letras y dgitos no declaradas como
componentes lxicos se consideran no terminales.
El primer lado izquierdo se considera como smbolo inicial por defecto, o
bien se declara : %start smbolo
Una accin semntica es una secuencia de proposiciones en C, dnde $$
se refiere al valor del atributo asociado con el no terminal del lado izquierdo,
mientras que $i se refiere al valor asociado con el i-simo smbolo
gramatical del lado derecho. Se ejecuta siempre que se reduzca por la
produccin asociada. Por defecto es {$$=$1;}.
Una accin semntica no tiene por qu venir al final de su regla. Yacc
permite que una accin sea escrita en mitad de una regla. Esta regla
devuelve un valor, accesible de forma normal por las acciones que estn a
su derecha, que pueden acceder a los valores devueltos por los smbolos a
su izquierda
Ejemplo: A:B
{$$=1;}
C
{x=$2; y=$3;}
El efecto es poner x a 1 e y al valor devuelto por C.
Las acciones que no terminan una regla son manejadas por Yacc como si
fueran un nuevo nombre de no-terminal, con una nueva regla para l con el
String vaco en su parte derecha, y, como accin de esa regla, ella misma.
Es decir, el ejemplo anterior lo maneja como si se hubiera escrito as:
$ACT : /* empty */
{$$=1;};
A : B $ACT C
{x=$2; y=$3;};
NOTA: Puede haber conflictos cuando ocurre una accin interior en una
regla antes de que el parser pueda estar seguro de qu regla est siendo
reducida.
En muchas aplicaciones, la salida no viene dada directamente con las
acciones; en su lugar, una estructura de datos, tal como un rbol de
anlisis, se construye en memoria y se aplican transformaciones en l antes
de que la salida sea generada.
Los rboles de anlisis son particularmente fciles de construir, si se tienen
rutinas para realizar y mantener la estructura de rbol deseada.
Ejemplo.- Tenemos una funcin C que se llama nodo, de forma que
nodo(L,n1,n2)
crea un nodo con etiqueta L y descendientes n1 y n2, y devuelve el ndice
del nuevo nodo.
El rbol de anlisis podra ser realizado suministrando acciones en la
especificacin como
expr : expr '+' expr
{$$=nodo('+',$1,$3);}
Pueden definirse tambin otras variables para ser usadas por las acciones.
Tanto las declaraciones como las definiciones deben aparecer en la seccin
de declaraciones, entre %{ y %}. Tienen mbito global.
Deben evitarse los nombres de variables que empiecen por yy, pues los
nombres de variables internas de Yacc comienzan de esta forma todos.
EJEMPLOS
Mini calculadora
A continuacin, vamos a analizar un ejemplo sencillo de una verdadera
especificacin de yacc, que es la gramtica para una calculadora sencilla que
permite hacer operaciones como suma, resta, multiplicacin, divisin y exponente.
%{
#include <math.h>
%}
%union{
double dval;
}
%token
%token
%token
%token
<dval> NUMBER
PLUS MINUS TIMES DIVIDE POWER
LEFT_PARENTHESIS
RIGHT_PARENTHESIS
END
{ printf("Result: %f\n",$1); }
{ $$=$1; }
que ser usada en la especificacin de lex, del mismo programa para asignarle
valor a los tokens que yacc usara para realizar operaciones. Esta estructura puede
llegar a ser muy compleja, y para saber de que tipo es cada token devuelto por
yylex(), se usan las definiciones %token y %type.
%token y %type
%token sirve para definir los tokens que hay, y si es necesario, el tipo de dato que
usan, todos los tokens son tomados como smbolos terminales, lo cual veremos
mejor reflejado en la seccin de reglas, estos tambin tienen el objetivo de servir
como etiquetas que yylex() regresa a yacc para identificar el token que se ha ledo
recientemente.
Su uso es como sigue :
%token [<miembro_de_union>] ETIQUETA1 [ETIQUETA2 ... ETIQUETAn]
Donde todo lo que esta entre [ y ] es opcional.
<miembro_de_union> : Indica el miembro al que sern mapeados los tokens en la
union yylval dentro de lex.
ETIQUETAS : Estos son los nombres con los que se identificaran los tokens
mismos, que sern traducidos en C como nmeros en instrucciones #define del
preprocesador de C.
%type es anlogo a %token, solo que este define el tipo de dato para smbolos no
terminales de nuestra gramtica, la nica diferencia es que el tipo de dato a usar
es obligatorio.
En nuestro ejemplo :
%token
%token
%token
%token
<dval> NUMBER
PLUS MINUS TIMES DIVIDE POWER
LEFT_PARENTHESIS
RIGHT_PARENTHESIS
END
.
.
.
%type <dval> Expresin
La primera lnea indica que el token NUMERO ser del tipo de miembro de dval,
es decir, un doubl.
Las siguientes tres lneas, son para definir algunos tokens mas que sern usados
en la gramtica, pero no necesitan un tipo de dato ni un miembro en yylval
asociado.
En la ultima lnea definimos el tipo de dato que usara nuestro no terminal
Expresin.
%left y %right
El siguiente paso, es definir el tipo de precedencia de nuestros tokens operadores,
en este punto tenemos dos factores, la precedencia por si misma, y la agrupacin
de los operadores.
Precedencia
La precedencia es asignada en orden inverso al que aparecen, es decir, el ultimo
operador declarado, tiene mayor precedencia que el anterior y as sucesivamente.
Asociatividad
%left y %right indican si el operador se agrupa a la derecha o a la izquierda, por
ejemplo, en el caso de POWER (Exponente) debe asociarse a la derecha, por que
buscamos que se resuelva de ese modo, de derecha a izquierda, por ejemplo :
Buscamos que
4^3^5^2^9
sea evaluado as :
4^(3^(5^(2^9)))
Por lo contrario, las sumas y restas queremos resolverlas de izquierda a derecha:
Buscamos que
4-3+5+2-9
sea evaluado as :
(((4-3)+5)+2)-9
Usar este tipo de declaraciones es importante para disminuir la posibilidad de
ambigedades en el lenguaje generado.
%start
En algunos casos es conveniente indicarle a yacc cual es el smbolo (no terminal)
inicial a la hora de hacer el parseo, es decir, el smbolo que se trata de reducir, si
esta opcin no es especificada, yacc toma al primer smbolo de la seccin de
reglas como smbolo inicial.
En nuestro ejemplo, se presentan ambos casos, nuestro smbolo inicial "Input" se
encuentra al inicio del archivo y tambin esta declarado como smbolo inicial.
%start Input
Reglas
.
.
| MINUS Expression %prec NEG
.
.
.
Reduccin
{ $$=-$2; }
[ \t]+
[0-9]
{digit}+
%%
{white}
{ /* Ignoramos espacios en blanco */ }
"exit"|"quit"|"bye" {printf("Terminando programa\n");exit(0);}
{integer}
{
yylval.dval=atof(yytext);
return(NUMBER);
}
"+"
return(PLUS);
"-"
return(MINUS);
"*"
return(TIMES);
"/"
return(DIVIDE);
"^"
return(POWER);
"("
return(LEFT_PARENTHESIS);
")"
return(RIGHT_PARENTHESIS);
"\n" return(END);
%%
Acerca de la seccin de definiciones de este lexer, lo nico relevante que
podemos mencionar es la lnea de C que dice :
#include "y.tab.h"
esta lnea incluye al archivo y.tab.h que contiene algunas de las definiciones de
yacc que lex necesita para poder interactuar con el, entre las mas importantes se
encuentran definidas todas las etiquetas de los tokens, como PLUS, MINUS,
NUMBER, etctera. Estas constantes son los valores que yylex() regresara a
yyparse() (la funcin del parser de yacc) para identificar el tipo de token que recin
se ha ledo.
En la seccin de reglas, en la parte del cdigo, podemos ver como al final de cada
regla, se hace un return especificando la etiqueta que fue declarada como %token
o como %left/%rigth en la especificacin yacc.
Para compilar y correr este ejemplo en sistemas UNIX o similares :
$ lex ejem1.1.l
$ yacc -d ejem1.1.y
$ cc -o ejem1.1 lex.yy.c y.tab.c -ly -ll -lm
$ ejem1.1
25*5-5
Result: 120.000000
5^2*2
Result: 50.000000
5^(2*2)
Result: 625.000000
bye
Terminando programa
$
Subrutinas
0.7pre7
0.7
0.7.1
0.8pre1
0.8pre2
1.0
1.2
2.0
2.1
3.0
3.1
3.2
4.0
4.1
4.2
6.0
MODO DE USO
ESTRUCTURA GENERAL DE UN PROGRAMA EN JAVACC
Cualquier cdigo escrito para JavaCC obedece a la siguiente estructura:
ficheros son los mismos para cualquier gramtica y pueden conservarse de una
para otra.
Tokens:
Constantes:
Cadenas: Caracteres entrecomillados, ejemplo: "cadena"
Enteros: Nmeros positivos, ejemplo: 234 o 0
Lgicas: TRUE y FALSE
Identificadores: Todos los identificadores son una secuencia de letras (a-zA-Z) y
nmeros que obligatoriamente deben comenzar con una letra (y no un
nmero). Los identificadores que se refieran a cadenas terminarn en "$".
compilador.Start();
System.out.println ("ExampleParser: La entrada ha sido leida con xito.");
}
catch(ParseException e){
System.out.println ("ExampleParser: Ha ocurrido un error durante el
anlisis.");
System.out.println (e.getMessage());
}
catch(TokenMgrError e){
System.out.println ("ExampleParser: Ha ocurrido un error.");
System.out.println (e.getMessage());
}
}
}
PARSER_END(ExampleParser)
//ESTRUCTURAS Y CARACTERES DE ESCAPE
SKIP : {
""
| "\t"
| "\n"
| "\r"
| <"rem" (~["\n","\r"])* ("\n" | "\r" | "\r\n")>
}
//TOKENS ESTTICOS
TOKEN : {
<INTEGER_CONSTANT: (<DIGIT>)+>
| <LOGIC_CONSTANT: "true" | "false" | "-1">
| <STRING_CONSTANT: "\"" ( ~["\"","\\","\n","\r"] | "\\" ( ["n","t","b","r","f","\\","\'","\""] |
( ["\n","\r"] | "\r\n")))* "\"">
| <#DIGIT: ["0"-"9"]>
}
//PALABRAS RESERVADAS
TOKEN : {
<NOT: "not">
| <IF: "if">
| <END: "end">
| <SUB: "sub">
| <LET: "let">
| <CALL: "call">
| <THEN: "then">
| <CASE: "case">
| <ELSE: "else">
| <INPUT: "input">
| <PRINT: "print">
| <SELECT: "select">
| <STATIC: "static">
}
//TOKEN IDENTIFICADOR
TOKEN : {
<IDENTIFIER: <LETTER>(<LETTER>|<DIGIT>)*(["$"])?>
| <#LETTER: (["a"-"z","A"-"Z"])>
}
//UNIDAD PRINCIPAL
void Start () : {}
{
(
INTEGER_CONSTANT | STRING_CONSTANT | LOGIC_CONSTANT |
NOT | IF | END | SUB | LET | CALL | THEN | CASE | ELSE | INPUT | PRINT |
SELECT | STATIC |
IDENTIFIER
)*
<EOF>
}
Para compilar este fichero, se debe hacer con "javacc" y posteriormente con
"javac":
javacc exparser.jj
javac *.java
Para ejecutar el programa:
java ExampleParser fichero
CONCLUSIN
El analizador sintctico ya se a YACC o JavaCC recibe los tokens o palabras
generadas por el analizador lxico y determina si la secuencia u orden que
presentan es correcta y permitida por el lenguaje. La salida que generar ser el
rbol sintctico. Gracias a estas excelentes herramientas, nosotros como
programadores no tenemos que reinventar la rueda cuando de crear un analizador
sintctico se trate, las numerosas opciones que nos brindan los generadores
hacen que esta tarea sea mas fcil.
REFERENCIAS
http://es.wikipedia.org/wiki/Yacc
http://www.lcc.uma.es/~galvez/theme/IntroduccionAJavaCC.pdf
http://kiwwito.com/construir-un-analizador-lexico-con-javacc