Sei sulla pagina 1di 23

GENERADOR DE ANALIZADOR SINTCTICO YACC

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
bison, 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; el cdigo fuente est
disponible con las distribuciones estndares del Plan 9 y de OpenSolaris.
Es correcto decir que yacc es una herramienta que sirve para generar un
programa, capaz de analizar gramaticalmente una entrada dada por lex, a partir de
una especificacin. Esta especificacin, debe contener los tokens reconocidos y
Jose Alfredo Jimenez Cornejo
Romeo H. Vazquez Velazquez

03/06/2015

los tipos de datos de los mismos si es que se ocupan para realizar operaciones
sobre ellos, y una especificacin de gramtica en un formato similar a BNF
(Backus Naus Form), que va desde el smbolo no terminal ms general a cada una
de las opciones terminales.
Ejemplo:
<Expresion>
Numero + <Expresion>
Numero - <Expresion>
Numero
En el ejemplo podemos ver que <Expresion> es un smbolo no terminal que est
compuesto por un "Numero" terminal seguido de un smbolo '+' o '-' terminales
seguido por un <Expresion> no terminal, que a su vez puede ser otro nmero u
otra expresin ms compleja.
Es importante notar que esta especificacin es recursiva sobre <Expresion> pero
no es ambigua, es decir, siempre se llegara a un terminal.
Una especificacin yacc se divide en tres secciones diferentes de manera similar a
lex, la de definiciones, la de reglas, y la de subrutinas, que van igualmente
separadas por un '%%', mismas que pueden incluir cdigo de C encerrado entre
un %{ y un %}.

Ejemplo 1.1 (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;
Jose Alfredo Jimenez Cornejo
Romeo H. Vazquez Velazquez

03/06/2015

}
%token
%token
%token
%token

<dval> NUMBER
PLUS MINUS TIMES DIVIDE POWER
LEFT_PARENTHESIS
RIGHT_PARENTHESIS
END

%left PLUS MINUS


%left TIMES DIVIDE
%left NEG
%right POWER
%type <dval> Expression
%start Input
%%
Input: Line
| Input Line
;
Line: END
| Expression END
;
Expression: NUMBER

{ printf("Result: %f\n",$1); }

{ $$=$1; }

| Expression PLUS Expression { $$=$1+$3; }


| Expression MINUS Expression { $$=$1-$3; }
| Expression TIMES Expression { $$=$1*$3; }
| Expression DIVIDE Expression { $$=$1/$3; }
| MINUS Expression %prec NEG { $$=-$2; }
| Expression POWER Expression { $$=pow($1,$3); }
| LEFT_PARENTHESIS Expression RIGHT_PARENTHESIS { $$=$2; }
;
%%
int yyerror(char *s) {
printf("%s\n",s);
}
int main(void) {
yyparse();
}
Jose Alfredo Jimenez Cornejo
Romeo H. Vazquez Velazquez

03/06/2015

Definiciones
En esta primera seccin, al igual que en lex, incluimos las libreras que usaremos
en el programa, definiciones de los tokens, tipos de datos y precedencia de la
gramtica.
%union
Esta definicin, se traduce a una unin de C que a su vez dar el tipo de dato a
una variable global de nombre yylval que ser de donde yacc tomara los datos a
procesar, en la unin se definen miembros cuyos correspondientes tipos de datos
sern usados para dar el tipo de dato a los tokens como se explicara en la
siguiente seccin. %union se traduce de la siguiente forma:
En yacc :
%union{
double dval;
}
En C :
typedef union
{
double dval;
} YYSTYPE;
Con esta definicin, yacc declara algunas uniones de este tipo, de las cuales la
ms importante es:

YYSTYPE yylval;

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 qu tipo es cada token devuelto por
yylex(), se usan las definiciones %token y %type.
Jose Alfredo Jimenez Cornejo
Romeo H. Vazquez Velazquez

03/06/2015

%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 <dval> NUMBER


%token PLUS

MINUS TIMES DIVIDE POWER

%token LEFT_PARENTHESIS

