|
En las prácticas anteriores hemos comentado la sintaxis básica de
JavaCC en cuanto a especificaciones léxicas y sintácticas. Las primeras se
basan en declaraciones de cuatro tipos: TOKEN, SKIP, MORE y SPECIALTOKEN.
Las especificaciones sintácticas, por su parte, se basan en la descripción
de la función asociada a cada símbolo no terminal por medio de una expresión
EBNF. Existe un tipo de especificación sintáctica especial que no utiliza el
formato de descripción EBNF sino una descripción directa de la función en
Java. La sintaxis de este tipo de especificación es la siguiente:
Este tipo de descripción sintáctica se suele utilizar para la gestión de
errores, ya que permite definir métodos de tipo skipTo(), es decir,
métodos para eliminar tokens hasta llegar a un token de un determinado tipo
que sirve como token de sincronismo. A continuación se muestran dos ejemplos
de definición de este tipo de función.
JAVACODE
void skipTo(int kind) {
Token tok = getToken(0);
while(tok.kind != EOF && tok.kind != kind) tok = getNextToken();
}
JAVACODE
void skipTo(String st) {
Token tok = getToken(0);
while(tok.kind != EOF && !tok.image.equals(st)) tok =
getNextToken();
}
|
El primer ejemplo define una función de sincronismo basada en el código del
token deseado (parámetro kind). Este código es un valor entero que se
almacena en el campo kind de los objetos de la clase Token. El
segundo ejemplo define una función de sincronismo basada en el lexema del
token deseado (parámetro st). Este lexema se almacena en el campo
image de los objetos de la clase Token.
El código de estas funciones utiliza los métodos getToken() y
getNextToken(), definidos en todos los analizadores sintácticos
generados por JavaCC. El método getToken(int i) devuelve el i-ésimo
token disponible en el flujo de entrada, de manera que para el valor 0 se
devuelve el último token consumido. Este método no supone un avance en la
cadena de tokens del flujo de entrada. Por su parte, el método
getNextToken() devuelve el siguiente token disponible en el flujo de
entrada y lo consume, es decir, lo elimina de la entrada.
Otro aspecto importante del contenido de estas funciones es que pueden
utilizar los identificadores léxicos de los tokens como valores enteros,
como el identificador EOF en estos ejemplos. Esto es posible porque estos
identificadores se utilizan para generar constantes enteras (definidas en la
interfaz NombreAnalizadorConstants) que están disponibles en el
código del analizador sintáctico.
Las funciones definidas por medio de declaraciones JAVACODE pueden ser
utilizadas como símbolos no terminales y, como tales, pueden incluirse en
las especificaciones EBNF de otras producciones. Es importante tener en
cuenta que con este tipo de definición no es posible calcular el conjunto de
predicción, por lo que no deben utilizarse nunca como primer elemento de una
definición EBNF, ya que el analizador no sabría si ante un determinado token
debe ejecutar ese símbolo o no.
A continuación se presenta una versión más elaborada de la función skipTo().
En este caso se definen dos listas de tokens: left y right.
La lista left contiene los tokens de sincronismo que deben quedar a la
izquierda del punto de sincronización, es decir, como tokens ya leídos.
La lista right contiene los tokens de sincronismo que deben quedar a la
derecha del punto de sincronización, es decir, como los siguientes tokens a leer.
La función skipTo() consume tokens hasta encontrar el final de entrada
o uno de los tokens de sincronismo. Si el token se encuentra en la lista left
también se consume. Si se encuentra en la lista right se deja como siguiente
token a consumir.
JAVACODE
void skipTo(int[] left, int[] right) {
Token prev = getToken(0);
Token next = getToken(1);
boolean flag = false;
if(prev.kind == EOF || next.kind == EOF) flag = true;
for(int i=0; i<left.length; i++) if(prev.kind == left[i]) flag =
true;
for(int i=0; i<right.length; i++) if(next.kind == right[i]) flag =
true;
while(!flag) {
getNextToken();
prev = getToken(0);
next = getToken(1);
if(prev.kind == EOF || next.kind == EOF) flag =
true;
for(int i=0; i<left.length; i++) if(prev.kind ==
left[i]) flag = true;
for(int i=0; i<right.length; i++) if(next.kind ==
right[i]) flag = true;
}
} |
|