RIGHT_PARENTHESIS

%token END
%type <dval> Expression
Jose Alfredo Jimenez Cornejo
Romeo H. Vazquez Velazquez

03/06/2015

La primera lnea indica que el token NUMERO ser del tipo de miembro de dval,
es decir, un double.
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 ltima lnea definimos el tipo de dato que usara nuestro no terminal
Expression.

%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 s misma, y la agrupacin
de los operadores.

Precedencia
La precedencia es asignada en orden inverso al que aparecen, es decir, el ltimo
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)))

Jose Alfredo Jimenez Cornejo


Romeo H. Vazquez Velazquez

03/06/2015

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 cul es el smbolo (no terminal)
inicial a la hora de hacer el paseo, 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 est declarado como smbolo inicial.
%start Input

Reglas

En esta parte finalmente llegamos a la definicin de la gramtica, ac podemos


observar que cada smbolo se define con su nombre, seguido de dos puntos ":"
seguidos de varios smbolos que conformaran su composicin gramatical que en
caso de tener varias opciones, son separados por "|" (or) indicando que se tienen
varias opciones para reducir ese smbolo y para terminar cada regla, un ";".

Ejemplo:
Si tomamos la gramtica que definimos al principio de esta seccin:

Jose Alfredo Jimenez Cornejo


Romeo H. Vazquez Velazquez

03/06/2015

<Expresion>
Numero + <Expresion>
Numero - <Expresion>
Numero
Y la transformamos a una regla de yacc, se vera como esto:

Expresion: NUMERO '+' Expresion


{$$ = $1 + $3;}
| NUMERO '-' Expresion
{$$ = $1 - $3;}
| NUMERO
{$$ = $1;}
;
En el ejemplo ya transformado a una regla gramatical de yacc, podemos ver que
ya se especifica que hacer con los smbolos de la gramtica una vez que son
resueltos en la parte de cdigo de C. En este ejemplo, Expresion es el smbolo no
terminal que se est definiendo de manera recursiva, el valor que tomara una vez
resuelto es el valor asignado a la variable $$, que traducida a C es una variable
mapeada al smbolo no terminal, $1, $2 y $3 son variables que son mapeadas al
valor de los smbolos de cada lnea de cada regla, estas son numeradas de
izquierda a derecha.

Ejemplo:

En este segmento de regla:

Expresion: NUMERO '+' Expresion

Expresion equivale a $$
NUMERO equivale a $1
'+' equivale a $2
y
Expresion (la parte recursiva) equivale a $3

Jose Alfredo Jimenez Cornejo


Romeo H. Vazquez Velazquez

03/06/2015

Todo esto claro, en la parte de acciones en C para cada lnea de la regla en yacc.
En el ejemplo tambin podemos encontrar el uso de %prec que sirve para cambiar
la precedencia de un operador dependiendo del contexto en el ejemplo, le
estamos dando una ms alta precedencia a la operacin "menos" cuando est en
un contexto unario, que a la mayora de los operadores excepto el POWER
(exponente):
.
.
.
| MINUS Expression %prec NEG

{ $$=-$2; }

.
.
Reduccin

Yacc reduce sus reglas generando un parse tree (no literalmente), y va


resolviendo cada regla completa tan pronto como puede, lo cual nos trae un
detalle de diseo de gramticas en yacc, y es la diferencia entre especificar la
recursividad por la derecha o por la izquierda, para expresiones muy sencillas que
generen un parse tree pequeo no hay ningn problema pero para casos donde la
reduccin es compleja, puede desbordar la pila ya que cuando la recursin es
derecha, para resolverla, tiene que guardar los datos de la izquierda, y si estos
son demasiados, no puede manejarlos.
Por lo contrario, cuando la recursin es izquierda, no tiene que guardar datos que
no va a utilizar por que recorre el rbol de izquierda a derecha y resuelve las
reglas tan pronto como puede.
En el ejemplo anterior tenemos la recursin por la derecha, un anlogo recurrente
por la izquierda seri como este:
Expresion: Expresion '+' NUMERO
{$$ = $1 + $3;}
| Expresion '-' NUMERO
{$$ = $1 - $3;}
| NUMERO
{$$ = $1;}
;
Jose Alfredo Jimenez Cornejo
Romeo H. Vazquez Velazquez

03/06/2015

10

Especificacin de Lex para el ejemplo1.1

Para que el Ejemplo1.1 pueda funcionar, al igual que cualquier otro programa en
yacc,

necesita

un

tokenizer,

continuacin

tenemos

su

tokenizer

correspondiente escrito en lex.

%{
#include "y.tab.h"
#include <stdlib.h>
#include <stdio.h>
%}
white
digit
integer

[ \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);
%%

Jose Alfredo Jimenez Cornejo


Romeo H. Vazquez Velazquez

03/06/2015

11

Acerca de la seccin de definiciones de este lexer, lo unico 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 l, entre las ms 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 cmo %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
En esta ltima seccin, es posible re implementar, siguiendo la misma idea de lex,
algunas funciones que pueden ser tiles en algn momento dado o declarar
nuevas funciones para usar dentro de nuestro cdigo o nuestras reglas, no hay
mucho que reimplementat a este nivel (yacc) a menos que sea con propsitos
Jose Alfredo Jimenez Cornejo
Romeo H. Vazquez Velazquez

03/06/2015

12

realmente especficos. Las funciones ms comnmente implementadas son main()


e yyerror(), la primera se usa para personalizar el programa con mensajes antes o
despus del parser, o para llamarlo varias veces en el cdigo y la segunda la
ocupa yyparse() cada vez que encuentra un error de sintaxis, en este ejemplo, se
incluyen ambas con su contenido mnimo.

Jose Alfredo Jimenez Cornejo


Romeo H. Vazquez Velazquez

03/06/2015

13

GENERADOR DE ANALIZADOR SINTCTICO JAVACC

JavaCC (Java Compiler Compiler) es un generador de analizadores sintcticos de


cdigo abierto para el lenguaje de programacin Java. JavaCC es similar a Yacc
en que genera un parser para una gramtica presentada en notacin BNF, con la
diferencia de que la salida es en cdigo Java. A diferencia de Yacc, JavaCC
genera analizadores descendentes (top-down), lo que lo limita a la clase de
gramticas LL(K) (en particular, la recursin desde izquierda no se puede usar). El
constructor de rboles que lo acompaa, JJTree, construye rboles de abajo hacia
arriba (bottom-up).

JavaCC est licenciado bajo una licencia BSD.

En 1996, Sun Microsystems liber un parser llamado Jack. Los desarrolladores


responsables de Jack crearon su propia compaa llamada Metamata y cambiaron
el nombre Jack a JavaCC. Metamata se convirti en WebGain. Despus de
queWebGain finalizara sus operaciones, JavaCC se traslad a su ubicacin
actual.
JavaCC integra las funciones de anlisis lxico y anlisis sintctico en una sola
herramienta, obteniendo a la salida cdigo java a diferencia de lex/yacc cuya
salida es cdigo C-.
Antes que nada veamos un ejemplo sobre el cual se va a ir haciendo un
seguimiento de cada uno de los distintos puntos que vayamos comentando. En l
se muestra una gramtica reconocedora de una calculadora. Ms adelante, en
Jose Alfredo Jimenez Cornejo
Romeo H. Vazquez Velazquez

03/06/2015

14

este mismo captulo, lo modificaremos con el objetivo de mostrar mayores


funcionalidades que aporta JavaCC.
La principal diferencia con lex/yacc es que los analizadores sintcticos generados
por JavaCC son analizadores sintcticos descendentes de tipo LL(k), mientras
que los analizadores sintcticos generados por yacc son de tipo ascendente
LALR. Otra diferencia bastante importante es que las especificaciones lxicas
pueden estar incluidas dentro de la especificacin de la gramtica. Por ejemplo,
podemos escribir algo como esto:

sentenciaIf : {} { "if" "(" expresion() ")" sentencia() } .


y automticamente se ocupa de generar tokens para if y para (.

Tambin es necesario resear que el anlisis lxico en JavaCC genera


combinaciones de autmatas finitos deterministas (AFD) y autmatas finitos no
deterministas (AFND); por contra, lex siempre genera autmatas finitos
deterministas.

ESTRUCTURA GENERAL DE UN PROGRAMA EN JAVACC

El fichero de entrada comienza con una lista de opciones, la cual es opcional.


Seguidamente nos encontramos la unidad de compilacin java la cual se
encuentra encerrada entre PARSER_BEGIN(nombre) y PARSER_END(nombre).
Despus nos encontramos con una lista de reglas de produccin (cada una de
estas partes son claramente distinguibles en el ejemplo 2.1.).
El nombre que sigue a PARSER_BEGIN y a PARSER_END debe ser el mismo, y
ste identifica al analizador sintctico que va a ser generado con posterioridad.
Por ejemplo si nuestro nombre es Gramtica, se generan los siguientes archivos:
Gramatica.java: El analizador sintctico.
GramaticaTokenManager.java: gestor de tokens para el analizador lxico.
Jose Alfredo Jimenez Cornejo
Romeo H. Vazquez Velazquez

03/06/2015

15

GramaticaConstants.java: El analizador lxico no maneja los tokens por el

Opciones_javacc
PARSER_BEGIN (<IDENTIFICADOR>)
Unidad de compilacin java
PARSER_END (<IDENTIFICADOR>)
(reglas de produccion)*
<EOF>
Nombre que especificamos nosotros en el cdigo, sino que a cada uno de ellos le
asigna un nmero. En este archivo se encuentra cada uno de los tokens junto con
el nmero que le ha sido asignado por el analizador lxico.

OPCIONES
1
La seccin options, si est presente, comienza con la palabra reservada options
seguida de una lista de una o ms opciones entre llaves. La misma opcin no
debe establecerse ms de una vez. En el ejemplo 2.1. Slo se ha usado una de
las opciones
(
LOOKAHEAD=1).

Las opciones pueden ser especificadas tanto en el archivo de la gramtica como


en la lnea de comandos. En caso de estar presentes en el archivo, stas deben
aparecer al principio precedidas por la palabra options. Y en el caso en que se
especifiquen en la lnea de comandos, stas tienen mayor prioridad que las
especificadas en el cdigo.
opciones::= [ options { (valores_de_las_opciones)* }]
Los nombres de las opciones van en maysculas.

Jose Alfredo Jimenez Cornejo


Romeo H. Vazquez Velazquez

03/06/2015

16

A continuacin se detallan brevemente las opciones ms importantes y las


funciones que realizan:
LOOKAHEAD: Indica la cantidad de tokens que son tenidos en cuenta por
el parser antes de tomar una decisin de parsing. El valor por defecto es 1,
con lo cual se trabaja con parser LL(1), aunque el nmero puede variar para
alguna/s produccin/es en particular. Cuanto ms pequeo sea el nmero
de tokens del lookahead, ms veloz ser el parser.
Valor por defecto: 1
CHOICE_AMBIGUITY_CHECK: Es el nmero de tokens que se consideran
en las elecciones de chequeo por ambigedad, de la forma "A | B | ...". Por
ejemplo: si existen dos tokens prefijos comunes para A y B, pero no tres;
entonces por medio de esta opcin, JavaCC permite usar un lookahead de
3 con propsitos de desambiguacin. La utilizacin de esta opcin provee
mayor informacin y flexibilidad ante las ambigedades, pero a costo de un
mayor tiempo de procesamiento.
Valor por defecto: 2
STATIC: Si el valor es true, todos los mtodos y clases variables se
especifican como estticas en el parser generado y en el token manager.
Esto permite la presencia de un nico objeto parser, pero mejora la
actuacin del procedimiento de parsing. Para ejecutar mltiples parsers
durante la ejecucin de un programa Java, debe invocarse el mtodo
ReInit() para reinicializar el parser, si es que haba sido definido como
esttico. Si el parser no es esttico, puede utilizarse el operador "new" para
construir los parsers que sean necesarios; los mismos, podran ser usados
simultneamente en threads diferentes.
Valor por defecto: true
DEBUG_PARSER: Esta opcin se utiliza para obtener informacin de
debugging desde el parser generado. Si el valor de la opcin es true, el
parser genera un trace de sus acciones. Dichos traces pueden
deshabilitarse por medio de la invocacin del mtodo disable_tracing() en la
Jose Alfredo Jimenez Cornejo
Romeo H. Vazquez Velazquez

03/06/2015

17

clase del parser generado, y, recprocamente, pueden habilitarse con el


mtodo enable_tracing().
Valor por defecto: false
IGNORE_CASE: Si su valor es true, el token manager generado no efecta
diferencia entre letras maysculas y minsculas en las especificaciones de
tokens y los archivos de entrada. Es muy usado para escribir gramticas
para lenguajes como HTML.
Valor por defecto: false.
FORCE_LA_CHECK: Esta opcin determina los controles de chequeo de
ambigedad de lookahead ejecutados por JavaCC. Cuando el valor es
false, el chequeo de ambigedad del lookahead se ejecuta para todos los
puntos de eleccin donde se usa el lookahead de 1 por defecto. El chequeo
de ambigedad de lookahead no se ejecuta en los puntos donde existe una
especificacin explcita de lookahead, o si la opcin LOOKAHEAD tiene un
valor distinto de 1.
Valor por defecto: false.
COMMON_TOKEN_ACTION: Si el valor de esta opcin es true, todas las
invocaciones del mtodo "getNextToken()" del token manager, causan una
llamada al mtodo definido "CommonTokenAction()" despus de que el
token haya sido ledo por el token manager. El usuario debe definir este
mtodo en la seccin TOKEN_MGR_DECLS. La signatura de este mtodo
es:
Valor por defecto: false.
OUTPUT_DIRECTORY: Es una opcin cuyo valor por defecto es el void
CommonTokenAction(Token t)

directorio actual. Controla dnde son

generados los archivos de salida.


Valor por defecto: directorio actual.

Jose Alfredo Jimenez Cornejo


Romeo H. Vazquez Velazquez

03/06/2015

18

REGLAS DE PRODUCCIN

Existen cuatro tipos de reglas de produccin en JavaCC: las reglas de produccin


javacode y BNF se usan para definir la gramtica a partir de la cual va a generarse
el analizador sintctico. Las Reglas de produccin mediante expresiones
regulares, se usan para definir los tokens de la gramtica el token manager es
generado a partir de esta informacin-. La seccin token manager decls se usa
para introducir declaraciones en el analizador sintctico que se generar con
posterioridad.

Reglas de produccin Javacode

Las reglas de produccin JAVACODE son una forma de escribir cdigo Java para
una regla de produccin en lugar de las tpicas expansiones EBNF. Esto suele ser
til cuando tenemos la necesidad de reconocer algo que no es de contexto libre o
algo para lo cual es difcil escribir una gramtica que lo reconozca. Para ello nos
serviremos de los mtodos que el propio parser nos proporciona con tal fin, a
saber:
getToken(),
jj_concume_token(), getNextToken(),etc. A continuacin se muestra un ejemplo de
uso de JAVACODE. En el ejemplo 2.3., el no terminal skipToMatchingBrace
consume tokens de la entrada hasta que encuentra una }.
JAVACODE
void skipToMatchingBrace(){
Token tok;
int nesting=1;
while(true){
tok=getToken(1);
if(tok.kind==LBRACE)
nesting++;
if(tok.kind==RBRACE){
nesting--;
if(nesting==0)
Jose Alfredo Jimenez Cornejo
Romeo H. Vazquez Velazquez

03/06/2015

19

break;
}
tok=getNextToken();
}
}
Cuando usamos este tipo de reglas de produccin tenemos que tener muchsimo
cuidado ya que es muy fcil especificar bastante ms de lo que realmente se
quiere.
Este tipo de reglas son problemticas cuando las utilizamos en lugares donde el
analizador sintctico tiene que hacer una eleccin. Supongamos que la regla de
produccin anterior es referenciada desde sta:
Reglas de produccin BNF
Las reglas de produccin BNF son la forma estndar de especificar gramticas en
JavaCC. Sobre ella diferenciaremos cada uno de los puntos que a continuacin se
exponen:

void unario() :
{}
{
}

<MENOS> elemento()
elemento()

Cada regla de produccin BNF consta de una parte izquierda la cual consiste en la
especificacin de un no terminal. Las reglas de produccin BNF definen este no
terminal en funcin de expansiones BNF que se encuentran en la parte derecha.
El no terminal se escribe exactamente de la misma forma en que se declara un
mtodo en
Java, (en el ejemplo 2.6. void unario():) . Ya que cada no terminal se traduce a
un mtodo en el analizador sintctico, la forma de escribir el no terminal hace que
esta asociacin sea obvia. El nombre del no terminal es el nombre del mtodo, y
Jose Alfredo Jimenez Cornejo
Romeo H. Vazquez Velazquez

03/06/2015

20

los parmetros y el valor de retorno son los medios para pasar valores arriba y
abajo dentro del rbol sintctico. parse tree- (Esto se mostrar ms adelante).

La parte derecha de una regla de produccin BNF tiene dos partes: La primera
parte es un conjunto de declaraciones y cdigo Java ( bloque Java). Este cdigo
es generado al principio del mtodo relacionado con el no terminal. Por lo tanto,
cada vez que el no terminal es usado en el proceso de anlisis, este cdigo es
ejecutado.
La segunda parte est formada por las expansiones BNF.
bnf_production::= java_return_type java_identifier ( java_parameter_list ) :
java_block
{ expansion_choices }
Reglas de produccin mediante expresiones regulares

Las expresiones regulares se usan para definir entidades lxicas las cuales van a
ser procesadas por el token manager. Una regla de produccin de este tipo
comienza con la especificacin de los estados lxicos a los cuales es aplicable.

regular_expr_production::= [lista_de_estados_lexicos]
regexpr_kind[ [ IGNORE CASE ] ] :
{ regexpr_spec ( | regexpr_spec )* }
Existe un estado lxico estndar llamado DEFAULT. Si la lista de estados lxicos
se omite, la expresin regular se aplica al estado DEFAULT.
lista_de_estados_lexicos::= < * >
| < identificador_java (, identificador_java)* >

La lista de estados lxicos describe el conjunto de estados lxicos, para los cuales
la expresin regular es aplicable. Si sta es < * >, entonces la expresin
Jose Alfredo Jimenez Cornejo
Romeo H. Vazquez Velazquez

03/06/2015

21

regular se puede aplicar a todos los estados lxicos. En otro caso se aplicar a
todos los estados lxicos cuyo identificador se encuentre en la lista de
identificadores del interior de los parntesis angulares.
A continuacin se especifica el tipo de regla de produccin. Existen cuatro tipos
basadas en expresiones regulares:
o TOKEN: La expresin regular de esta regla de produccin describe tokens
de la gramtica. El token manager crea un objeto de tipo Token para cada
una de las entradas que se ajusten a dicha expresin regular, y luego lo
retorna al analizador sintctico.
o SPECIAL_TOKEN: La expresin regular de esta regla de produccin
describe tokens especiales.

Los tokens especiales son iguales que los

tokens slo que no tienen importancia durante el anlisis sintctico. Son


muy tiles cuando hay que procesar entidades lxicas,

del tipo de

comentarios, las cuales no tienen ninguna importancia para el anlisis


sintctico, pero son una parte muy importante del fichero de entrada.
o SKIP: Cualquier entrada que se ajuste a la expresin regular especificada
en esta seccin ser ignorada por el token manager.
o MORE: A veces es necesario ir construyendo el token que vamos a pasar al
analizador sintctico de forma gradual. Las entradas que se ajustan a la
expresin regular son guardadas en un buffer hasta que llega el siguiente
o TOKEN o SPECIAL_TOKEN. Despus todos los valores almacenados en el
buffer son concatenados para formar un solo TOKEN o SPECIAL_TOKEN y
es entonces cuando se enva al analizador sintctico. En el caso en que
una entrada encajara con una expresin regular especificada en la seccin
SKIP, el contenido del buffer es desechado.

VERSIONES:

Jose Alfredo Jimenez Cornejo


Romeo H. Vazquez Velazquez

03/06/2015

22

Updated
Size
Title + Description + Tags

Actions

Date

beta

about 1 year ago

Beta / Nightly Versions


Tags: beta nightly javacc

contrib

over 4 years ago

Contributions, including grammars


Tags: --

oldversions

over 4 years ago

Older versions of JavaCC


Tags: --

releases

almost 2 years ago

JavaCC releases
Tags: --

javacc-5.0.tar.gz

over 4 years ago

461.6
KB

over 4 years ago

627.3
KB

over 4 years ago

791.1
KB

over 4 years ago

1.1 MB

about 1 year ago

726.7
KB

about 1 year ago

1.2 MB

javacc-5.0.tar.gz
Tags: -javacc-5.0.zip
javacc-5.0.zip
Tags: -javacc-5.0src.tar.gz
javacc-5.0src.tar.gz
Tags: -javacc-5.0src.zip
javacc-5.0src.zip
Tags: -javacc-6.0.zip
This is the binary distribution of JavaCC 6.0.1 - with
few bug fixes.
Tags: javacc 6.0 c++ code parser lexer 6.0.1
JavacCC-6.0src.zip
JavaCC 6.0 source distribution
Tags: javacc c++ parser lexer genertor

Jose Alfredo Jimenez Cornejo


Romeo H. Vazquez Velazquez

03/06/2015

23

Conclusin
Como hemos visto un generador de analizadores sintcticos es un programa que
toma como su entrada una especificacin de la sintaxis de un lenguaje en alguna
forma, y produce como su salida un procedimiento de anlisis sintctico para ese
lenguaje, tambin sabemos ahora que un generador de analizadores sintcticos
ampliamente utilizado que incorpora el algoritmo de anlisis sintctico LR(1) se
conoce como Yacc, este no es directamente un analizador sino un generador de
analizadores. A partir de un fichero fuente en yacc, se genera un fichero fuente en
C que contiene el analizador sintctico.
Por otra parte el generador de analizadores sintcticos JavaCC integra las
funciones de anlisis lxico y anlisis sintctico en una sola herramienta,
obteniendo a la salida cdigo java a diferencia de lex/yacc cuya salida es cdigo
C.
La principal diferencia con lex/yacc es que los analizadores sintcticos generados
por JavaCC son analizadores sintcticos descendentes de tipo LL(k), mientras que
los analizadores sintcticos generados por yacc son de tipo ascendente LALR.
Otra diferencia bastante importante es que las especificaciones lxicas pueden
estar incluidas dentro de la especificacin de la gramtica.
De manera general JavaCC es similar a Yacc en que genera un parser para una
gramtica presentada en notacin BNF, con la diferencia de que la salida es en
cdigo Java. A diferencia de Yacc, JavaCC genera analizadores descendentes
(top-down), lo que lo limita a la clase de gramticas LL(K) (en particular, la
recursin desde izquierda no se puede usar).
Otra diferencia bastante importante es que las especificaciones lxicas pueden
estar incluidas dentro de la especificacin de la gramtica.
JavaCC es quiz el generador de parsers usado con aplicaciones Java ms
popular.

Referencias:
http://www.lcc.uma.es/~galvez/theme/IntroduccionAJavaCC.pdf
http://www.udb.edu.sv/udb/archivo/guia/informatica-ingenieria/compiladores/2014/ii/guia-5.pdf
http://es.wikipedia.org/wiki/JavaCC
http://es.wikipedia.org/wiki/Yacc
Jose Alfredo Jimenez Cornejo
Romeo H. Vazquez Velazquez

03/06/2015

24

Potrebbero piacerti anche