javacc

53
Una introducción al generador JavaCC José Gabriel Pérez Díez Departamento L.P.S.I. Escuela Universitaria de Informática Universidad Politécnica de Madrid Enero 2010

Upload: neoslayfer

Post on 08-Aug-2015

58 views

Category:

Documents


1 download

TRANSCRIPT

Page 1: JavaCC

Una introduccioacuten al generador JavaCC

Joseacute Gabriel Peacuterez Diacuteez

Departamento LPSI

Escuela Universitaria de Informaacutetica

Universidad Politeacutecnica de Madrid

Enero 2010

Contenido

Principios baacutesicos del generador JavaCC2Descripcioacuten inicial2Instalacioacuten y documentacioacuten2Obtencioacuten de un analizador

leacutexico-sintaacutectico3Ejemplo de presentacioacuten4Analizadores generados5Forma de una especificacioacuten JavaCC6Seccioacuten de

opciones6Seccioacuten de ejecucioacuten7Seccioacuten de sintaxis8Seccioacuten de lexicografiacutea11Tareas asociadas a la estructura

sintaacutectica16Bloque para un siacutembolo16Acciones sintaacutecticas16Valor comunicado por un meacutetodo18Comunicacioacuten entre los

analizadores leacutexico y sintaacutectico22Tareas asociadas a las piezas sintaacutecticas25Accioacuten ligada a una pieza sintaacutectica25Bloque

de declaraciones lexicograacuteficas27Declaraciones lexicograacuteficas predefinidas27Accioacuten lexicograacutefica comuacuten30A modo de

recapitulacioacuten34Precisiones sobre el analizador sintaacutectico generado36

1

Principios baacutesicos del generador JavaCC

loz Descripcioacuten inicialEl generador JavaCC (Java Compiler Compiler) es una herramienta para generar analizadores de lengua-jes acepta como entrada una especificacioacuten de un determinado lenguaje y produce como salida un analiza-dor para ese lenguaje el analizador generado estaacute escrito en Java La especificacioacuten proporcionada al gene-rador JavaCC puede contemplar distintos aspectos del lenguaje para el que se quiere obtener el analizador

- Caracteriacutesticas lexicograacuteficas y sintaacutecticases la forma maacutes frecuente de uso del generador la especificacioacuten proporcionada define las caracteriacutesti -cas sintaacutecticas y lexicograacuteficas de un lenguaje y se genera un analizador leacutexico-sintaacutectico del lenguaje especificado

- Caracteriacutesticas lexicograacuteficasen la especificacioacuten proporcionada al generador soacutelo se definen caracteriacutesticas lexicograacuteficas del lengua-je con el coacutedigo generado se puede obtener un analizador lexicograacutefico

- Caracteriacutesticas lexicograacuteficas y sintaacutecticas y comprobaciones semaacutenticastambieacuten es posible completar una especificacioacuten leacutexico-sintaacutectica con la inclusioacuten de coacutedigo Java com-plementario para que el programa generado (que incorpora adecuadamente ese coacutedigo auxiliar) pueda hacer un anaacutelisis completo (leacutexico sintaacutectico y semaacutentico) del lenguaje especificado

loz Instalacioacuten y documentacioacutenDado que el coacutedigo generado por JavaCC estaacute escrito en Java es necesario disponer de una versioacuten del sis-tema Java (compilador de Java e inteacuterprete de la Maacutequina Virtual Java) Son programas de libre distribu-cioacuten y faacuteciles de conseguir

El generador JavaCC tambieacuten es un programa de libre distribucioacuten se puede conseguir en

la paacutegina oficial de JavaCC httpsjavaccdevjavanet

la paacutegina de la asignatura

(se tiene la versioacuten Java Compiler Compiler version 50)

Se consigue un fichero empaquetado de nombre javacc-50zip tras desempaquetar (en un determi-nado directorio que puede elegirse como convenga) dicho fichero se tienen instalados entre otros los si-guientes ficheros (que son los que interesan para el sistema operativo windows)

middotmiddotmiddotmiddot javacc-50binjavaccbatmiddotmiddotmiddotmiddot javacc-50binjjdocbatmiddotmiddotmiddotmiddot javacc-50binjjtreebat

los nombre de estos ficheros se corresponden con los nombres de los comandos para llamar a las herra-mientas instaladas

javacc generador de analizadores

jjdoc productor de documentacioacuten

jjtree preprocesador de apoyo para tareas semaacutenticas

Para que la llamada a estos comandos pueda realizarse desde cualquier directorio ha de anotarse el camino (PATH) que lleve hasta middotmiddotmiddotmiddot javacc-50bin Tambieacuten ha de tenerse en cuenta si la instalacioacuten del sistema Java se tiene preparada para que el compilador (javac) y el inteacuterprete (java) se puedan ejecutar desde cualquier directorio

Para comprobar si la instalacioacuten del generador se ha realizado adecuadamente se puede llamar desde la liacute-nea de comandos al generador javacc y apareceraacute por pantalla una informacioacuten sobre el uso de dicho comando la primera liacutenea de esa informacioacuten es

Java Compiler Compiler Version 50 (Parser Generator)

2

Documentacioacuten sobre JavaCC

Se dispone de abundante documentacioacuten relativa a JavaCC entre otras se pueden encontrar

documentacioacuten que acompantildea a los ficheros de la versioacuten instalada se tienen diversos ficheros enmiddotmiddotmiddotmiddot javacc-50dochtml

(en javaccgrmhtml se tiene una descripcioacuten general del generador)

documentacioacuten variada en la red

JavaCC DocumentationThe JavaCC TutorialIntroduction to JavaCCThe JavaCC FAQetc

Primera prueba con JavaCC

En lo que sigue se expone un ldquoejemplo de presentacioacutenrdquo completo la generacioacuten de un analizador leacutexico-sintaacutectico para un tipo de expresiones muy sencillo Se puede empezar el estudio de JavaCC probando su funcionamiento con ese ejemplo

loz Obtencioacuten de un analizador leacutexico-sintaacutectico

bull Pasos para la generacioacuten del analizador

1- Edicioacuten de la especificacioacuten (editor de texto plano)

vi | edit |∙ ∙ ∙ NombreFicherojj

(el nombre del fichero puede tener cualquier extensioacuten suele usarse jj)

2- Ejecucioacuten del generador

javacc NombreFicherojj

Si el nombre elegido para la especificacioacuten es NombreDeLaEspecif (maacutes adelante se indica la manera de dar un nombre a la especificacioacuten) como resultado de la generacioacuten se obtiene (ademaacutes de otros ficheros auxiliares) el fichero

NombreDeLaEspecifjava

3- Compilacioacuten del analizador generado

javac NombreDeLaEspecifjava

Como resultado de la compilacioacuten se obtiene (ademaacutes de otras clases auxiliares) el ficheroNombreDeLaEspecifclass

bull Ejecucioacuten del analizador generadoSi el nombre del fichero donde se encuentra el texto fuente (escrito en el lenguaje para el que se ha generado el analizador) que se pretende analizar es Programalen

java NombreDeLaEspecif lt Programalen

Si se desea que los resultados del anaacutelisis en vez de presentarse por pantalla queden grabados en un fi -chero de nombre Salidadat

java NombreDeLaEspecif lt Programalen gt Salidadat

3

loz Ejemplo de presentacioacuten

bull Descripcioacuten del lenguaje

El lenguaje L estaacute formado por las expresiones en las que pueden aparecer- variables- constantes- operadores + y

Las variables son nombres formados por una uacutenica letra (minuacutescula o mayuacutescula) las constantes son nuacute-meros enteros de una o maacutes cifras El espacio y el tabulador pueden estar presentes pero no tienen ninguacuten significado los finales de liacutenea tampoco son significativos (una expresioacuten puede codificarse ocupando una o maacutes liacuteneas)

La sintaxis de las expresiones se especifica mediante la siguiente gramaacutetica

ltExpresiongt = ltTerminogt + ltTerminogt ltTerminogt = ltFactorgt ltFactorgt ltFactorgt = variable | constante | ( ltExpresiongt )

bull Especificacioacuten leacutexico-sintaacutectica codificada con la notacioacuten JavaCCUna manera de escribir la especificacioacuten (para la que se ha elegido el nombre ExprMin) de forma que sea aceptada por el generador es

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Anaacutelisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

4

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP | t | n | r

bull Obtencioacuten del analizadorSi la especificacioacuten precedente se tiene grabada en un fichero de nombre Ejemplojj para obtener el analizador

- se ejecuta el generador javacc Ejemplojj

- se compila el analizador generado javac ExprMinjava

bull Ejecucioacuten del analizadorSi se quiere analizar una expresioacuten grabada en un fichero de nombre PruebaExptxt

- se ejecuta el analizador obtenido java ExprMin lt PruebaExptxt

loz Analizadores generadosEn su funcionamiento maacutes sencillo y habitual JavaCC genera un analizador sintaacutectico complementado con un analizador lexicograacutefico para que conjuntamente se pueda realizar un anaacutelisis leacutexico-sintaacutectico de un texto de entrada

El analizador sintaacutectico obtenido es en general LL(k) descendente y determinista con la consulta de k siacutembolos por adelantado si la gramaacutetica proporcionada cumple la condicioacuten LL(1) se genera un analiza -dor sintaacutectico descendente-predictivo-recursivo Maacutes adelante se hacen algunas precisiones sobre esta afir-macioacuten

Si la especificacioacuten leacutexico-sintaacutectica de un lenguaje codificada en JavaCC tiene dado (como indicativo que acompantildea a las palabras reservadas PARSER_BEGIN y PARSER_END) el nombre EspLexSin y se tiene grabada en un fichero de nombre Lenguajejj cuando se ejecuta el generador tomando como entrada ese fichero

javacc Lenguajejj

se obtienen los siguientes ficheros (clases) con coacutedigo Java

Tokenjavadescripciones para la comunicacioacuten entre los analizadores leacutexico y sintaacutectico

5

TokenMgrErrorjavatratamiento de errores para el anaacutelisis lexicograacutefico

ParseExceptionjavatratamiento de errores para el anaacutelisis sintaacutectico

SimpleCharStreamjavacomponentes para la realizacioacuten de las tareas de entradasalida del analizador

EspLexSinConstantsjavadefinicioacuten de la representacioacuten interna de las piezas sintaacutecticas

EspLexSinTokenManagerjavaanalizador lexicograacutefico

EspLexSinjavaanalizador sintaacutectico

Puede apreciarse que hay dos categoriacuteas de nombres de ficheros generados los cuatro primeros nombres citados no dependen del nombre de la especificacioacuten considerada los otros nombres de ficheros se forman a partir del nombre dado a la especificacioacuten

Forma de una especificacioacuten JavaCC (versioacuten simplificada)La forma que se describe en lo que sigue es una versioacuten simplificada el generador JavaCC admite especificaciones con otras muchas posi-bilidades no mencionadas en esta introduccioacuten

Una especificacioacuten para el generador JavaCC puede considerarse dividida en cuatro secciones

loz Seccioacuten de opcionesEn esta seccioacuten cuya presencia es optativa se pueden asignar valores a diversos paraacutemetros (llamados op-ciones) que sirven para configurar ciertas caracteriacutesticas del funcionamiento del generador o del analizador generado Cada paraacutemetro (opcioacuten) tiene un valor por defecto que es el que toma cuando no se le asigna expliacutecitamente un valor Los valores de las opciones tambieacuten se pueden fijar en la liacutenea de comandos

Seccioacuten de opciones

Seccioacuten de ejecucioacuten

Seccioacuten de sintaxis

Seccioacuten de lexicografiacutea

6

cuando se ejecuta el generador (lo indicado en la liacutenea de comandos tiene prioridad sobre lo especificado en esta seccioacuten de opciones)

Algunas de las opciones para las que se puede fijar un valor son

Ignore_Case (valor por defecto false)

indica si en el texto analizado ha de distinguirse o no entre letras minuacutesculas y mayuacutesculas

Build_Parser (valor por defecto true)

indica si se genera el analizador sintaacutectico o no

Build_Token_Manager (valor por defecto true)

indica si se genera el analizador lexicograacutefico o no

Sanity_Check (valor por defecto true)

indica si se realizan comprobaciones sobre la gramaacutetica sintaacutectica

Debug_Parser (valor por defecto false)

indica si se genera una traza para el anaacutelisis leacutexico-sintaacutectico

Error_Reporting (valor por defecto true)

indica si los mensajes de error emitidos son maacutes o menos explicativos

Static (valor por defecto true)

con el valor por defecto los meacutetodos de los analizadores leacutexico y sintaacutectico se generan con el des-criptor estaacutetico (static)

Los nombres de las opciones pueden escribirse con letras minuacutesculas o mayuacutesculas los valores de las op-ciones son en la mayoriacutea de los casos un valor entero o un valor loacutegico Si se incluye la seccioacuten se empie-za poniendo la palabra reservada options en este caso al menos ha de ponerse una opcioacuten

La forma de esta seccioacuten es

options nombreOpcion1 = valorOpcion1

nombreOpcion2 = valorOpcion2

∙ ∙ ∙ ∙

nombreOpcionk = valorOpcionk

loz Seccioacuten de ejecucioacutenEn esta seccioacuten se pone el coacutedigo Java que contiene la llamada al analizador generado para que se realice el anaacutelisis de un determinado texto de entrada Tambieacuten se establece aquiacute el nombre de la especificacioacuten que es el nombre que se toma para formar los nombres de parte de los ficheros (clases) generados

La seccioacuten estaacute delimitada por dos palabras reservadas ambas acompantildeadas por un mismo nombre (puesto entre pareacutentesis) ese nombre es el que se da a la especificacioacuten Entre esas dos palabras ha de ponerse una clase directora para el proceso de anaacutelisis el nombre de esa clase directora ha de coincidir con el nombre dado a la especificacioacuten

PARSER_BEGIN (NombreDeLaEspecif)

7

∙ ∙ ∙ ∙

public class NombreDeLaEspecif

∙ ∙ ∙ ∙

∙ ∙ ∙ ∙

PARSER_END (NombreDeLaEspecif)

Una posible versioacuten sencilla de la clase directora es

public class NombreDeLaEspecif public static void main (String[] argum) throws ParseException NombreDeLaEspecif anLeSi = new NombreDeLaEspecif (Systemin) anLeSisimboloPrograma()

se crea el objeto anLeSi para poder aplicar los meacutetodos de anaacutelisis se asocia la entrada standard ( Systemin ) a la entrada para el analizador generado se aplica al objeto creado el meacutetodo de anaacutelisis correspondiente al siacutembolo inicial de la gramaacutetica

loz Seccioacuten de sintaxisEn esta seccioacuten se describe la sintaxis del lenguaje para el que se desea generar el analizador usaacutendose pa-ra ello una notacioacuten parecida a la BNF En lo que sigue se expone la forma de las producciones tal y como se escriben en JavaCC ponieacutendolas en comparacioacuten con las producciones de la notacioacuten BNF-Ampliada

bull Reglas sintaacutecticasEl conjunto de reglas que definen un siacutembolo no terminal

ltNombreSimbgt = α1

| α2

∙ ∙ ∙

| αn

se codifica de la siguiente manera

void nombreSimb () α1-jcc

| α2-jcc

∙ ∙ ∙

| αn-jcc

8

- el nombre del siacutembolo no terminal (parte izquierda de las reglas) se escribe como el encabeza-miento de un meacutetodo en Java sin argumentos y sin valor de devolucioacuten (meacutetodo de tipo void) se sigue como en Java la costumbre de escribir los nombres de los meacutetodos empezando con letra mi-nuacutescula

- el siacutembolo dos puntos representa la separacioacuten entre las partes izquierda y derechas de las reglas (en vez del siacutembolo = de la notacioacuten BNF)

- el siacutembolo barra vertical representa la separacioacuten entre distintas partes derechas con la misma parte izquierda (igual que en la notacioacuten BNF)

- despueacutes del siacutembolo dos puntos se pone un bloque de coacutedigo Java este coacutedigo se suele dedicar a la realizacioacuten de tareas semaacutenticas si se pretende hacer soacutelo un anaacutelisis sintaacutectico el bloque que-daraacute vaciacuteo (aunque siempre es obligada su presencia)

- el conjunto de las alternativas (partes derechas de las reglas) estaacute delimitado mediante llaves como si constituyese un bloque de coacutedigo Java

- αi-jcc representa la codificacioacuten de una alternativa (parte derecha) empleaacutendose una nota-cioacuten que se describe a continuacioacuten

bull Siacutembolos no terminales en las partes derechasEn la notacioacuten BNF se pone ltNombreSimbgt en la notacioacuten JavaCC se escribe

nombreSimb()

esto es se pone como si fuese una llamada en Java a un meacutetodo sin argumentos

bull Siacutembolos terminales en las partes derechasSe considera una clasificacioacuten en las piezas sintaacutecticas (siacutembolos terminales de una gramaacutetica sintaacutectica) distinguieacutendose entre piezas sintaacutecticas nominales y anoacutenimas

son nominales las piezas sintaacutecticas a las que se asocia un nombre en otra parte de la especificacioacuten (maacutes adelante se describe la manera de indicar esta asociacioacuten) ese nombre asociado a la pieza es el que se usa al escribir las partes derechas de las producciones En la notacioacuten JavaCC un siacutembolo terminal no-minal se escribe poniendo el nombre delimitado por los caracteres lt y gt

ltnombreTerminalgt

son anoacutenimas las piezas sintaacutecticas que no tienen nombre asociado no se precisa porque la pieza se re -presenta por su propia secuencia de caracteres (lexema) En la notacioacuten JavaCC un siacutembolo terminal anoacutenimo se escribe poniendo su lexema delimitado por comillas por ejemplo

= lt lt= END

bull Metasiacutembolos de opcionalidad y de repeticioacutenEn la notacioacuten BNF se emplean los corchetes para indicar opcionalidad en la notacioacuten JavaCC se utilizan tambieacuten esos mismos siacutembolos

[ α ] en ambas notaciones

En la notacioacuten BNF se emplean las llaves para indicar una repeticioacuten de cero o maacutes veces en la notacioacuten JavaCC se utilizan los pareacutentesis y un asterisco detraacutes del pareacutentesis de cerrar

α en notacioacuten BNF-Ampliada

( α ) en notacioacuten JavaCC

bull Otros metasiacutembolos de la notacioacuten JavaCC

9

La opcionalidad tambieacuten se puede indicar empleando el metasiacutembolo de interrogacioacuten

( α )

Para indicar repeticioacuten de una o maacutes veces se utiliza el metasiacutembolo operador aditivo suma

( α )+

bull Control del final de fichero de entradaPara controlar el final del fichero en el texto analizado ha de antildeadirse una regla sintaacutectica especial que tenga (seguacuten la notacioacuten BNF) la forma

ltProgramaCompletogt = ltProgramagt FF

donde

ltProgramaCompletogt es un nuevo siacutembolo (no terminal) auxiliar antildeadido

ltProgramagt es el siacutembolo inicial de la gramaacutetica sintaacutectica

FF es una representacioacuten del final del fichero

En JavaCC el final de fichero se escribe con la notacioacuten ltEOFgt siendo EOF una palabra reservada la regla sintaacutectica especial antildeadida a la especificacioacuten quedariacutea codificada asiacute

void programaCompleto() programa() ltEOFgt

bull Representacioacuten de la produccioacuten vaciacuteaLas producciones vaciacuteas (reglas sintaacutecticas cuya parte derecha es la palabra vaciacutea ε ) no se pueden repre-sentar expliacutecitamente en una especificacioacuten leacutexico-sintaacutectica escrita en JavaCC Para indicar la presencia de una produccioacuten vaciacutea han de aplicarse las posibilidades de la notacioacuten BNF-Ampliada seguacuten esta nota-cioacuten cuando se indica la opcionalidad de un componente se estaacute especificando impliacutecitamente la palabra vaciacutea (en el caso de que el componente no esteacute presente)

Asiacute pues si se quisiera escribir en JavaCC la construccioacuten sintaacutectica expresada por las reglas

ltComponentegt = α

| ε

deberiacutean de considerarse con la notacioacuten equivalente

ltComponentegt = [ α ]

y si hubiera varias alternativas no vaciacuteas

ltComponentegt = α | β

| ε

habriacutea que considerarlas como

ltComponentegt = [ α | β ]

o tambieacuten podriacutea verse como

ltComponentegt = α

10

| [ β ]

Como ejemplo sea la especificacioacuten sintaacutectica de una llamada a una funcioacuten que puede tener cero o maacutes paraacutemetros separados por comas aunque no haya paraacutemetros los pareacutentesis estaacuten presentes Las siguien-tes reglas definen esta estructura

ltLlamadagt = id ( ltParametrosgt )

ltParametrosgt = ltExpresiongt ltExpresiongt | ε

En JavaCC la definicioacuten del siacutembolo ltParametrosgt se puede escribir como sigue (noacutetese que aquiacute los pareacutentesis empleados para aplicar el operador son metasiacutembolos)

void parametros() [ expresion() ( expresion() ) ]

loz Seccioacuten de lexicografiacuteaEn esta seccioacuten se indica la lexicografiacutea del lenguaje para el que se va a generar el analizador la notacioacuten de JavaCC que representa la forma de cada una de las piezas sintaacutecticas es una variante de las bien conoci-das expresiones regulares

bull El nombre de las piezas sintaacutecticas nominalesPara asociar un nombre a una pieza sintaacutectica y para al mismo tiempo definir la forma de la pieza se em-plea la notacioacuten

TOKEN lt nombreTerminal Expresioacuten-Regular gt

- TOKEN es una palabra reservada detraacutes de ella se pone el separador dos puntos

- nombreTerminal representa el nombre que se quiere asociar a la pieza sintaacutectica

- Expresioacuten-Regular indica una expresioacuten regular que especifica la forma de la pieza

- el nombre y la expresioacuten se separan mediante el caraacutecter dos puntos y conjuntamente se deli -mitan mediante los caracteres lt y gt a su vez toda la asociacioacuten se delimita con llaves

bull Expresiones regulares en JavaCCLas expresiones regulares en una especificacioacuten JavaCC se escriben de manera parecida a la forma utiliza-da en otras conocidas notaciones aunque con ciertas peculiaridades A continuacioacuten se mencionan los as-pectos maacutes significativos de las expresiones regulares JavaCC y algunos ejemplos

Los operadores de las expresiones son

| unioacuten

repeticioacuten cero o maacutes veces

+ repeticioacuten una o maacutes veces

opcionalidad

11

Los pareacutentesis permiten fijar el orden de aplicacioacuten de los operadores Una peculiaridad de las expresiones regulares JavaCC es que los operadores de repeticioacuten y de opcionalidad requieren que el componente al que se aplican esteacute en todo caso delimitado por pareacutentesis

Se pueden citar las siguientes caracteriacutesticas baacutesicas de las expresiones regulares en JavaCC

Se tiene la posibilidad de definir conjuntos de caracteres poniendo entre corchetes la relacioacuten de carac-teres separados por comas y cada caraacutecter delimitado por comillas En un conjunto se puede fijar un ran-go de caracteres poniendo el primero y el uacuteltimo separados por el siacutembolo guioacuten (-) A veces resulta maacutes coacutemodo indicar los caracteres que no pertenecen al conjunto para ello se puede definir el comple -mentario de un conjunto poniendo el siacutembolo ~ delante del corchete inicial

Para indicar que una secuencia de caracteres colocados consecutivamente constituye por siacute misma una expresioacuten regular (o un componente de una expresioacuten) se escribe la secuencia delimitada por comillas El siacutembolo se emplea como metacaraacutecter para indicar que el siacutembolo que estaacute inmediatamente a su derecha se considere como caraacutecter del alfabeto no como posible metacaraacutecter (operador delimitador)

En una expresioacuten regular se pueden incluir como si fueran caracteres las secuencias de escape con la misma notacioacuten y el mismo significado que en Java o en C

Noacutetese que en una produccioacuten sintaacutectica la opcionalidad se puede especificar de dos formas con corchetes o con el siacutembolo de interrogacioacuten sin embargo en una expresioacuten regular la opcionalidad soacutelo se puede es-pecificar mediante el siacutembolo de interrogacioacuten (los corchetes en las expresiones regulares son para definir conjuntos de caracteres)

A continuacioacuten se muestran ejemplos de expresiones regulares escritas seguacuten la notacioacuten JavaCC

ENDsecuencia de las tres letras mayuacutesculas que conforman la palabra END

whilesecuencia de las cinco letras minuacutesculas que conforman la palabra while

ncaraacutecter representativo del fin de liacutenea (secuencia de escape)

rcaraacutecter representativo del retorno de carro (secuencia de escape)

caraacutecter dos puntos

=secuencia de dos caracteres (siacutembolo de asignacioacuten en Pascal)

gtcaraacutecter representativo del operador de relacioacuten mayor

(i | I)(f | F)palabra ldquoifrdquo escrita con letras minuacutesculas o mayuacutesculas (cuatro combinaciones)

if | IFpalabra ldquoifrdquo escrita soacutelo con minuacutesculas o soacutelo con mayuacutesculas (dos combinaciones)

12

u | o | i | e | auna cualquiera de las vocales minuacutesculas (pero soacutelo una de ellas)

[ A E I O U ]una cualquiera de las vocales mayuacutesculas (pero soacutelo una de ellas)

[ a - z ntilde ]una cualquiera de las letras minuacutesculas incluyendo la letra ntilde

[ a - z A - Z 0 - 9 ]una cualquiera de las letras o de las cifras decimales

([ 0 - 9 ])+constante entera (nuacutemero de una o maacutes cifras)

noacutetese que es incorrecto poner [ 0 - 9 ]+

( + | - )([ 0 - 9 ])+constante entera precedida opcionalmente por un signo

noacutetese que la primera cifra ha de seguir inmediatamente al signo (si existe)

([+ -])([ 0 - 9 ])+representa lo mismo que la expresioacuten anterior

[ a - z ] ([ a - z 0 - 9 ])una letra minuacutescula seguida de cero o maacutes letras minuacutesculas o cifras decimales

~[a - z ntilde A - Z Ntilde]cualquier caraacutecter que no sea una letra

noacutetese que la expresioacuten representa un uacutenico caraacutecter

~[n r]cualquier caraacutecter (un uacutenico caraacutecter) excepto el fin de liacutenea y el retorno de carro

~[ ]cualquier caraacutecter del alfabeto (un uacutenico caraacutecter)

([ 0 - 9 ])+ ( ([ 0 - 9 ])+)constante aritmeacutetica con parte decimal opcional

Los modernossecuencia de catorce caracteres Los modernos

noacutetese el uso de para imponer la condicioacuten de caraacutecter del siacutembolo comillas

nsecuencia de cuatro caracteres n

noacutetese que aquiacute no se representa la secuencia de escape

bull Piezas sintaacutecticas internas (ldquoprivaterdquo)

13

Es posible definir piezas sintaacutecticas auxiliares llamadas ldquointernasrdquo porque no se comunican al analizador sintaacutectico sino que soacutelo sirven para facilitar la escritura de la forma de las piezas sintaacutecticas que siacute se co-munican con el uso de las piezas internas es posible evitar repeticiones de escritura y hacer maacutes legible la especificacioacuten de las caracteriacutesticas lexicograacuteficas Las piezas internas permiten dar nombre a subexpre-siones regulares que a su vez pueden emplearse como componentes para escribir expresiones maacutes com-plejas que incorporan esos nombres Para indicar que una pieza sintaacutectica se considere como interna se pone el siacutembolo delante del nombre asociado a la pieza

A continuacioacuten se muestra un ejemplo tiacutepico de utilizacioacuten de las piezas internas en una especificacioacuten le-xicograacutefica en JavaCC Se quieren definir tres piezas sintaacutecticas que representan un nombre una constante entera y una constante decimal una posible especificacioacuten es

TOKEN ltnombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN ltcteEntera ( [ 0 - 9 ] )+ gt

TOKEN ltcteDecimal ( [ 0 - 9 ] )+ ( [ 0 - 9 ] )+ gt

Con la inclusioacuten de tres piezas sintaacutecticas internas auxiliares esta misma especificacioacuten podriacutea escribirse de la siguiente manera

TOKEN lt letra [ a - z A - Z ntilde Ntilde ] gt

TOKEN lt cifra [ 0 - 9 ] gt

TOKEN lt numero ( ltcifragt )+ gt

TOKEN lt nombre ( ltletragt )+ gt

TOKEN lt cteEntera ltnumerogt gt

TOKEN lt cteDecimal ltnumerogt ltnumerogt gt

Los nombres letra cifra y numero son exclusivos (internos) del analizador lexicograacutefico y no se pueden emplear como nombres de piezas en la especificacioacuten de las reglas sintaacutecticas cuando esos nom-bres se usan en la definicioacuten de otras piezas han de escribirse delimitados por los siacutembolos lt y gt

bull Distincioacuten restringida entre minuacutesculas y mayuacutesculasSe tiene la opcioacuten Ignore_Case para determinar si en el anaacutelisis del texto de entrada se distingue entre las letras minuacutesculas y las mayuacutesculas el valor por defecto false indica que siacute se diferencian si se asig-na el valor true a esta opcioacuten entonces no se diferencian en ninguacuten caso con independencia de que en los patrones que se definan para las piezas sintaacutecticas se pongan minuacutesculas o mayuacutesculas

Pero a veces conviene que la diferenciacioacuten entre minuacutesculas y mayuacutesculas no se aplique de manera ge-neral a toda la entrada sino en particular a las secuencias de caracteres que se acoplen a ciertos patrones Para este fin existe la posibilidad de establecer que en la expresioacuten regular que define el patroacuten de una determinada pieza sintaacutectica no se diferencie entre minuacutesculas y mayuacutesculas ello se consigue poniendo

14

[ IGNORE_CASE ]

inmediatamente detraacutes de la palabra TOKEN con la que empieza la especificacioacuten de la pieza sintaacutectica en la que se pretende la no distincioacuten entre minuacutesculas y mayuacutesculas

Noacutetese que ignore_case tiene dos significados

- es el nombre de un paraacutemetro de la seccioacuten de opciones (puede ponerse en minuacutesculas o mayuacutesculas)- es una palabra reservada que puede aplicarse (escribieacutendola entre corchetes y en mayuacutesculas) a la de -finicioacuten de una pieza sintaacutectica

Por ejemplo sea un lenguaje que en general distingue entre minuacutesculas y mayuacutesculas pero en la codifi -cacioacuten del exponente de una constante real exponencial puede ponerse la letra e en minuacutescula o en ma-yuacutescula En la especificacioacuten JavaCC para ese lenguaje la opcioacuten Ignore_Case ha de tomar el valor false en la definicioacuten de una constante exponencial son posibles las siguientes formas equivalentes

1) sin hacer uso de IGNORE_CASE

TOKEN lt cifras ( [ 0 - 9 ] )+ gt

TOKEN lt signo [+ -] gt

TOKEN lt constante

ltcifrasgt ( ltcifrasgt) [e E] (ltsignogt) ltcifrasgt gt

2) aplicando IGNORE_CASE

TOKEN lt cifras ( [ 0 - 9 ] )+ gt

TOKEN lt signo [+ -] gt

TOKEN [IGNORE_CASE] lt constante

ltcifrasgt ( ltcifrasgt) e (ltsignogt) ltcifrasgt gt

bull Secuencias de caracteres sin efecto en la estructura sintaacutecticaEn los lenguajes de programacioacuten suele haber secuencias de caracteres que no forman parte de la estructu-ra sintaacutectica de un programa esas secuencias son detectadas por el analizador lexicograacutefico pero no son comunicadas al sintaacutectico no han de considerarse como piezas sino que han de saltarse En JavaCC se pueden definir estas secuencias mediante la palabra reservada SKIP la notacioacuten de una especificacioacuten con la palabra SKIP de una secuencia que ha de saltarse es la misma que la notacioacuten usada para las espe-cificaciones con la palabra TOKEN de las piezas que han de comunicarse

Los dos casos maacutes habituales de secuencias que no se comunican al analizador sintaacutectico son

a) los caracteres separadores que no tienen significado en el texto analizadoen los lenguajes de codificacioacuten en formato libre hay caracteres del texto fuente que han de ignorarse en la mayoriacutea de los casos estos caracteres son el espacio en blanco el tabulador el retorno de carro y el fin de liacutenea esta caracteriacutestica se especifica

SKIP |t | r |n

b) los comentarios

15

en el anaacutelisis sintaacutectico de un programa los comentarios han de ignorarse una manera de especificar esta circunstancia es poniendo en una definicioacuten SKIP la expresioacuten regular que denota la forma de los comentarios del lenguaje por ejemplo para los comentarios de una liacutenea de Java se pondriacutea

SKIP

lt (~ [n r]) gt

Tareas asociadas a la estructura sintaacutecticaJavaCC genera un analizador sintaacutectico descendente recursivo constituido por un conjunto de meacutetodos ca-da siacutembolo no terminal de la gramaacutetica sintaacutectica tiene asociado un meacutetodo de anaacutelisis (el meacutetodo tiene el mismo nombre que el siacutembolo correspondiente) Un meacutetodo de anaacutelisis ademaacutes de la realizacioacuten de la parte del anaacutelisis sintaacutectico que le corresponde puede llevar a cabo otras tareas complementarias que sirvan para efectuar un anaacutelisis maacutes complejo del texto de entrada

loz Bloque para un siacutemboloAl describir la forma de las reglas sintaacutecticas de un siacutembolo no terminal en una especificacioacuten JavaCC ya se ha visto que detraacutes de la definicioacuten del nombre del siacutembolo y antes de empezar con las diferentes alter-nativas que tiene asociadas se incluye un bloque de coacutedigo Java (en el ejemplo de presentacioacuten este bloque estaacute vaciacuteo en todos los siacutembolos no terminales de la gramaacutetica) Cada bloque situado en esa posicioacuten puede considerarse ligado al siacutembolo no terminal en cuya descripcioacuten estaacute incluido el coacutedigo Java del bloque se traslada literalmente al principio del meacutetodo generado correspondiente al siacutembolo no terminal y por lo tan-to es lo primero que se considera (se ejecuta) cuando en el transcurso del proceso de anaacutelisis se realiza una llamada al meacutetodo Asiacute pues cada siacutembolo no terminal siempre tiene asociado un uacutenico bloque de coacute-digo Java ndashbloque de presencia obligada aunque pueda estar vaciacuteo- en este bloque se pueden poner de-claraciones y sentencias

loz Acciones sintaacutecticasAdemaacutes del bloque asociado a cada uno de los siacutembolos no terminales de la gramaacutetica se pueden incluir otros bloques de coacutedigo Java intercalados en cualquiera de las partes derechas de las reglas sintaacutecticas Los bloques incluidos en las partes derechas se denominan ldquoacciones sintaacutecticasrdquo (parser actions) ya que son trozos de coacutedigo que pueden considerarse asociados a los puntos de la estructura sintaacutectica donde estaacuten co-locados Una accioacuten sintaacutectica se puede poner en cualquier posicioacuten al principio al final o en el medio de la parte derecha de una regla precedida o seguida de siacutembolos terminales o no terminales el coacutedigo Java de una accioacuten sintaacutectica se traslada literalmente al meacutetodo asociado al siacutembolo no terminal de la parte izquierda de la regla de manera que se ejecuta cada vez que el analizador transcurre por el punto de la estructura sintaacutectica donde se ha incorporado la accioacuten (esto es las acciones sintaacutecticas se incorporan al coacutedigo del analizador sintaacutectico) No obstante su nombre en los bloques de las acciones sintaacutecticas suele ponerse coacutedigo dedicado a la realizacioacuten de tareas semaacutenticas

Si en el bloque asociado a un siacutembolo no terminal se ponen declaraciones los nombres ahiacute definidos son accesibles en todas las acciones sintaacutecticas intercaladas en las partes derechas de las reglas de ese siacutembolo la justificacioacuten es sencilla el coacutedigo del bloque asociado al siacutembolo se incluye al principio de un meacutetodo y el coacutedigo de las acciones pertenece a ese mismo meacutetodo

∆ Ejemplo 1

Se considera de nuevo el ejemplo de presentacioacuten ahora se pretende completar el anaacutelisis leacutexico-sintaacutec-tico con la realizacioacuten de las siguientes tareas

- cada vez que se analiza una expresioacuten un teacutermino o un factor se graba el nombre del siacutembolo no terminal correspondiente y

16

- cuando se analiza un teacutermino en el que hay operadores multiplicativos se cuenta su nuacutemero y se graba el resultado (noacutetese que se cuentan los operadores de cada teacutermino pero no se acumula la cuenta de todos los operadores de la expresioacuten)

Para la realizacioacuten de estas tareas se antildeaden bloques de coacutedigo Java En la especificacioacuten que se describe a continuacioacuten puede apreciarse- para los siacutembolos no terminales expresion termino y factor se ha incluido en los blo-ques correspondientes la operacioacuten de grabacioacuten de su nombre que se ejecutaraacute cada vez que se efec-tuacutee una llamada al meacutetodo- en el bloque asociado a termino se tiene la declaracioacuten del contador de operadores (que seraacute una variable local del meacutetodo)- en la parte derecha de la regla que define la estructura de un teacutermino se han incluido dos acciones sintaacutecticas una para contar la cantidad de operadores (situada detraacutes de la pieza sintaacutectica del opera-dor) y otra para grabar la cantidad de operadores del teacutermino (situada al final de la parte derecha)- se ha definido el meacutetodo grabar para facilitar la escritura del coacutedigo Java

options Ignore_Case = true

PARSER_BEGIN (ExprMin) public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

private static void grabar(String nombre) Systemoutprintln ( -gt + nombre + n)

PARSER_END (ExprMin)

void unaExpresion()

expresion() ltEOFgt

void expresion() grabar(Expresion)

termino() ( + termino() )

void termino() int nAst = 0 grabar(Termino)

17

factor() ( nAst++ factor() ) if (nAst gt 0) Systemoutprintln (n Asteriscos + nAst + n)

void factor() grabar(Factor)

ltconstantegt | ltvariablegt | ( expresion() )

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP | t | n | r

Cuando se incluyen acciones sintaacutecticas la legibilidad del aspecto sintaacutectico de la especificacioacuten queda oscurecida para paliar ese inconveniente puede antildeadirse un comentario que muestre la regla sintaacutectica escueta sin las acciones por ejemplo podriacutea escribirse

void termino() int nAst = 0 grabar(Termino)

factor() ( factor() )

factor()

( nAst++ factor() )

if (nAst gt 0) Systemoutprintln (n Asteriscos + nAst + n)

loz Valor comunicado por un meacutetodoEn todo lo visto hasta ahora los meacutetodos de anaacutelisis sintaacutectico generados por JavaCC no devuelven valor alguno al terminar su tarea de anaacutelisis ya que en la especificacioacuten siempre se ha declarado el nombre de un meacutetodo precedido de void (son meacutetodos de tipo void) Pero en general un meacutetodo de anaacutelisis sintaacutec-tico puede ser de cualquier tipo (primitivo o no) por ello un meacutetodo puede devolver un valor de cualquier tipo (de cualquier clase) al meacutetodo desde el que se efectuoacute la llamada

Esta posibilidad de devolucioacuten de valores tiene una utilidad fundamental en la implementacioacuten de analiza-dores de lenguajes permite la comunicacioacuten de datos (atributos de las entidades analizadas) entre los dife -rentes meacutetodos que colaboran en el anaacutelisis del programa completo

En una especificacioacuten JavaCC la llamada a un meacutetodo ocurre cuando en la parte derecha de una regla estaacute el nombre del siacutembolo no terminal asociado al meacutetodo Cuando un meacutetodo devuelve un valor (tiene un tipo distinto de void) es habitual la recepcioacuten del valor devuelto en una variable en la propia especificacioacuten

18

se puede indicar el nombre de la variable a la que se asignaraacute el valor devuelto tras la llamada para ello se emplea una notacioacuten ideacutentica a la forma que tienen las sentencias de asignacioacuten en el lenguaje Java

dato = nombreSimb()

donde dato es el nombre de la variable a la que se asigna el valor devuelto por el meacutetodo llamado esa va -riable ha de estar oportunamente declarada del mismo tipo que dicho meacutetodo

∆ Ejemplo 2

En el mismo ejemplo de presentacioacuten se desea ademaacutes del anaacutelisis leacutexico-sintaacutectico de la entrada contar la cantidad de operadores que hay en la expresioacuten A continuacioacuten se expone una especificacioacuten JavaCC que es una posible solucioacuten soacutelo se muestran las reglas sintaacutecticas (el resto de la especificacioacuten es igual que en el ejemplo de presentacioacuten)

En la especificacioacuten que se propone puede apreciarse- los siacutembolos no terminales expresion termino y factor se han declarado de tipo int los meacutetodos correspondientes devuelven un valor de tipo entero- el valor devuelto por los meacutetodos sirve para propagar a traveacutes de la estructura sintaacutectica la cuenta de los operadores que hay en el texto analizado- por tratarse de meacutetodos que devuelven un valor es preciso incluir las correspondientes sentencias de devolucioacuten return

void unaExpresion() int nOper nOper = expresion() ltEOFgt Systemoutprintln (Cantidad de operadores = + nOper)

int expresion() int n i n = termino() ( + n++ i = termino() n = n + i )

return n

int termino() int n i n = factor() ( n++ i = factor() n = n + i )

return n

int factor() int n ltconstantegt return 0 | ltvariablegt return 0 | ( n = expresion() ) return n

La especificacioacuten de un factor tambieacuten podriacutea escribirse de esta otra manera

19

int factor() int n = 0

( ltconstantegt | ltvariablegt | ( n = expresion() ) )

return n

A continuacioacuten se expone un nuevo ejemplo para ilustrar la utilidad que proporciona el valor devuelto por un meacutetodo de anaacutelisis sintaacutectico para la propagacioacuten (ascendente) de atributos (propiedades de las entida-des analizadas)

∆ Ejemplo 3

Se pretende ampliar el analizador del ejemplo de presentacioacuten para que se emita un mensaje indicativo cuando la expresioacuten analizada sea una expresioacuten en la que soacutelo aparecen constantes en la solucioacuten que se muestra soacutelo se incluyen las reglas sintaacutecticas (en el resto de la especificacioacuten no hay modificacio-nes)

void unaExpresion() boolean esCte

expresion() ltEOFgt

esCte = expresion() ltEOFgt

if (esCte) Systemoutprintln(nExpresion constanten)

boolean expresion() boolean esCte esTambienCte

termino() ( + termino() )

esCte = termino() ( + esTambienCte = termino() esCte = esCte ampamp esTambienCte )

return esCte

boolean termino() boolean esCte esTambienCte

factor() ( factor() )

esCte = factor() ( esTambienCte = factor() esCte = esCte ampamp esTambienCte )

return esCte

20

boolean factor() boolean esCte

ltconstantegt return true | ltvariablegt return false | ( esCte = expresion() ) return esCte

bull Reconsideracioacuten sobre las producciones vaciacuteasAnteriormente se ha comentado que para representar una produccioacuten vaciacutea en una especificacioacuten JavaCC hay que hacer uso de los metasiacutembolos de opcionalidad para ilustrar esta idea se ha incluido como ejem-plo la especificacioacuten de una lista de paraacutemetros compuesta por cero o maacutes expresiones separadas entre siacute por el caraacutecter coma

void parametros() [ expresion() ( expresion() ) ]

Pero si se aprovecha la posibilidad de incorporar acciones sintaacutecticas se tiene una manera de especificar directamente producciones vaciacuteas para conseguirlo basta considerar una produccioacuten vaciacutea (no tiene nada en su parte derecha) acompantildeada de una accioacuten sintaacutectica cuyo bloque de coacutedigo esteacute vaciacuteo Seguacuten esta posibilidad la especificacioacuten anterior podriacutea tambieacuten escribirse asiacute

void parametros() expresion() ( expresion() ) |

En general la accioacuten sintaacutectica asociada a una produccioacuten vaciacutea no tiene por queacute ser un bloque vaciacuteo de coacutedigo por ejemplo si se quiere que el meacutetodo que analiza los paraacutemetros de una llamada devuelva la cantidad de paraacutemetros encontrados se podriacutea escribir la siguiente especificacioacuten

int parametros()

int cantidad

( expresion() cantidad = 1

( expresion() cantidad++ )

| cantidad = 0 )

return cantidad

y si no quisiera ponerse expliacutecitamente la alternativa de la produccioacuten vaciacutea podriacutea escribirse

int parametros() int cantidad = 0

21

[ expresion() cantidad++ ( expresion() cantidad++ ) ]

return cantidad

Comunicacioacuten entre los analizadores leacutexico y sintaacutecticoCada vez que el analizador sintaacutectico realiza una llamada al analizador lexicograacutefico recibe como resultado de la llamada una representacioacuten de la pieza sintaacutectica encontrada en el texto analizado En los analizadores generados por JavaCC la comunicacioacuten de la pieza se efectuacutea mediante un valor de clase Token Esta cla-se estaacute definida en el fichero Tokenjava que se obtiene en todo caso siempre con el mismo contenido cualquiera que sea la especificacioacuten proporcionada como entrada al generador

Entre los campos de la clase Token se encuentran los siguientes

public int kind

nuacutemero entero que sirve de representacioacuten interna de la pieza sintaacutectica estos nuacutemeros asociados a las piezas son asignados automaacuteticamente por JavaCC los valores considerados pueden consultar-se en el fichero generado de nombre Constantsjava

public String image

cadena que contiene de la secuencia de caracteres (lexema) que constituyen la pieza sintaacutectica co-municada

public int beginLine beginColumn endLine endColumn

posiciones ocupadas (nuacutemeros de liacutenea y columna en el fichero de entrada) por el comienzo y el fi-nal del lexema de la pieza sintaacutectica comunicada

La clase Token tiene otros campos y meacutetodos en el fichero Tokenjava se tienen comentarios des-criptivos sobre todos los campos y meacutetodos que forman parte de la clase todos los campos de la clase se declaran como public

Ya se ha comentado anteriormente que cuando un meacutetodo de anaacutelisis sintaacutectico (asociado a un siacutembolo no terminal) tiene tipo el valor devuelto tras una llamada se puede almacenar en una variable por si se desea utilizar en alguna accioacuten sintaacutectica para ello se escribe

valor = nombreSimbolo()

(siendo valor el nombre de la variable y nombreSimbolo el nombre del siacutembolo no terminal) De manera anaacuteloga el objeto de la clase Token representativo de la pieza sintaacutectica encontrada en la entrada por el analizador lexicograacutefico puede asignarse a una variable del mismo tipo para tener accesibles las ca -racteriacutesticas de la pieza por si se precisaran en alguna accioacuten sintaacutectica para ello en la parte de la especifi -cacioacuten sintaacutectica donde aparezca la pieza sintaacutectica se escribe

dato = lt nombrePieza gt

donde nombrePieza es el nombre de la pieza sintaacutectica y dato es el nombre de una variable de tipo Token en la que se anota el valor indicativo de las propiedades de la pieza sintaacutectica comunicada

Tambieacuten existe otra manera de acceder a las caracteriacutesticas de la pieza sintaacutectica que el analizador lexico -graacutefico comunica al analizador sintaacutectico en la clase del analizador sintaacutectico se tiene declarado el campo

[ static ] public Token token

22

cada vez que el analizador sintaacutectico recibe una pieza sintaacutectica comunicada por el analizador lexicograacutefico se deja anotada en el campo token y ese contenido no se altera hasta que sea proporcionada la siguiente pieza de la entrada

∆ Ejemplo 4

Se considera una ampliacioacuten del ejemplo de presentacioacuten ademaacutes del anaacutelisis de la entrada se pretende obtener una relacioacuten numerada de las variables y las constantes indicando el nuacutemero de liacutenea y el nuacuteme-ro de columna donde estaacuten situadas por ejemplo si en la entrada se tiene la expresioacuten (grabada en dos liacuteneas)

x (y + 11) + 22

en la salida se tendraacute

1- x linea 1 columna 1

2- y linea 1 columna 6

3- 11 linea 1 columna 10

4- 22 linea 2 columna 5

En la especificacioacuten que se propone como solucioacuten puede apreciarse- en la clase del analizador sintaacutectico se han incluido meacutetodos auxiliares en los que se usan los nom-bres image beginLine beginColumn que son campos de un objeto de clase Token

- en la especificacioacuten sintaacutectica de factor se emplea la variable pieza de tipo Token para reco-ger las caracteriacutesticas de las piezas sintaacutecticas detectadas- la variable numero hace de contador para numerar las constantes y variables

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin

static int numero = 0

private static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

private static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

private static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

23

PARSER_END (ExprMin)

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() Token pieza pieza = ltconstantegt numero++ grabarDatosPieza(numero pieza) | pieza = ltvariablegt numero++ grabarDatosPieza(numero pieza) | ( expresion() )

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP | n | t | r

Si se agrupan las dos acciones sintaacutecticas comunes la especificacioacuten de factor tambieacuten puede escri-birse de esta forma

void factor() Token pieza ( pieza = ltconstantegt | pieza = ltvariablegt

) numero++ grabarDatosPieza(numero pieza)

| ( expresion() )

24

Si se accede al campo token de la clase del analizador sintaacutectico se puede resolver este ejemplo pres-cindiendo de la variable pieza

void factor() ( ltconstantegt | ltvariablegt

) numero++ grabarDatosPieza(numero token)

| ( expresion() )

Tareas asociadas a las piezas sintaacutecticasYa se ha visto que es posible asociar bloques de coacutedigo Java a puntos de la estructura sintaacutectica para que se realicen ciertas operaciones cuando el analizador sintaacutectico pase por esa zona de la estructura Tambieacuten es posible asociar coacutedigo Java a las piezas sintaacutecticas para que el analizador lexicograacutefico efectuacutee determina-das tareas cuando detecte en la entrada analizada piezas que tengan asociado coacutedigo

loz Accioacuten ligada a una pieza sintaacutecticaSi se asocia un bloque de coacutedigo a una pieza sintaacutectica se consigue que cada vez que se detecta en la en-trada analizada la presencia de esa pieza se ejecute en ese preciso momento el coacutedigo del bloque asociado a la pieza Estas acciones se denominan ldquoacciones lexicograacuteficasrdquo (lexical actions) el coacutedigo del bloque se traslada literalmente al coacutedigo del analizador lexicograacutefico generado para que se ejecute oportunamente cuando se detecte la pieza ligada al bloque de coacutedigo La accioacuten lexicograacutefica se ejecuta inmediatamente antes que la pieza sea comunicada al analizador sintaacutectico

Se puede asociar una accioacuten lexicograacutefica a una pieza sintaacutectica declarada con TOKEN y tambieacuten a una se-cuencia de caracteres declarada con SKIP ademaacutes de eacutestas hay otras dos formas de declaraciones lexico-graacuteficas (no mencionadas en esta introduccioacuten) que tambieacuten pueden llevar una accioacuten asociada

En una especificacioacuten JavaCC la forma de una pieza sintaacutectica (o bien de una secuencia de caracteres) se define poniendo su descripcioacuten delimitada entre llaves detraacutes de la indicacioacuten TOKEN (o bien SKIP) si se quiere asociar una accioacuten lexicograacutefica a la pieza (o a la secuencia) hay que colocar el bloque de coacutedigo de la accioacuten a continuacioacuten de la descripcioacuten de la forma de la pieza (o de la secuencia) el bloque (delimi-tado por llaves) queda situado entre la descripcioacuten y la llave que cierra la especificacioacuten

La pieza sintaacutectica especial que representa el final del fichero (EOF) tambieacuten puede llevar asociada una ac-cioacuten lexicograacutefica para ello hay que poner

ltgt TOKEN lt EOF gt coacutedigo de la accioacuten asociada

para justificar la presencia de los caracteres ltgt delante de la palabra TOKEN hay que basarse en el con-cepto de ldquoestado lexicograacuteficordquo (cuya explicacioacuten queda fuera del alcance de esta introduccioacuten)

∆ Ejemplo 5

Se considera nuevamente el ejemplo de presentacioacuten como resultado del anaacutelisis de una expresioacuten se pretende obtener una relacioacuten de los pareacutentesis contenidos en la expresioacuten en el mismo orden en que se encuentran ademaacutes por cada salto de liacutenea del texto de entrada se grabaraacute en la salida el caraacutecter y

25

se pasaraacute a una nueva liacutenea tambieacuten se anotaraacute la frase Final del trabajo cuando se alcance el final del fichero de entrada Asiacute por ejemplo si la entrada analizada es

(a + b) (( x )) (y + 7)

los resultados obtenidos seraacuten

() (())()

Final del trabajo

En la especificacioacuten JavaCC que se propone como solucioacuten soacutelo se muestran las reglas sintaacutecticas y le -xicograacuteficas (en el resto no hay cambios) en esta solucioacuten puede apreciarse- las piezas sintaacutecticas correspondientes a los pareacutentesis se han definido de manera nominal para po-der asociarles una accioacuten lexicograacutefica- los caracteres incluidos en la pieza de tipo SKIP se han descompuesto en dos grupos ya que la ac-cioacuten lexicograacutefica incorporada soacutelo afecta a uno de ellos

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ltpAbrirgt expresion() ltpCerrargt

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

TOKEN

26

lt pAbrir ( gt Systemoutprint(()

TOKEN lt pCerrar ) gt Systemoutprint())

ltgtTOKEN lt EOF gt Systemoutprintln ( Final del trabajo )

SKIP n Systemoutprint( n)

SKIP | t | r

loz Bloque de declaraciones lexicograacuteficasCada definicioacuten de una pieza sintaacutectica puede llevar asociada una accioacuten lexicograacutefica que se ejecuta in-mediatamente despueacutes de detectar en la entrada un lexema que se ajusta a la definicioacuten de la pieza cada accioacuten lexicograacutefica es un bloque de coacutedigo Java que puede contener sus propias declaraciones Pero a ve-ces interesa tener declaraciones que sean compartidas por todas las acciones lexicograacuteficas con indepen-dencia de la pieza a la que esteacute asociada cada una de ellas

Para incluir declaraciones que puedan usarse desde el coacutedigo asociado a cualquiera de las piezas sintaacutecti -cas se dispone de una construccioacuten con el siguiente formato

TOKEN_MGR_DECLS

que consta de una palabra reservada seguida del siacutembolo dos puntos seguido de un bloque de coacutedigo Java (delimitado por llaves) esta declaracioacuten se puede colocar en cualquier parte de una especificacioacuten JavaCC si se incluye soacutelo puede ponerse una vez

Todas las declaraciones de variables o de meacutetodos puestas en el bloque de declaraciones lexicograacuteficas compartidas son accesibles desde el coacutedigo de cualquier accioacuten lexicograacutefica la justificacioacuten es sencilla el bloque de declaraciones se traslada al principio del coacutedigo del analizador lexicograacutefico generado y las ac-ciones lexicograacuteficas tambieacuten se trasladan a ese analizador

loz Declaraciones lexicograacuteficas predefinidasDe lo expuesto hasta ahora se aprecia que en una accioacuten lexicograacutefica asociada a una pieza sintaacutectica re-sultan accesibles

- las declaraciones de su propio bloque (si tiene declaraciones)- los componentes del bloque de declaraciones compartidas (si estaacute incluido en la especificacioacuten)

Ademaacutes de esto se tienen otras declaraciones pertenecientes a la clase TokenManager que constituye el analizador lexicograacutefico que tambieacuten son utilizables desde cualquiera de las acciones lexico-graacuteficas estas declaraciones pueden considerarse como ldquopredefinidasrdquo ya que son incorporadas automaacuteti-camente por el generador Entre estas declaraciones accesibles se encuentran las siguientes

[ static ] StringBuffer imagecontiene la cadena de caracteres representativa de la pieza sintaacutectica actual que se acaba de recono-cer en le entrada se trata de un campo de la clase y se declara como static cuando el analizador

27

se ha generado con la opcioacuten (por defecto) Static = true si se generase con la opcioacuten Sta-tic = false no quedariacutea declarada como static

Token matchedToken

variable de clase Token que representa la pieza sintaacutectica actual la que se acaba de encontrar en la entrada se trata de una variable local del meacutetodo al que se ha trasladado el coacutedigo de la accioacuten (y por ello accesible desde ese coacutedigo)

El campo image puede usarse tanto si la accioacuten lexicograacutefica estaacute asociada a una pieza declarada con TOKEN como si lo estaacute a una secuencia declarada con SKIP Sin embargo la variable matchedToken no puede usarse en una accioacuten asociada a un patroacuten especificado como SKIP el motivo es que lo declara-do como SKIP no constituye propiamente una pieza sintaacutectica ya que no se va a comunicar al analizador sintaacutectico

∆ Ejemplo 6

Se propone aquiacute una nueva solucioacuten para el ejemplo nordm 4 expuesto anteriormente

Esta nueva solucioacuten tal y como se expone aquiacute admite algunas mejoras ya que su pretensioacuten soacutelo es ser-vir de ilustracioacuten para algunos aspectos que se acaban de citar (maacutes adelante se propone otra solucioacuten al-ternativa)

En la especificacioacuten de la solucioacuten es posible apreciar esto- las acciones lexicograacuteficas pueden llevar sus propias declaraciones las variables nLineaVar nColumnaVar nLineaCte y nColumnaCte se declaran dentro de las acciones

- la variable matchedToken a la que se hace referencia en el coacutedigo de la accioacuten lexicograacutefica aso-ciada a una variable contiene la informacioacuten sobre la pieza sintaacutectica que se acaba de detectar una va-riable de la expresioacuten analizada anaacutelogamente con la referencia relativa a una constante- el nombre image que aparece en las acciones lexicograacuteficas se refiere al campo de la clase Expr-MinTokenManager (analizador lexicograacutefico) noacutetese que en efecto se comunica como un paraacuteme-tro de clase StringBuffer

- la variable numero se emplea para numerar las constantes y variables encontradas

- los nombres beginLine y beginColumn son campos (puacuteblicos) de la clase Token por ello se pueden aplicar a un objeto de esa clase

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

void unaExpresion()

28

expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n StringBuffer lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

TOKEN lt variable [a-z] gt

int nLineaVar nColumnaVar numero++ nLineaVar = matchedTokenbeginLine nColumnaVar = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaVar nColumnaVar)

TOKEN lt constante ( [0-9] ) + gt

int nLineaCte nColumnaCte

29

numero++ nLineaCte = matchedTokenbeginLine nColumnaCte = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaCte nColumnaCte)

SKIP | n | t | r

La especificacioacuten precedente se puede escribir de otra manera maacutes escueta como se indica a continua-cioacuten (soacutelo se muestran las modificaciones) sobre esta nueva especificacioacuten puede mencionarse- se han eliminado por innecesarias las variables declaradas dentro de las acciones lexicograacuteficas- el nombre image que aparece en el bloque de las declaraciones compartidas se refiere al campo de la clase Token noacutetese que en efecto se aplica a un objeto de esa clase y se comunica como un paraacute -metro de tipo String

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

TOKEN lt variable [a-z] gt

numero++ grabarDatosPieza(numero matchedToken)

TOKEN lt constante ( [0-9] ) + gt

numero++ grabarDatosPieza(numero matchedToken)

loz Accioacuten lexicograacutefica comuacutenSi hubiera que realizar la misma tarea tras la deteccioacuten de todas y cada una de las piezas sintaacutecticas una posibilidad incoacutemoda seriacutea repetir el mismo coacutedigo en todas las acciones lexicograacuteficas para evitar tanta reiteracioacuten se tiene la opcioacuten Common_Token_Action su valor por defecto es false pero si se le asigna el valor true es posible definir una ldquoaccioacuten lexicograacutefica comuacutenrdquo que se escribe una uacutenica vez y se aplica a todas las piezas sintaacutecticas

30

Cuando la opcioacuten Common_Token_Action se establece con el valor true es obligado incluir el blo-que de declaraciones lexicograacuteficas compartidas (TOKEN_MGR_DECLS) Ademaacutes en ese bloque se preci-sa la definicioacuten de un meacutetodo cuyo encabezamiento sea

[ static ] void CommonTokenAction(Token t)

el coacutedigo de este meacutetodo constituye la accioacuten lexicograacutefica comuacuten que se ejecutaraacute despueacutes de la deteccioacuten de cualquier pieza sintaacutectica el meacutetodo tiene un paraacutemetro de tipo Token mediante el que se puede co-municar la informacioacuten correspondiente a la pieza que se acaba de encontrar en la entrada analizada

Si una pieza sintaacutectica tiene asociada una accioacuten lexicograacutefica propia la accioacuten lexicograacutefica comuacuten (si es-taacute definida) se ejecutaraacute inmediatamente despueacutes de realizar la accioacuten propia

La accioacuten lexicograacutefica comuacuten se aplica a todas las piezas sintaacutecticas definidas como TOKEN bien sea no-minalmente (en las reglas lexicograacuteficas) o bien anoacutenimamente (incorporadas dentro de las reglas sintaacutecti-cas) sin embargo no se aplica cuando la secuencia acoplada se corresponde con una expresioacuten regular es-pecificada como SKIP (noacutetese que por el contrario una especificacioacuten SKIP siacute puede tener asociada una accioacuten lexicograacutefica propia)

El coacutedigo del meacutetodo CommonTokenAction se traslada como un componente maacutes del bloque de decla-raciones compartidas (TOKEN_MGR_DECLS) al principio de la clase del analizador lexicograacutefico

∆ Ejemplo 7

Se pretende ahora ampliar el ejemplo de presentacioacuten con la obtencioacuten de una relacioacuten numerada de los lexemas de las piezas sintaacutecticas encontradas en la entrada analizada en el caso de las variables y de las constantes antes del lexema se mostraraacute un indicativo (Var Const) por ejemplo si la expresioacuten de la entrada es

( a + 987 ) b

se grabaraacute la salida

( 1Var a 2+ 3Const 987 4) 5 6Var b 7 8

En la solucioacuten que se propone a continuacioacuten se muestran la definicioacuten de las opciones y las reglas sin-taacutecticas y lexicograacuteficas (en el resto no hay modificaciones) puede resaltarse lo siguiente- las llamadas al meacutetodo CommonTokenAction se incorporan automaacuteticamente no hay que incluir-las en la especificacioacuten como paraacutemetro de llamada a este meacutetodo siempre se emplea el objeto que re -presenta la pieza que se acaba de detectar- la accioacuten lexicograacutefica comuacuten realiza la actualizacioacuten del contador de piezas y la grabacioacuten del lexe-ma de la pieza detectada con su nuacutemero de orden- la accioacuten lexicograacutefica comuacuten tambieacuten se aplica a las piezas sintaacutecticas anoacutenimas (pareacutentesis y ope-radores) y a la pieza especial que representa el final del fichero (EOF)

- las piezas sintaacutecticas ltconstantegt y ltvariablegt tienen asociada una accioacuten lexicograacutefica propia que se ejecuta antes de la accioacuten lexicograacutefica comuacuten- los caracteres que se saltan (SKIP) no estaacuten afectados por la accioacuten lexicograacutefica comuacuten

options Ignore_Case = true Common_Token_Action = true

31

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarIndicativo(String indicativo) Systemoutprint(indicativo + )

static void grabarLexema (int num String lexema) Systemoutprint(lexema + + num + n)

static void CommonTokenAction(Token pieza) numero++ grabarLexema(numero piezaimage)

TOKEN lt variable [a-z] gt

grabarIndicativo(Var)

32

TOKEN lt constante ( [0-9] ) + gt

grabarIndicativo(Const)

SKIP | n | t | r

A modo de ejemplo de aplicacioacuten de la accioacuten lexicograacutefica comuacuten en lo que sigue se ofrece una solucioacuten para la grabacioacuten (en el fichero predefinido de salida) del texto analizado con las liacuteneas numeradas

∆ Ejemplo 8 [ Listado del texto analizado ]Se desea completar el ejemplo de presentacioacuten con la grabacioacuten de un listado con las liacuteneas numeradas del texto analizado (noacutetese que una expresioacuten puede escribirse ocupando varias liacuteneas) No se muestra la especificacioacuten sintaacutectica debido a que no hay ninguna modificacioacuten en ella Los aspectos resentildeables de la solucioacuten que se propone son- se usa la variable numLin como contador de liacuteneas

- en la variable linea se van yuxtaponiendo los lexemas de las piezas sintaacutecticas de una liacutenea esta yuxtaposicioacuten se realiza mediante la accioacuten lexicograacutefica comuacuten- las secuencias reconocidas con los patrones especificados como SKIP no implican la ejecucioacuten de la accioacuten lexicograacutefica comuacuten por ello su lexema (almacenado en la variable image) ha de yuxtaponer-se expliacutecitamente- hay que separar el patroacuten correspondiente al fin de liacutenea para detectar el momento en que ha de gra-barse la liacutenea completa cuyo final se ha alcanzado

options Ignore_Case = true Common_Token_Action = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(nnAnalisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

TOKEN_MGR_DECLS static int numLin = 0 static String linea =

33

static void CommonTokenAction(Token pieza) linea = linea + piezaimage

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP lt | t | r gt linea = linea + image

SKIP n numLin++ Systemoutprintln(numLin + + linea) linea =

A modo de recapitulacioacutenↄ Accioacuten lexicograacutefica Accioacuten sintaacutectica

bull accioacuten lexicograacutefica bloque de coacutedigo Java asociado a una pieza sintaacutectica nominal (TOKEN) o a una secuencia de caracteres (SKIP) se ejecuta cuando se detecta en la entrada la pieza o la secuencia corres-pondiente no se puede aplicar a piezas sintaacutecticas anoacutenimas (autodefinidas con comillas)bull accioacuten sintaacutectica bloque de coacutedigo Java asociado a un punto de la estructura sintaacutectica (el punto indica-do por el sitio de la produccioacuten donde se inserta el bloque) se ejecuta cuando el anaacutelisis de la entrada pa-sa por ese punto de la estructura

ↄ Valor de una pieza sintaacutectica comunicadaEl analizador lexicograacutefico comunica al analizador sintaacutectico una pieza a traveacutes de un valor de la clase Token (fichero Tokenjava) un objeto de la clase Token representa las caracteriacutesticas de la pieza comunicada En la clase Token entre otros se tienen los campos

String imagelexema de la pieza

int beginLine beginColumn endLine endColumnposicioacuten del lexema en el fichero analizado (indicada por los nuacutemeros de fila y de columna de su comienzo y de su terminacioacuten)

ↄ Valor asociado a un siacutembolo

34

En las producciones que especifican la sintaxis se pueden incluir asignaciones que sirven para dejar ano-tado el valor asociado a un siacutembolo

bull siacutembolo no terminal valor = nombreSimbolo()el meacutetodo de anaacutelisis asociado a nombreSimbolo se ha declarado de un cierto tipo (distinto del tipo void) valor ha de ser una variable del mismo tipo que el meacutetodo

bull siacutembolo terminal dato = lt nombrePieza gtel valor asignado es el valor de la clase Token representativo de la pieza sintaacutectica (nominal) dato ha de ser una variable de tipo Token

ↄ Miradas sobre la pieza sintaacutectica comunicadaLa pieza sintaacutectica comunicada puede mirarse desde cada uno de los dos analizadores

bull analizador sintaacutecticola pieza comunicada (desde el analizador lexicograacutefico) se tiene anotada en un campo de la clase del analizador sintaacutectico declarado como

(static) public Token token

este valor es accesible desde el coacutedigo de todos los meacutetodos de anaacutelisis sintaacutectico el contenido del campo variacutea cuando se recibe una nueva pieza sintaacutectica

bull analizador lexicograacuteficola pieza dispuesta para ser comunicada (al analizador sintaacutectico) se tiene anotada en una variable local declarada como

Token matchedToken

este valor es accesible para el coacutedigo de todas las acciones lexicograacuteficas

ↄ El lexema de la pieza comunicadaEl lexema de la pieza sintaacutectica comunicada se encuentra disponible en dos sitios distintos que compar-ten el mismo nombre pero que son de distinto tipo y se usan de distinta manera

bull String image

es un campo de la clase TOKEN puede consultarse en relacioacuten con un objeto de esa clase que esteacute disponible

bull StringBuffer image

es un campo de la clase del analizador lexicograacutefico ( ∙ ∙ ∙ ∙ TokenManagerjava) es accesible desde el coacutedigo de la clase y por lo tanto desde todas las acciones lexicograacuteficas (bloques de coacute-digo que estaacuten incorporados al analizador lexicograacutefico)

se trata de un valor disponible tanto para las piezas sintaacutecticas nominales (TOKEN) como para las secuencias que se saltan (SKIP)

ↄ Resumen relativo a la lexicografiacutea

TOKEN pieza sintaacutectica nominal∙ ∙ ∙ pieza sintaacutectica anoacutenimaSKIP secuencia de caracteres que no forman pieza

35

Precisiones sobre el analizador sintaacutectico generadoPuede decirse que en su funcionamiento maacutes sencillo cuando no se emplean las posibilidades de examen por adelantado (ldquolookaheadrdquo) JavaCC comprueba que las reglas sintaacutecticas cumplen la condicioacuten LL(1) y generan un analizador descendente-predictivo-recursivo Pero esta apreciacioacuten hay que matizarla ya que el analizador sintaacutectico generado no se ajusta en sentido estricto al modelo de analizador LL(1)

En el modelo de anaacutelisis LL(1) el orden del examen de las distintas alternativas de expansioacuten de un siacutembo-lo no terminal es indiferente ya que se conocen los siacutembolos directores de cada una de ellas (que resultan ser conjuntos disjuntos) en la implementacioacuten del subprograma de anaacutelisis asociado al siacutembolo no terminal ltNombreSimbgt cuyas producciones son

ltNombreSimbgt = α1

| α2

∙ ∙ ∙

| αn

se podriacutea preguntar en cualquier orden si la pieza actual pertenece al conjunto de siacutembolos directores de las distintas reglas Sin embargo en el analizador sintaacutectico generado por JavaCC la implementacioacuten del meacute-todo asociado al siacutembolo no terminal ltNombreSimbgt no sigue esa pauta

Si las anteriores reglas sintaacutecticas se transcriben en JavaCC de la forma

void nombreSimb () α1-jcc

| α2-jcc

∙ ∙ ∙

| αn-jcc

1

accioacutenlexicograacutefica

comuacuten

Commom -TokenAction2

accioacutenlexicograacutefica

propia

(accioacuten ligadaa una pieza)3

bloque dedeclaracioneslexicograacuteficas

TOKEN_MGR_DECLS4

declaracioneslexicograacuteficaspredefinidas

(disponibilidad

de uso)TOKENsisisisi∙ ∙ ∙sino--SKIPnosisisi

Las indicaciones si de las columnas 3 y 4 son consecuencia de las indicaciones de la columna 2

La accioacuten lexicograacutefica comuacuten se ejecuta despueacutes de la accioacuten lexicograacutefica propia

36

el meacutetodo de anaacutelisis generado procede de la siguiente manera

se van considerando las alternativas sucesivamente en el mismo orden en el que estaacuten especifica-das primero la de α1 (α1-jcc) despueacutes la de α2 (α2-jcc) etc

para cada alternativa seleccionada se comprueba si la pieza por adelantado coincide con alguno de los siacutembolos iniciales de la parte derecha y si hay coincidencia se prosigue el anaacutelisis con ella si no hay coincidencia se pasa a la alternativa siguiente

siguiendo con este orden si se llega al final de las alternativas sin haberse encontrado coincidencia alguna se produce un error sintaacutectico

En el analizador generado por JavaCC hay una peculiaridad que no se tiene en los analizadores LL(1) es-trictamente considerados la palabra vaciacutea siacute se considera como posible siacutembolo inicial de la parte derecha una regla (esto ocurre siempre que la parte derecha es anulable) De esta peculiaridad se deriva una conse-cuencia que ha de tenerse en cuenta al escribir la especificacioacuten dado que siempre es posible considerar la palabra vaciacutea como la siguiente pieza por adelantado si se llega a examinar la alternativa correspondiente a una produccioacuten anulable el analizador generado siempre seleccionaraacute esa alternativa con independencia de la siguiente pieza que esteacute presente en la entrada que queda por analizar

Asiacute pues si alguna de las producciones que definen un siacutembolo no terminal es anulable es importante el or-den en que se escriben las alternativas en una especificacioacuten sintaacutectica JavaCC el funcionamiento del ana-lizador generado siacute depende de la colocacioacuten elegida para las reglas

Para ilustrar estas peculiaridades del analizador sintaacutectico generado por JavaCC a continuacioacuten se expone un ejemplo

∆ Ejemplo 9

Se quiere obtener un analizador leacutexico-sintaacutectico de textos que se ajustan al siguiente formato una se-cuencia de uno o maacutes nombres seguida del siacutembolo igual seguido de un nuacutemero entero si la secuencia tiene maacutes de un nombre han de estar separados bien por una coma en todos los casos o bien por el siacutem-bolo dos puntos en todos los casos esto es son correctas las secuencias

uno dos tres = 123exclusivo = 0Abajo Arriba = 57

y son incorrectas las secuenciasuno dos tres = 123

exclusivo inclusive = 0

fuera dentro = 56

Una gramaacutetica que define las secuencias con este formato es

ltListagt = nombre ltOtrosNombresgt = numero

ltOtrosNombresgt = nombre nombre | nombre nombre | ε

se trata de una gramaacutetica que cumple la condicioacuten LL(1)- el siacutembolo director para la alternativa vaciacutea es el siacutembolo igual es evidente pues que las tres reglas de ltOtrosNombresgt tienen siacutembolos directores distintos

37

- la decisioacuten de abandonar la repeticioacuten del anaacutelisis de un nuevo nombre (en las dos primeras alterna-tivas de ltOtrosNombresgt) se puede tomar ante la presencia del siacutembolo igual

En la implementacioacuten de un analizador sintaacutectico LL(1) en sentido estricto la seleccioacuten de la alternativa vaciacutea para expandir el siacutembolo ltOtrosNombresgt se hariacutea tras comprobar expliacutecitamente que la pie-za sintaacutectica por adelantado es el siacutembolo igual

El funcionamiento del analizador generado por JavaCC depende del orden de colocacioacuten de las reglas A continuacioacuten se exponen varias posibilidades de especificacioacuten y se comenta el analizador generado en cada caso

bull Especificacioacuten primeraSe transcriben literalmente las reglas tal y como se han escrito antes en la notacioacuten BNF noacutetese que para poder codificar la parte derecha de la regla vaciacutea ha de incluirse una accioacuten sintaacutectica vaciacutea

PARSER_BEGIN (Igualdad)

public class Igualdad

public static void main (String[] argum) throws ParseException Igualdad analisis = new Igualdad(Systemin) analisissecuencia() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (Igualdad)

void secuencia() lista() ltEOFgt

void lista() lt nombre gt otrosNombres() = lt numero gt

void otrosNombres() lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt ) |

TOKEN lt nombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN lt numero ( [ 0 - 9 ] )+ gt

SKIP | r | n

38

El analizador generado funciona correctamente puesto que en el meacutetodo otrosNombres se selec-ciona la alternativa vaciacutea despueacutes de haber comprobado que la pieza por adelantado no es siacutembolo ini-cial de ninguna de las dos alternativas precedentes (no es una coma ni un siacutembolo dos puntos)

bull Especificacioacuten segundaSe considera la misma especificacioacuten anterior pero cambiando el orden de colocacioacuten de las reglas ahora la produccioacuten vaciacutea se coloca como primera alternativa (soacutelo se muestran las partes modifica-das)

void otrosNombres() | lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt )

Con la entrada de esta especificacioacuten el generador JavaCC produce un mensaje en el que se avisa de que las dos alternativas que estaacuten colocadas detraacutes de la alternativa vaciacutea nunca podraacuten seleccionarse aun asiacute se genera un analizador Pero es un analizador que funciona incorrectamente asiacute por ejemplo la entrada

mxa nyz = 57

se considera incorrecta despueacutes de visto el primer nombre se intenta aplicar la produccioacuten vaciacutea (se espera encontrar el siacutembolo igual) pero habriacutea que aplicar la regla cuyo siacutembolo director es la coma

bull Especificacioacuten terceraSe utilizan las posibilidades de la notacioacuten BNF-Ampliada para prescindir de la especificacioacuten expliacuteci-ta de la palabra vaciacutea se aprovecha que JavaCC admite el operador + para indicar repeticiones de una o maacutes veces la alternativa vaciacutea estaacute impliacutecitamente contemplada en la posibilidad de cero repeti-ciones que contempla el operador (soacutelo se muestran las partes modificadas en la especificacioacuten)

void otrosNombres() ( lt nombre gt )+ | ( lt nombre gt )

El meacutetodo otrosNombres() generado hace las comprobaciones en el siguiente orden

- se comprueba si la pieza por adelantado es una coma (siacutembolo inicial de la primera alternativa) y en este caso se selecciona la primera regla- si no es una coma se comprueba si es un siacutembolo dos puntos (siacutembolo inicial de la segunda alter-nativa) y en este caso se selecciona la segunda regla- si no es un siacutembolo dos puntos se considera la palabra vaciacutea como siacutembolo inicial (siempre posi-ble impliacutecitamente) para la segunda alternativa en este caso para que no haya error la pieza por adelantado ha de ser un siacutembolo igual

Se trata pues de un analizador que funciona correctamente

bull Especificacioacuten cuarta

39

Se pone la misma especificacioacuten anterior pero cambiando el orden de consideracioacuten de la regla vaciacutea ahora se incluye impliacutecitamente en la primera alternativa (soacutelo se muestran las partes modificadas)

void otrosNombres() ( lt nombre gt ) | ( lt nombre gt )+

Para esta especificacioacuten el generador JavaCC emite un aviso en el que se indica que la segunda alter -nativa nunca se seleccionaraacute no obstante se genera un analizador que no funciona como se supone que deberiacutea hacerlo por ejemplo el anaacutelisis de la entrada

nyz mxa = 1

produce un error despueacutes de tratado el primer nombre se intenta aplicar la produccioacuten vaciacutea (corres-pondiente a la repeticioacuten de cero veces contemplada en la primera regla) por lo que no se encuentra el esperado siacutembolo igual

40

Page 2: JavaCC

Contenido

Principios baacutesicos del generador JavaCC2Descripcioacuten inicial2Instalacioacuten y documentacioacuten2Obtencioacuten de un analizador

leacutexico-sintaacutectico3Ejemplo de presentacioacuten4Analizadores generados5Forma de una especificacioacuten JavaCC6Seccioacuten de

opciones6Seccioacuten de ejecucioacuten7Seccioacuten de sintaxis8Seccioacuten de lexicografiacutea11Tareas asociadas a la estructura

sintaacutectica16Bloque para un siacutembolo16Acciones sintaacutecticas16Valor comunicado por un meacutetodo18Comunicacioacuten entre los

analizadores leacutexico y sintaacutectico22Tareas asociadas a las piezas sintaacutecticas25Accioacuten ligada a una pieza sintaacutectica25Bloque

de declaraciones lexicograacuteficas27Declaraciones lexicograacuteficas predefinidas27Accioacuten lexicograacutefica comuacuten30A modo de

recapitulacioacuten34Precisiones sobre el analizador sintaacutectico generado36

1

Principios baacutesicos del generador JavaCC

loz Descripcioacuten inicialEl generador JavaCC (Java Compiler Compiler) es una herramienta para generar analizadores de lengua-jes acepta como entrada una especificacioacuten de un determinado lenguaje y produce como salida un analiza-dor para ese lenguaje el analizador generado estaacute escrito en Java La especificacioacuten proporcionada al gene-rador JavaCC puede contemplar distintos aspectos del lenguaje para el que se quiere obtener el analizador

- Caracteriacutesticas lexicograacuteficas y sintaacutecticases la forma maacutes frecuente de uso del generador la especificacioacuten proporcionada define las caracteriacutesti -cas sintaacutecticas y lexicograacuteficas de un lenguaje y se genera un analizador leacutexico-sintaacutectico del lenguaje especificado

- Caracteriacutesticas lexicograacuteficasen la especificacioacuten proporcionada al generador soacutelo se definen caracteriacutesticas lexicograacuteficas del lengua-je con el coacutedigo generado se puede obtener un analizador lexicograacutefico

- Caracteriacutesticas lexicograacuteficas y sintaacutecticas y comprobaciones semaacutenticastambieacuten es posible completar una especificacioacuten leacutexico-sintaacutectica con la inclusioacuten de coacutedigo Java com-plementario para que el programa generado (que incorpora adecuadamente ese coacutedigo auxiliar) pueda hacer un anaacutelisis completo (leacutexico sintaacutectico y semaacutentico) del lenguaje especificado

loz Instalacioacuten y documentacioacutenDado que el coacutedigo generado por JavaCC estaacute escrito en Java es necesario disponer de una versioacuten del sis-tema Java (compilador de Java e inteacuterprete de la Maacutequina Virtual Java) Son programas de libre distribu-cioacuten y faacuteciles de conseguir

El generador JavaCC tambieacuten es un programa de libre distribucioacuten se puede conseguir en

la paacutegina oficial de JavaCC httpsjavaccdevjavanet

la paacutegina de la asignatura

(se tiene la versioacuten Java Compiler Compiler version 50)

Se consigue un fichero empaquetado de nombre javacc-50zip tras desempaquetar (en un determi-nado directorio que puede elegirse como convenga) dicho fichero se tienen instalados entre otros los si-guientes ficheros (que son los que interesan para el sistema operativo windows)

middotmiddotmiddotmiddot javacc-50binjavaccbatmiddotmiddotmiddotmiddot javacc-50binjjdocbatmiddotmiddotmiddotmiddot javacc-50binjjtreebat

los nombre de estos ficheros se corresponden con los nombres de los comandos para llamar a las herra-mientas instaladas

javacc generador de analizadores

jjdoc productor de documentacioacuten

jjtree preprocesador de apoyo para tareas semaacutenticas

Para que la llamada a estos comandos pueda realizarse desde cualquier directorio ha de anotarse el camino (PATH) que lleve hasta middotmiddotmiddotmiddot javacc-50bin Tambieacuten ha de tenerse en cuenta si la instalacioacuten del sistema Java se tiene preparada para que el compilador (javac) y el inteacuterprete (java) se puedan ejecutar desde cualquier directorio

Para comprobar si la instalacioacuten del generador se ha realizado adecuadamente se puede llamar desde la liacute-nea de comandos al generador javacc y apareceraacute por pantalla una informacioacuten sobre el uso de dicho comando la primera liacutenea de esa informacioacuten es

Java Compiler Compiler Version 50 (Parser Generator)

2

Documentacioacuten sobre JavaCC

Se dispone de abundante documentacioacuten relativa a JavaCC entre otras se pueden encontrar

documentacioacuten que acompantildea a los ficheros de la versioacuten instalada se tienen diversos ficheros enmiddotmiddotmiddotmiddot javacc-50dochtml

(en javaccgrmhtml se tiene una descripcioacuten general del generador)

documentacioacuten variada en la red

JavaCC DocumentationThe JavaCC TutorialIntroduction to JavaCCThe JavaCC FAQetc

Primera prueba con JavaCC

En lo que sigue se expone un ldquoejemplo de presentacioacutenrdquo completo la generacioacuten de un analizador leacutexico-sintaacutectico para un tipo de expresiones muy sencillo Se puede empezar el estudio de JavaCC probando su funcionamiento con ese ejemplo

loz Obtencioacuten de un analizador leacutexico-sintaacutectico

bull Pasos para la generacioacuten del analizador

1- Edicioacuten de la especificacioacuten (editor de texto plano)

vi | edit |∙ ∙ ∙ NombreFicherojj

(el nombre del fichero puede tener cualquier extensioacuten suele usarse jj)

2- Ejecucioacuten del generador

javacc NombreFicherojj

Si el nombre elegido para la especificacioacuten es NombreDeLaEspecif (maacutes adelante se indica la manera de dar un nombre a la especificacioacuten) como resultado de la generacioacuten se obtiene (ademaacutes de otros ficheros auxiliares) el fichero

NombreDeLaEspecifjava

3- Compilacioacuten del analizador generado

javac NombreDeLaEspecifjava

Como resultado de la compilacioacuten se obtiene (ademaacutes de otras clases auxiliares) el ficheroNombreDeLaEspecifclass

bull Ejecucioacuten del analizador generadoSi el nombre del fichero donde se encuentra el texto fuente (escrito en el lenguaje para el que se ha generado el analizador) que se pretende analizar es Programalen

java NombreDeLaEspecif lt Programalen

Si se desea que los resultados del anaacutelisis en vez de presentarse por pantalla queden grabados en un fi -chero de nombre Salidadat

java NombreDeLaEspecif lt Programalen gt Salidadat

3

loz Ejemplo de presentacioacuten

bull Descripcioacuten del lenguaje

El lenguaje L estaacute formado por las expresiones en las que pueden aparecer- variables- constantes- operadores + y

Las variables son nombres formados por una uacutenica letra (minuacutescula o mayuacutescula) las constantes son nuacute-meros enteros de una o maacutes cifras El espacio y el tabulador pueden estar presentes pero no tienen ninguacuten significado los finales de liacutenea tampoco son significativos (una expresioacuten puede codificarse ocupando una o maacutes liacuteneas)

La sintaxis de las expresiones se especifica mediante la siguiente gramaacutetica

ltExpresiongt = ltTerminogt + ltTerminogt ltTerminogt = ltFactorgt ltFactorgt ltFactorgt = variable | constante | ( ltExpresiongt )

bull Especificacioacuten leacutexico-sintaacutectica codificada con la notacioacuten JavaCCUna manera de escribir la especificacioacuten (para la que se ha elegido el nombre ExprMin) de forma que sea aceptada por el generador es

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Anaacutelisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

4

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP | t | n | r

bull Obtencioacuten del analizadorSi la especificacioacuten precedente se tiene grabada en un fichero de nombre Ejemplojj para obtener el analizador

- se ejecuta el generador javacc Ejemplojj

- se compila el analizador generado javac ExprMinjava

bull Ejecucioacuten del analizadorSi se quiere analizar una expresioacuten grabada en un fichero de nombre PruebaExptxt

- se ejecuta el analizador obtenido java ExprMin lt PruebaExptxt

loz Analizadores generadosEn su funcionamiento maacutes sencillo y habitual JavaCC genera un analizador sintaacutectico complementado con un analizador lexicograacutefico para que conjuntamente se pueda realizar un anaacutelisis leacutexico-sintaacutectico de un texto de entrada

El analizador sintaacutectico obtenido es en general LL(k) descendente y determinista con la consulta de k siacutembolos por adelantado si la gramaacutetica proporcionada cumple la condicioacuten LL(1) se genera un analiza -dor sintaacutectico descendente-predictivo-recursivo Maacutes adelante se hacen algunas precisiones sobre esta afir-macioacuten

Si la especificacioacuten leacutexico-sintaacutectica de un lenguaje codificada en JavaCC tiene dado (como indicativo que acompantildea a las palabras reservadas PARSER_BEGIN y PARSER_END) el nombre EspLexSin y se tiene grabada en un fichero de nombre Lenguajejj cuando se ejecuta el generador tomando como entrada ese fichero

javacc Lenguajejj

se obtienen los siguientes ficheros (clases) con coacutedigo Java

Tokenjavadescripciones para la comunicacioacuten entre los analizadores leacutexico y sintaacutectico

5

TokenMgrErrorjavatratamiento de errores para el anaacutelisis lexicograacutefico

ParseExceptionjavatratamiento de errores para el anaacutelisis sintaacutectico

SimpleCharStreamjavacomponentes para la realizacioacuten de las tareas de entradasalida del analizador

EspLexSinConstantsjavadefinicioacuten de la representacioacuten interna de las piezas sintaacutecticas

EspLexSinTokenManagerjavaanalizador lexicograacutefico

EspLexSinjavaanalizador sintaacutectico

Puede apreciarse que hay dos categoriacuteas de nombres de ficheros generados los cuatro primeros nombres citados no dependen del nombre de la especificacioacuten considerada los otros nombres de ficheros se forman a partir del nombre dado a la especificacioacuten

Forma de una especificacioacuten JavaCC (versioacuten simplificada)La forma que se describe en lo que sigue es una versioacuten simplificada el generador JavaCC admite especificaciones con otras muchas posi-bilidades no mencionadas en esta introduccioacuten

Una especificacioacuten para el generador JavaCC puede considerarse dividida en cuatro secciones

loz Seccioacuten de opcionesEn esta seccioacuten cuya presencia es optativa se pueden asignar valores a diversos paraacutemetros (llamados op-ciones) que sirven para configurar ciertas caracteriacutesticas del funcionamiento del generador o del analizador generado Cada paraacutemetro (opcioacuten) tiene un valor por defecto que es el que toma cuando no se le asigna expliacutecitamente un valor Los valores de las opciones tambieacuten se pueden fijar en la liacutenea de comandos

Seccioacuten de opciones

Seccioacuten de ejecucioacuten

Seccioacuten de sintaxis

Seccioacuten de lexicografiacutea

6

cuando se ejecuta el generador (lo indicado en la liacutenea de comandos tiene prioridad sobre lo especificado en esta seccioacuten de opciones)

Algunas de las opciones para las que se puede fijar un valor son

Ignore_Case (valor por defecto false)

indica si en el texto analizado ha de distinguirse o no entre letras minuacutesculas y mayuacutesculas

Build_Parser (valor por defecto true)

indica si se genera el analizador sintaacutectico o no

Build_Token_Manager (valor por defecto true)

indica si se genera el analizador lexicograacutefico o no

Sanity_Check (valor por defecto true)

indica si se realizan comprobaciones sobre la gramaacutetica sintaacutectica

Debug_Parser (valor por defecto false)

indica si se genera una traza para el anaacutelisis leacutexico-sintaacutectico

Error_Reporting (valor por defecto true)

indica si los mensajes de error emitidos son maacutes o menos explicativos

Static (valor por defecto true)

con el valor por defecto los meacutetodos de los analizadores leacutexico y sintaacutectico se generan con el des-criptor estaacutetico (static)

Los nombres de las opciones pueden escribirse con letras minuacutesculas o mayuacutesculas los valores de las op-ciones son en la mayoriacutea de los casos un valor entero o un valor loacutegico Si se incluye la seccioacuten se empie-za poniendo la palabra reservada options en este caso al menos ha de ponerse una opcioacuten

La forma de esta seccioacuten es

options nombreOpcion1 = valorOpcion1

nombreOpcion2 = valorOpcion2

∙ ∙ ∙ ∙

nombreOpcionk = valorOpcionk

loz Seccioacuten de ejecucioacutenEn esta seccioacuten se pone el coacutedigo Java que contiene la llamada al analizador generado para que se realice el anaacutelisis de un determinado texto de entrada Tambieacuten se establece aquiacute el nombre de la especificacioacuten que es el nombre que se toma para formar los nombres de parte de los ficheros (clases) generados

La seccioacuten estaacute delimitada por dos palabras reservadas ambas acompantildeadas por un mismo nombre (puesto entre pareacutentesis) ese nombre es el que se da a la especificacioacuten Entre esas dos palabras ha de ponerse una clase directora para el proceso de anaacutelisis el nombre de esa clase directora ha de coincidir con el nombre dado a la especificacioacuten

PARSER_BEGIN (NombreDeLaEspecif)

7

∙ ∙ ∙ ∙

public class NombreDeLaEspecif

∙ ∙ ∙ ∙

∙ ∙ ∙ ∙

PARSER_END (NombreDeLaEspecif)

Una posible versioacuten sencilla de la clase directora es

public class NombreDeLaEspecif public static void main (String[] argum) throws ParseException NombreDeLaEspecif anLeSi = new NombreDeLaEspecif (Systemin) anLeSisimboloPrograma()

se crea el objeto anLeSi para poder aplicar los meacutetodos de anaacutelisis se asocia la entrada standard ( Systemin ) a la entrada para el analizador generado se aplica al objeto creado el meacutetodo de anaacutelisis correspondiente al siacutembolo inicial de la gramaacutetica

loz Seccioacuten de sintaxisEn esta seccioacuten se describe la sintaxis del lenguaje para el que se desea generar el analizador usaacutendose pa-ra ello una notacioacuten parecida a la BNF En lo que sigue se expone la forma de las producciones tal y como se escriben en JavaCC ponieacutendolas en comparacioacuten con las producciones de la notacioacuten BNF-Ampliada

bull Reglas sintaacutecticasEl conjunto de reglas que definen un siacutembolo no terminal

ltNombreSimbgt = α1

| α2

∙ ∙ ∙

| αn

se codifica de la siguiente manera

void nombreSimb () α1-jcc

| α2-jcc

∙ ∙ ∙

| αn-jcc

8

- el nombre del siacutembolo no terminal (parte izquierda de las reglas) se escribe como el encabeza-miento de un meacutetodo en Java sin argumentos y sin valor de devolucioacuten (meacutetodo de tipo void) se sigue como en Java la costumbre de escribir los nombres de los meacutetodos empezando con letra mi-nuacutescula

- el siacutembolo dos puntos representa la separacioacuten entre las partes izquierda y derechas de las reglas (en vez del siacutembolo = de la notacioacuten BNF)

- el siacutembolo barra vertical representa la separacioacuten entre distintas partes derechas con la misma parte izquierda (igual que en la notacioacuten BNF)

- despueacutes del siacutembolo dos puntos se pone un bloque de coacutedigo Java este coacutedigo se suele dedicar a la realizacioacuten de tareas semaacutenticas si se pretende hacer soacutelo un anaacutelisis sintaacutectico el bloque que-daraacute vaciacuteo (aunque siempre es obligada su presencia)

- el conjunto de las alternativas (partes derechas de las reglas) estaacute delimitado mediante llaves como si constituyese un bloque de coacutedigo Java

- αi-jcc representa la codificacioacuten de una alternativa (parte derecha) empleaacutendose una nota-cioacuten que se describe a continuacioacuten

bull Siacutembolos no terminales en las partes derechasEn la notacioacuten BNF se pone ltNombreSimbgt en la notacioacuten JavaCC se escribe

nombreSimb()

esto es se pone como si fuese una llamada en Java a un meacutetodo sin argumentos

bull Siacutembolos terminales en las partes derechasSe considera una clasificacioacuten en las piezas sintaacutecticas (siacutembolos terminales de una gramaacutetica sintaacutectica) distinguieacutendose entre piezas sintaacutecticas nominales y anoacutenimas

son nominales las piezas sintaacutecticas a las que se asocia un nombre en otra parte de la especificacioacuten (maacutes adelante se describe la manera de indicar esta asociacioacuten) ese nombre asociado a la pieza es el que se usa al escribir las partes derechas de las producciones En la notacioacuten JavaCC un siacutembolo terminal no-minal se escribe poniendo el nombre delimitado por los caracteres lt y gt

ltnombreTerminalgt

son anoacutenimas las piezas sintaacutecticas que no tienen nombre asociado no se precisa porque la pieza se re -presenta por su propia secuencia de caracteres (lexema) En la notacioacuten JavaCC un siacutembolo terminal anoacutenimo se escribe poniendo su lexema delimitado por comillas por ejemplo

= lt lt= END

bull Metasiacutembolos de opcionalidad y de repeticioacutenEn la notacioacuten BNF se emplean los corchetes para indicar opcionalidad en la notacioacuten JavaCC se utilizan tambieacuten esos mismos siacutembolos

[ α ] en ambas notaciones

En la notacioacuten BNF se emplean las llaves para indicar una repeticioacuten de cero o maacutes veces en la notacioacuten JavaCC se utilizan los pareacutentesis y un asterisco detraacutes del pareacutentesis de cerrar

α en notacioacuten BNF-Ampliada

( α ) en notacioacuten JavaCC

bull Otros metasiacutembolos de la notacioacuten JavaCC

9

La opcionalidad tambieacuten se puede indicar empleando el metasiacutembolo de interrogacioacuten

( α )

Para indicar repeticioacuten de una o maacutes veces se utiliza el metasiacutembolo operador aditivo suma

( α )+

bull Control del final de fichero de entradaPara controlar el final del fichero en el texto analizado ha de antildeadirse una regla sintaacutectica especial que tenga (seguacuten la notacioacuten BNF) la forma

ltProgramaCompletogt = ltProgramagt FF

donde

ltProgramaCompletogt es un nuevo siacutembolo (no terminal) auxiliar antildeadido

ltProgramagt es el siacutembolo inicial de la gramaacutetica sintaacutectica

FF es una representacioacuten del final del fichero

En JavaCC el final de fichero se escribe con la notacioacuten ltEOFgt siendo EOF una palabra reservada la regla sintaacutectica especial antildeadida a la especificacioacuten quedariacutea codificada asiacute

void programaCompleto() programa() ltEOFgt

bull Representacioacuten de la produccioacuten vaciacuteaLas producciones vaciacuteas (reglas sintaacutecticas cuya parte derecha es la palabra vaciacutea ε ) no se pueden repre-sentar expliacutecitamente en una especificacioacuten leacutexico-sintaacutectica escrita en JavaCC Para indicar la presencia de una produccioacuten vaciacutea han de aplicarse las posibilidades de la notacioacuten BNF-Ampliada seguacuten esta nota-cioacuten cuando se indica la opcionalidad de un componente se estaacute especificando impliacutecitamente la palabra vaciacutea (en el caso de que el componente no esteacute presente)

Asiacute pues si se quisiera escribir en JavaCC la construccioacuten sintaacutectica expresada por las reglas

ltComponentegt = α

| ε

deberiacutean de considerarse con la notacioacuten equivalente

ltComponentegt = [ α ]

y si hubiera varias alternativas no vaciacuteas

ltComponentegt = α | β

| ε

habriacutea que considerarlas como

ltComponentegt = [ α | β ]

o tambieacuten podriacutea verse como

ltComponentegt = α

10

| [ β ]

Como ejemplo sea la especificacioacuten sintaacutectica de una llamada a una funcioacuten que puede tener cero o maacutes paraacutemetros separados por comas aunque no haya paraacutemetros los pareacutentesis estaacuten presentes Las siguien-tes reglas definen esta estructura

ltLlamadagt = id ( ltParametrosgt )

ltParametrosgt = ltExpresiongt ltExpresiongt | ε

En JavaCC la definicioacuten del siacutembolo ltParametrosgt se puede escribir como sigue (noacutetese que aquiacute los pareacutentesis empleados para aplicar el operador son metasiacutembolos)

void parametros() [ expresion() ( expresion() ) ]

loz Seccioacuten de lexicografiacuteaEn esta seccioacuten se indica la lexicografiacutea del lenguaje para el que se va a generar el analizador la notacioacuten de JavaCC que representa la forma de cada una de las piezas sintaacutecticas es una variante de las bien conoci-das expresiones regulares

bull El nombre de las piezas sintaacutecticas nominalesPara asociar un nombre a una pieza sintaacutectica y para al mismo tiempo definir la forma de la pieza se em-plea la notacioacuten

TOKEN lt nombreTerminal Expresioacuten-Regular gt

- TOKEN es una palabra reservada detraacutes de ella se pone el separador dos puntos

- nombreTerminal representa el nombre que se quiere asociar a la pieza sintaacutectica

- Expresioacuten-Regular indica una expresioacuten regular que especifica la forma de la pieza

- el nombre y la expresioacuten se separan mediante el caraacutecter dos puntos y conjuntamente se deli -mitan mediante los caracteres lt y gt a su vez toda la asociacioacuten se delimita con llaves

bull Expresiones regulares en JavaCCLas expresiones regulares en una especificacioacuten JavaCC se escriben de manera parecida a la forma utiliza-da en otras conocidas notaciones aunque con ciertas peculiaridades A continuacioacuten se mencionan los as-pectos maacutes significativos de las expresiones regulares JavaCC y algunos ejemplos

Los operadores de las expresiones son

| unioacuten

repeticioacuten cero o maacutes veces

+ repeticioacuten una o maacutes veces

opcionalidad

11

Los pareacutentesis permiten fijar el orden de aplicacioacuten de los operadores Una peculiaridad de las expresiones regulares JavaCC es que los operadores de repeticioacuten y de opcionalidad requieren que el componente al que se aplican esteacute en todo caso delimitado por pareacutentesis

Se pueden citar las siguientes caracteriacutesticas baacutesicas de las expresiones regulares en JavaCC

Se tiene la posibilidad de definir conjuntos de caracteres poniendo entre corchetes la relacioacuten de carac-teres separados por comas y cada caraacutecter delimitado por comillas En un conjunto se puede fijar un ran-go de caracteres poniendo el primero y el uacuteltimo separados por el siacutembolo guioacuten (-) A veces resulta maacutes coacutemodo indicar los caracteres que no pertenecen al conjunto para ello se puede definir el comple -mentario de un conjunto poniendo el siacutembolo ~ delante del corchete inicial

Para indicar que una secuencia de caracteres colocados consecutivamente constituye por siacute misma una expresioacuten regular (o un componente de una expresioacuten) se escribe la secuencia delimitada por comillas El siacutembolo se emplea como metacaraacutecter para indicar que el siacutembolo que estaacute inmediatamente a su derecha se considere como caraacutecter del alfabeto no como posible metacaraacutecter (operador delimitador)

En una expresioacuten regular se pueden incluir como si fueran caracteres las secuencias de escape con la misma notacioacuten y el mismo significado que en Java o en C

Noacutetese que en una produccioacuten sintaacutectica la opcionalidad se puede especificar de dos formas con corchetes o con el siacutembolo de interrogacioacuten sin embargo en una expresioacuten regular la opcionalidad soacutelo se puede es-pecificar mediante el siacutembolo de interrogacioacuten (los corchetes en las expresiones regulares son para definir conjuntos de caracteres)

A continuacioacuten se muestran ejemplos de expresiones regulares escritas seguacuten la notacioacuten JavaCC

ENDsecuencia de las tres letras mayuacutesculas que conforman la palabra END

whilesecuencia de las cinco letras minuacutesculas que conforman la palabra while

ncaraacutecter representativo del fin de liacutenea (secuencia de escape)

rcaraacutecter representativo del retorno de carro (secuencia de escape)

caraacutecter dos puntos

=secuencia de dos caracteres (siacutembolo de asignacioacuten en Pascal)

gtcaraacutecter representativo del operador de relacioacuten mayor

(i | I)(f | F)palabra ldquoifrdquo escrita con letras minuacutesculas o mayuacutesculas (cuatro combinaciones)

if | IFpalabra ldquoifrdquo escrita soacutelo con minuacutesculas o soacutelo con mayuacutesculas (dos combinaciones)

12

u | o | i | e | auna cualquiera de las vocales minuacutesculas (pero soacutelo una de ellas)

[ A E I O U ]una cualquiera de las vocales mayuacutesculas (pero soacutelo una de ellas)

[ a - z ntilde ]una cualquiera de las letras minuacutesculas incluyendo la letra ntilde

[ a - z A - Z 0 - 9 ]una cualquiera de las letras o de las cifras decimales

([ 0 - 9 ])+constante entera (nuacutemero de una o maacutes cifras)

noacutetese que es incorrecto poner [ 0 - 9 ]+

( + | - )([ 0 - 9 ])+constante entera precedida opcionalmente por un signo

noacutetese que la primera cifra ha de seguir inmediatamente al signo (si existe)

([+ -])([ 0 - 9 ])+representa lo mismo que la expresioacuten anterior

[ a - z ] ([ a - z 0 - 9 ])una letra minuacutescula seguida de cero o maacutes letras minuacutesculas o cifras decimales

~[a - z ntilde A - Z Ntilde]cualquier caraacutecter que no sea una letra

noacutetese que la expresioacuten representa un uacutenico caraacutecter

~[n r]cualquier caraacutecter (un uacutenico caraacutecter) excepto el fin de liacutenea y el retorno de carro

~[ ]cualquier caraacutecter del alfabeto (un uacutenico caraacutecter)

([ 0 - 9 ])+ ( ([ 0 - 9 ])+)constante aritmeacutetica con parte decimal opcional

Los modernossecuencia de catorce caracteres Los modernos

noacutetese el uso de para imponer la condicioacuten de caraacutecter del siacutembolo comillas

nsecuencia de cuatro caracteres n

noacutetese que aquiacute no se representa la secuencia de escape

bull Piezas sintaacutecticas internas (ldquoprivaterdquo)

13

Es posible definir piezas sintaacutecticas auxiliares llamadas ldquointernasrdquo porque no se comunican al analizador sintaacutectico sino que soacutelo sirven para facilitar la escritura de la forma de las piezas sintaacutecticas que siacute se co-munican con el uso de las piezas internas es posible evitar repeticiones de escritura y hacer maacutes legible la especificacioacuten de las caracteriacutesticas lexicograacuteficas Las piezas internas permiten dar nombre a subexpre-siones regulares que a su vez pueden emplearse como componentes para escribir expresiones maacutes com-plejas que incorporan esos nombres Para indicar que una pieza sintaacutectica se considere como interna se pone el siacutembolo delante del nombre asociado a la pieza

A continuacioacuten se muestra un ejemplo tiacutepico de utilizacioacuten de las piezas internas en una especificacioacuten le-xicograacutefica en JavaCC Se quieren definir tres piezas sintaacutecticas que representan un nombre una constante entera y una constante decimal una posible especificacioacuten es

TOKEN ltnombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN ltcteEntera ( [ 0 - 9 ] )+ gt

TOKEN ltcteDecimal ( [ 0 - 9 ] )+ ( [ 0 - 9 ] )+ gt

Con la inclusioacuten de tres piezas sintaacutecticas internas auxiliares esta misma especificacioacuten podriacutea escribirse de la siguiente manera

TOKEN lt letra [ a - z A - Z ntilde Ntilde ] gt

TOKEN lt cifra [ 0 - 9 ] gt

TOKEN lt numero ( ltcifragt )+ gt

TOKEN lt nombre ( ltletragt )+ gt

TOKEN lt cteEntera ltnumerogt gt

TOKEN lt cteDecimal ltnumerogt ltnumerogt gt

Los nombres letra cifra y numero son exclusivos (internos) del analizador lexicograacutefico y no se pueden emplear como nombres de piezas en la especificacioacuten de las reglas sintaacutecticas cuando esos nom-bres se usan en la definicioacuten de otras piezas han de escribirse delimitados por los siacutembolos lt y gt

bull Distincioacuten restringida entre minuacutesculas y mayuacutesculasSe tiene la opcioacuten Ignore_Case para determinar si en el anaacutelisis del texto de entrada se distingue entre las letras minuacutesculas y las mayuacutesculas el valor por defecto false indica que siacute se diferencian si se asig-na el valor true a esta opcioacuten entonces no se diferencian en ninguacuten caso con independencia de que en los patrones que se definan para las piezas sintaacutecticas se pongan minuacutesculas o mayuacutesculas

Pero a veces conviene que la diferenciacioacuten entre minuacutesculas y mayuacutesculas no se aplique de manera ge-neral a toda la entrada sino en particular a las secuencias de caracteres que se acoplen a ciertos patrones Para este fin existe la posibilidad de establecer que en la expresioacuten regular que define el patroacuten de una determinada pieza sintaacutectica no se diferencie entre minuacutesculas y mayuacutesculas ello se consigue poniendo

14

[ IGNORE_CASE ]

inmediatamente detraacutes de la palabra TOKEN con la que empieza la especificacioacuten de la pieza sintaacutectica en la que se pretende la no distincioacuten entre minuacutesculas y mayuacutesculas

Noacutetese que ignore_case tiene dos significados

- es el nombre de un paraacutemetro de la seccioacuten de opciones (puede ponerse en minuacutesculas o mayuacutesculas)- es una palabra reservada que puede aplicarse (escribieacutendola entre corchetes y en mayuacutesculas) a la de -finicioacuten de una pieza sintaacutectica

Por ejemplo sea un lenguaje que en general distingue entre minuacutesculas y mayuacutesculas pero en la codifi -cacioacuten del exponente de una constante real exponencial puede ponerse la letra e en minuacutescula o en ma-yuacutescula En la especificacioacuten JavaCC para ese lenguaje la opcioacuten Ignore_Case ha de tomar el valor false en la definicioacuten de una constante exponencial son posibles las siguientes formas equivalentes

1) sin hacer uso de IGNORE_CASE

TOKEN lt cifras ( [ 0 - 9 ] )+ gt

TOKEN lt signo [+ -] gt

TOKEN lt constante

ltcifrasgt ( ltcifrasgt) [e E] (ltsignogt) ltcifrasgt gt

2) aplicando IGNORE_CASE

TOKEN lt cifras ( [ 0 - 9 ] )+ gt

TOKEN lt signo [+ -] gt

TOKEN [IGNORE_CASE] lt constante

ltcifrasgt ( ltcifrasgt) e (ltsignogt) ltcifrasgt gt

bull Secuencias de caracteres sin efecto en la estructura sintaacutecticaEn los lenguajes de programacioacuten suele haber secuencias de caracteres que no forman parte de la estructu-ra sintaacutectica de un programa esas secuencias son detectadas por el analizador lexicograacutefico pero no son comunicadas al sintaacutectico no han de considerarse como piezas sino que han de saltarse En JavaCC se pueden definir estas secuencias mediante la palabra reservada SKIP la notacioacuten de una especificacioacuten con la palabra SKIP de una secuencia que ha de saltarse es la misma que la notacioacuten usada para las espe-cificaciones con la palabra TOKEN de las piezas que han de comunicarse

Los dos casos maacutes habituales de secuencias que no se comunican al analizador sintaacutectico son

a) los caracteres separadores que no tienen significado en el texto analizadoen los lenguajes de codificacioacuten en formato libre hay caracteres del texto fuente que han de ignorarse en la mayoriacutea de los casos estos caracteres son el espacio en blanco el tabulador el retorno de carro y el fin de liacutenea esta caracteriacutestica se especifica

SKIP |t | r |n

b) los comentarios

15

en el anaacutelisis sintaacutectico de un programa los comentarios han de ignorarse una manera de especificar esta circunstancia es poniendo en una definicioacuten SKIP la expresioacuten regular que denota la forma de los comentarios del lenguaje por ejemplo para los comentarios de una liacutenea de Java se pondriacutea

SKIP

lt (~ [n r]) gt

Tareas asociadas a la estructura sintaacutecticaJavaCC genera un analizador sintaacutectico descendente recursivo constituido por un conjunto de meacutetodos ca-da siacutembolo no terminal de la gramaacutetica sintaacutectica tiene asociado un meacutetodo de anaacutelisis (el meacutetodo tiene el mismo nombre que el siacutembolo correspondiente) Un meacutetodo de anaacutelisis ademaacutes de la realizacioacuten de la parte del anaacutelisis sintaacutectico que le corresponde puede llevar a cabo otras tareas complementarias que sirvan para efectuar un anaacutelisis maacutes complejo del texto de entrada

loz Bloque para un siacutemboloAl describir la forma de las reglas sintaacutecticas de un siacutembolo no terminal en una especificacioacuten JavaCC ya se ha visto que detraacutes de la definicioacuten del nombre del siacutembolo y antes de empezar con las diferentes alter-nativas que tiene asociadas se incluye un bloque de coacutedigo Java (en el ejemplo de presentacioacuten este bloque estaacute vaciacuteo en todos los siacutembolos no terminales de la gramaacutetica) Cada bloque situado en esa posicioacuten puede considerarse ligado al siacutembolo no terminal en cuya descripcioacuten estaacute incluido el coacutedigo Java del bloque se traslada literalmente al principio del meacutetodo generado correspondiente al siacutembolo no terminal y por lo tan-to es lo primero que se considera (se ejecuta) cuando en el transcurso del proceso de anaacutelisis se realiza una llamada al meacutetodo Asiacute pues cada siacutembolo no terminal siempre tiene asociado un uacutenico bloque de coacute-digo Java ndashbloque de presencia obligada aunque pueda estar vaciacuteo- en este bloque se pueden poner de-claraciones y sentencias

loz Acciones sintaacutecticasAdemaacutes del bloque asociado a cada uno de los siacutembolos no terminales de la gramaacutetica se pueden incluir otros bloques de coacutedigo Java intercalados en cualquiera de las partes derechas de las reglas sintaacutecticas Los bloques incluidos en las partes derechas se denominan ldquoacciones sintaacutecticasrdquo (parser actions) ya que son trozos de coacutedigo que pueden considerarse asociados a los puntos de la estructura sintaacutectica donde estaacuten co-locados Una accioacuten sintaacutectica se puede poner en cualquier posicioacuten al principio al final o en el medio de la parte derecha de una regla precedida o seguida de siacutembolos terminales o no terminales el coacutedigo Java de una accioacuten sintaacutectica se traslada literalmente al meacutetodo asociado al siacutembolo no terminal de la parte izquierda de la regla de manera que se ejecuta cada vez que el analizador transcurre por el punto de la estructura sintaacutectica donde se ha incorporado la accioacuten (esto es las acciones sintaacutecticas se incorporan al coacutedigo del analizador sintaacutectico) No obstante su nombre en los bloques de las acciones sintaacutecticas suele ponerse coacutedigo dedicado a la realizacioacuten de tareas semaacutenticas

Si en el bloque asociado a un siacutembolo no terminal se ponen declaraciones los nombres ahiacute definidos son accesibles en todas las acciones sintaacutecticas intercaladas en las partes derechas de las reglas de ese siacutembolo la justificacioacuten es sencilla el coacutedigo del bloque asociado al siacutembolo se incluye al principio de un meacutetodo y el coacutedigo de las acciones pertenece a ese mismo meacutetodo

∆ Ejemplo 1

Se considera de nuevo el ejemplo de presentacioacuten ahora se pretende completar el anaacutelisis leacutexico-sintaacutec-tico con la realizacioacuten de las siguientes tareas

- cada vez que se analiza una expresioacuten un teacutermino o un factor se graba el nombre del siacutembolo no terminal correspondiente y

16

- cuando se analiza un teacutermino en el que hay operadores multiplicativos se cuenta su nuacutemero y se graba el resultado (noacutetese que se cuentan los operadores de cada teacutermino pero no se acumula la cuenta de todos los operadores de la expresioacuten)

Para la realizacioacuten de estas tareas se antildeaden bloques de coacutedigo Java En la especificacioacuten que se describe a continuacioacuten puede apreciarse- para los siacutembolos no terminales expresion termino y factor se ha incluido en los blo-ques correspondientes la operacioacuten de grabacioacuten de su nombre que se ejecutaraacute cada vez que se efec-tuacutee una llamada al meacutetodo- en el bloque asociado a termino se tiene la declaracioacuten del contador de operadores (que seraacute una variable local del meacutetodo)- en la parte derecha de la regla que define la estructura de un teacutermino se han incluido dos acciones sintaacutecticas una para contar la cantidad de operadores (situada detraacutes de la pieza sintaacutectica del opera-dor) y otra para grabar la cantidad de operadores del teacutermino (situada al final de la parte derecha)- se ha definido el meacutetodo grabar para facilitar la escritura del coacutedigo Java

options Ignore_Case = true

PARSER_BEGIN (ExprMin) public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

private static void grabar(String nombre) Systemoutprintln ( -gt + nombre + n)

PARSER_END (ExprMin)

void unaExpresion()

expresion() ltEOFgt

void expresion() grabar(Expresion)

termino() ( + termino() )

void termino() int nAst = 0 grabar(Termino)

17

factor() ( nAst++ factor() ) if (nAst gt 0) Systemoutprintln (n Asteriscos + nAst + n)

void factor() grabar(Factor)

ltconstantegt | ltvariablegt | ( expresion() )

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP | t | n | r

Cuando se incluyen acciones sintaacutecticas la legibilidad del aspecto sintaacutectico de la especificacioacuten queda oscurecida para paliar ese inconveniente puede antildeadirse un comentario que muestre la regla sintaacutectica escueta sin las acciones por ejemplo podriacutea escribirse

void termino() int nAst = 0 grabar(Termino)

factor() ( factor() )

factor()

( nAst++ factor() )

if (nAst gt 0) Systemoutprintln (n Asteriscos + nAst + n)

loz Valor comunicado por un meacutetodoEn todo lo visto hasta ahora los meacutetodos de anaacutelisis sintaacutectico generados por JavaCC no devuelven valor alguno al terminar su tarea de anaacutelisis ya que en la especificacioacuten siempre se ha declarado el nombre de un meacutetodo precedido de void (son meacutetodos de tipo void) Pero en general un meacutetodo de anaacutelisis sintaacutec-tico puede ser de cualquier tipo (primitivo o no) por ello un meacutetodo puede devolver un valor de cualquier tipo (de cualquier clase) al meacutetodo desde el que se efectuoacute la llamada

Esta posibilidad de devolucioacuten de valores tiene una utilidad fundamental en la implementacioacuten de analiza-dores de lenguajes permite la comunicacioacuten de datos (atributos de las entidades analizadas) entre los dife -rentes meacutetodos que colaboran en el anaacutelisis del programa completo

En una especificacioacuten JavaCC la llamada a un meacutetodo ocurre cuando en la parte derecha de una regla estaacute el nombre del siacutembolo no terminal asociado al meacutetodo Cuando un meacutetodo devuelve un valor (tiene un tipo distinto de void) es habitual la recepcioacuten del valor devuelto en una variable en la propia especificacioacuten

18

se puede indicar el nombre de la variable a la que se asignaraacute el valor devuelto tras la llamada para ello se emplea una notacioacuten ideacutentica a la forma que tienen las sentencias de asignacioacuten en el lenguaje Java

dato = nombreSimb()

donde dato es el nombre de la variable a la que se asigna el valor devuelto por el meacutetodo llamado esa va -riable ha de estar oportunamente declarada del mismo tipo que dicho meacutetodo

∆ Ejemplo 2

En el mismo ejemplo de presentacioacuten se desea ademaacutes del anaacutelisis leacutexico-sintaacutectico de la entrada contar la cantidad de operadores que hay en la expresioacuten A continuacioacuten se expone una especificacioacuten JavaCC que es una posible solucioacuten soacutelo se muestran las reglas sintaacutecticas (el resto de la especificacioacuten es igual que en el ejemplo de presentacioacuten)

En la especificacioacuten que se propone puede apreciarse- los siacutembolos no terminales expresion termino y factor se han declarado de tipo int los meacutetodos correspondientes devuelven un valor de tipo entero- el valor devuelto por los meacutetodos sirve para propagar a traveacutes de la estructura sintaacutectica la cuenta de los operadores que hay en el texto analizado- por tratarse de meacutetodos que devuelven un valor es preciso incluir las correspondientes sentencias de devolucioacuten return

void unaExpresion() int nOper nOper = expresion() ltEOFgt Systemoutprintln (Cantidad de operadores = + nOper)

int expresion() int n i n = termino() ( + n++ i = termino() n = n + i )

return n

int termino() int n i n = factor() ( n++ i = factor() n = n + i )

return n

int factor() int n ltconstantegt return 0 | ltvariablegt return 0 | ( n = expresion() ) return n

La especificacioacuten de un factor tambieacuten podriacutea escribirse de esta otra manera

19

int factor() int n = 0

( ltconstantegt | ltvariablegt | ( n = expresion() ) )

return n

A continuacioacuten se expone un nuevo ejemplo para ilustrar la utilidad que proporciona el valor devuelto por un meacutetodo de anaacutelisis sintaacutectico para la propagacioacuten (ascendente) de atributos (propiedades de las entida-des analizadas)

∆ Ejemplo 3

Se pretende ampliar el analizador del ejemplo de presentacioacuten para que se emita un mensaje indicativo cuando la expresioacuten analizada sea una expresioacuten en la que soacutelo aparecen constantes en la solucioacuten que se muestra soacutelo se incluyen las reglas sintaacutecticas (en el resto de la especificacioacuten no hay modificacio-nes)

void unaExpresion() boolean esCte

expresion() ltEOFgt

esCte = expresion() ltEOFgt

if (esCte) Systemoutprintln(nExpresion constanten)

boolean expresion() boolean esCte esTambienCte

termino() ( + termino() )

esCte = termino() ( + esTambienCte = termino() esCte = esCte ampamp esTambienCte )

return esCte

boolean termino() boolean esCte esTambienCte

factor() ( factor() )

esCte = factor() ( esTambienCte = factor() esCte = esCte ampamp esTambienCte )

return esCte

20

boolean factor() boolean esCte

ltconstantegt return true | ltvariablegt return false | ( esCte = expresion() ) return esCte

bull Reconsideracioacuten sobre las producciones vaciacuteasAnteriormente se ha comentado que para representar una produccioacuten vaciacutea en una especificacioacuten JavaCC hay que hacer uso de los metasiacutembolos de opcionalidad para ilustrar esta idea se ha incluido como ejem-plo la especificacioacuten de una lista de paraacutemetros compuesta por cero o maacutes expresiones separadas entre siacute por el caraacutecter coma

void parametros() [ expresion() ( expresion() ) ]

Pero si se aprovecha la posibilidad de incorporar acciones sintaacutecticas se tiene una manera de especificar directamente producciones vaciacuteas para conseguirlo basta considerar una produccioacuten vaciacutea (no tiene nada en su parte derecha) acompantildeada de una accioacuten sintaacutectica cuyo bloque de coacutedigo esteacute vaciacuteo Seguacuten esta posibilidad la especificacioacuten anterior podriacutea tambieacuten escribirse asiacute

void parametros() expresion() ( expresion() ) |

En general la accioacuten sintaacutectica asociada a una produccioacuten vaciacutea no tiene por queacute ser un bloque vaciacuteo de coacutedigo por ejemplo si se quiere que el meacutetodo que analiza los paraacutemetros de una llamada devuelva la cantidad de paraacutemetros encontrados se podriacutea escribir la siguiente especificacioacuten

int parametros()

int cantidad

( expresion() cantidad = 1

( expresion() cantidad++ )

| cantidad = 0 )

return cantidad

y si no quisiera ponerse expliacutecitamente la alternativa de la produccioacuten vaciacutea podriacutea escribirse

int parametros() int cantidad = 0

21

[ expresion() cantidad++ ( expresion() cantidad++ ) ]

return cantidad

Comunicacioacuten entre los analizadores leacutexico y sintaacutecticoCada vez que el analizador sintaacutectico realiza una llamada al analizador lexicograacutefico recibe como resultado de la llamada una representacioacuten de la pieza sintaacutectica encontrada en el texto analizado En los analizadores generados por JavaCC la comunicacioacuten de la pieza se efectuacutea mediante un valor de clase Token Esta cla-se estaacute definida en el fichero Tokenjava que se obtiene en todo caso siempre con el mismo contenido cualquiera que sea la especificacioacuten proporcionada como entrada al generador

Entre los campos de la clase Token se encuentran los siguientes

public int kind

nuacutemero entero que sirve de representacioacuten interna de la pieza sintaacutectica estos nuacutemeros asociados a las piezas son asignados automaacuteticamente por JavaCC los valores considerados pueden consultar-se en el fichero generado de nombre Constantsjava

public String image

cadena que contiene de la secuencia de caracteres (lexema) que constituyen la pieza sintaacutectica co-municada

public int beginLine beginColumn endLine endColumn

posiciones ocupadas (nuacutemeros de liacutenea y columna en el fichero de entrada) por el comienzo y el fi-nal del lexema de la pieza sintaacutectica comunicada

La clase Token tiene otros campos y meacutetodos en el fichero Tokenjava se tienen comentarios des-criptivos sobre todos los campos y meacutetodos que forman parte de la clase todos los campos de la clase se declaran como public

Ya se ha comentado anteriormente que cuando un meacutetodo de anaacutelisis sintaacutectico (asociado a un siacutembolo no terminal) tiene tipo el valor devuelto tras una llamada se puede almacenar en una variable por si se desea utilizar en alguna accioacuten sintaacutectica para ello se escribe

valor = nombreSimbolo()

(siendo valor el nombre de la variable y nombreSimbolo el nombre del siacutembolo no terminal) De manera anaacuteloga el objeto de la clase Token representativo de la pieza sintaacutectica encontrada en la entrada por el analizador lexicograacutefico puede asignarse a una variable del mismo tipo para tener accesibles las ca -racteriacutesticas de la pieza por si se precisaran en alguna accioacuten sintaacutectica para ello en la parte de la especifi -cacioacuten sintaacutectica donde aparezca la pieza sintaacutectica se escribe

dato = lt nombrePieza gt

donde nombrePieza es el nombre de la pieza sintaacutectica y dato es el nombre de una variable de tipo Token en la que se anota el valor indicativo de las propiedades de la pieza sintaacutectica comunicada

Tambieacuten existe otra manera de acceder a las caracteriacutesticas de la pieza sintaacutectica que el analizador lexico -graacutefico comunica al analizador sintaacutectico en la clase del analizador sintaacutectico se tiene declarado el campo

[ static ] public Token token

22

cada vez que el analizador sintaacutectico recibe una pieza sintaacutectica comunicada por el analizador lexicograacutefico se deja anotada en el campo token y ese contenido no se altera hasta que sea proporcionada la siguiente pieza de la entrada

∆ Ejemplo 4

Se considera una ampliacioacuten del ejemplo de presentacioacuten ademaacutes del anaacutelisis de la entrada se pretende obtener una relacioacuten numerada de las variables y las constantes indicando el nuacutemero de liacutenea y el nuacuteme-ro de columna donde estaacuten situadas por ejemplo si en la entrada se tiene la expresioacuten (grabada en dos liacuteneas)

x (y + 11) + 22

en la salida se tendraacute

1- x linea 1 columna 1

2- y linea 1 columna 6

3- 11 linea 1 columna 10

4- 22 linea 2 columna 5

En la especificacioacuten que se propone como solucioacuten puede apreciarse- en la clase del analizador sintaacutectico se han incluido meacutetodos auxiliares en los que se usan los nom-bres image beginLine beginColumn que son campos de un objeto de clase Token

- en la especificacioacuten sintaacutectica de factor se emplea la variable pieza de tipo Token para reco-ger las caracteriacutesticas de las piezas sintaacutecticas detectadas- la variable numero hace de contador para numerar las constantes y variables

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin

static int numero = 0

private static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

private static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

private static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

23

PARSER_END (ExprMin)

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() Token pieza pieza = ltconstantegt numero++ grabarDatosPieza(numero pieza) | pieza = ltvariablegt numero++ grabarDatosPieza(numero pieza) | ( expresion() )

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP | n | t | r

Si se agrupan las dos acciones sintaacutecticas comunes la especificacioacuten de factor tambieacuten puede escri-birse de esta forma

void factor() Token pieza ( pieza = ltconstantegt | pieza = ltvariablegt

) numero++ grabarDatosPieza(numero pieza)

| ( expresion() )

24

Si se accede al campo token de la clase del analizador sintaacutectico se puede resolver este ejemplo pres-cindiendo de la variable pieza

void factor() ( ltconstantegt | ltvariablegt

) numero++ grabarDatosPieza(numero token)

| ( expresion() )

Tareas asociadas a las piezas sintaacutecticasYa se ha visto que es posible asociar bloques de coacutedigo Java a puntos de la estructura sintaacutectica para que se realicen ciertas operaciones cuando el analizador sintaacutectico pase por esa zona de la estructura Tambieacuten es posible asociar coacutedigo Java a las piezas sintaacutecticas para que el analizador lexicograacutefico efectuacutee determina-das tareas cuando detecte en la entrada analizada piezas que tengan asociado coacutedigo

loz Accioacuten ligada a una pieza sintaacutecticaSi se asocia un bloque de coacutedigo a una pieza sintaacutectica se consigue que cada vez que se detecta en la en-trada analizada la presencia de esa pieza se ejecute en ese preciso momento el coacutedigo del bloque asociado a la pieza Estas acciones se denominan ldquoacciones lexicograacuteficasrdquo (lexical actions) el coacutedigo del bloque se traslada literalmente al coacutedigo del analizador lexicograacutefico generado para que se ejecute oportunamente cuando se detecte la pieza ligada al bloque de coacutedigo La accioacuten lexicograacutefica se ejecuta inmediatamente antes que la pieza sea comunicada al analizador sintaacutectico

Se puede asociar una accioacuten lexicograacutefica a una pieza sintaacutectica declarada con TOKEN y tambieacuten a una se-cuencia de caracteres declarada con SKIP ademaacutes de eacutestas hay otras dos formas de declaraciones lexico-graacuteficas (no mencionadas en esta introduccioacuten) que tambieacuten pueden llevar una accioacuten asociada

En una especificacioacuten JavaCC la forma de una pieza sintaacutectica (o bien de una secuencia de caracteres) se define poniendo su descripcioacuten delimitada entre llaves detraacutes de la indicacioacuten TOKEN (o bien SKIP) si se quiere asociar una accioacuten lexicograacutefica a la pieza (o a la secuencia) hay que colocar el bloque de coacutedigo de la accioacuten a continuacioacuten de la descripcioacuten de la forma de la pieza (o de la secuencia) el bloque (delimi-tado por llaves) queda situado entre la descripcioacuten y la llave que cierra la especificacioacuten

La pieza sintaacutectica especial que representa el final del fichero (EOF) tambieacuten puede llevar asociada una ac-cioacuten lexicograacutefica para ello hay que poner

ltgt TOKEN lt EOF gt coacutedigo de la accioacuten asociada

para justificar la presencia de los caracteres ltgt delante de la palabra TOKEN hay que basarse en el con-cepto de ldquoestado lexicograacuteficordquo (cuya explicacioacuten queda fuera del alcance de esta introduccioacuten)

∆ Ejemplo 5

Se considera nuevamente el ejemplo de presentacioacuten como resultado del anaacutelisis de una expresioacuten se pretende obtener una relacioacuten de los pareacutentesis contenidos en la expresioacuten en el mismo orden en que se encuentran ademaacutes por cada salto de liacutenea del texto de entrada se grabaraacute en la salida el caraacutecter y

25

se pasaraacute a una nueva liacutenea tambieacuten se anotaraacute la frase Final del trabajo cuando se alcance el final del fichero de entrada Asiacute por ejemplo si la entrada analizada es

(a + b) (( x )) (y + 7)

los resultados obtenidos seraacuten

() (())()

Final del trabajo

En la especificacioacuten JavaCC que se propone como solucioacuten soacutelo se muestran las reglas sintaacutecticas y le -xicograacuteficas (en el resto no hay cambios) en esta solucioacuten puede apreciarse- las piezas sintaacutecticas correspondientes a los pareacutentesis se han definido de manera nominal para po-der asociarles una accioacuten lexicograacutefica- los caracteres incluidos en la pieza de tipo SKIP se han descompuesto en dos grupos ya que la ac-cioacuten lexicograacutefica incorporada soacutelo afecta a uno de ellos

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ltpAbrirgt expresion() ltpCerrargt

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

TOKEN

26

lt pAbrir ( gt Systemoutprint(()

TOKEN lt pCerrar ) gt Systemoutprint())

ltgtTOKEN lt EOF gt Systemoutprintln ( Final del trabajo )

SKIP n Systemoutprint( n)

SKIP | t | r

loz Bloque de declaraciones lexicograacuteficasCada definicioacuten de una pieza sintaacutectica puede llevar asociada una accioacuten lexicograacutefica que se ejecuta in-mediatamente despueacutes de detectar en la entrada un lexema que se ajusta a la definicioacuten de la pieza cada accioacuten lexicograacutefica es un bloque de coacutedigo Java que puede contener sus propias declaraciones Pero a ve-ces interesa tener declaraciones que sean compartidas por todas las acciones lexicograacuteficas con indepen-dencia de la pieza a la que esteacute asociada cada una de ellas

Para incluir declaraciones que puedan usarse desde el coacutedigo asociado a cualquiera de las piezas sintaacutecti -cas se dispone de una construccioacuten con el siguiente formato

TOKEN_MGR_DECLS

que consta de una palabra reservada seguida del siacutembolo dos puntos seguido de un bloque de coacutedigo Java (delimitado por llaves) esta declaracioacuten se puede colocar en cualquier parte de una especificacioacuten JavaCC si se incluye soacutelo puede ponerse una vez

Todas las declaraciones de variables o de meacutetodos puestas en el bloque de declaraciones lexicograacuteficas compartidas son accesibles desde el coacutedigo de cualquier accioacuten lexicograacutefica la justificacioacuten es sencilla el bloque de declaraciones se traslada al principio del coacutedigo del analizador lexicograacutefico generado y las ac-ciones lexicograacuteficas tambieacuten se trasladan a ese analizador

loz Declaraciones lexicograacuteficas predefinidasDe lo expuesto hasta ahora se aprecia que en una accioacuten lexicograacutefica asociada a una pieza sintaacutectica re-sultan accesibles

- las declaraciones de su propio bloque (si tiene declaraciones)- los componentes del bloque de declaraciones compartidas (si estaacute incluido en la especificacioacuten)

Ademaacutes de esto se tienen otras declaraciones pertenecientes a la clase TokenManager que constituye el analizador lexicograacutefico que tambieacuten son utilizables desde cualquiera de las acciones lexico-graacuteficas estas declaraciones pueden considerarse como ldquopredefinidasrdquo ya que son incorporadas automaacuteti-camente por el generador Entre estas declaraciones accesibles se encuentran las siguientes

[ static ] StringBuffer imagecontiene la cadena de caracteres representativa de la pieza sintaacutectica actual que se acaba de recono-cer en le entrada se trata de un campo de la clase y se declara como static cuando el analizador

27

se ha generado con la opcioacuten (por defecto) Static = true si se generase con la opcioacuten Sta-tic = false no quedariacutea declarada como static

Token matchedToken

variable de clase Token que representa la pieza sintaacutectica actual la que se acaba de encontrar en la entrada se trata de una variable local del meacutetodo al que se ha trasladado el coacutedigo de la accioacuten (y por ello accesible desde ese coacutedigo)

El campo image puede usarse tanto si la accioacuten lexicograacutefica estaacute asociada a una pieza declarada con TOKEN como si lo estaacute a una secuencia declarada con SKIP Sin embargo la variable matchedToken no puede usarse en una accioacuten asociada a un patroacuten especificado como SKIP el motivo es que lo declara-do como SKIP no constituye propiamente una pieza sintaacutectica ya que no se va a comunicar al analizador sintaacutectico

∆ Ejemplo 6

Se propone aquiacute una nueva solucioacuten para el ejemplo nordm 4 expuesto anteriormente

Esta nueva solucioacuten tal y como se expone aquiacute admite algunas mejoras ya que su pretensioacuten soacutelo es ser-vir de ilustracioacuten para algunos aspectos que se acaban de citar (maacutes adelante se propone otra solucioacuten al-ternativa)

En la especificacioacuten de la solucioacuten es posible apreciar esto- las acciones lexicograacuteficas pueden llevar sus propias declaraciones las variables nLineaVar nColumnaVar nLineaCte y nColumnaCte se declaran dentro de las acciones

- la variable matchedToken a la que se hace referencia en el coacutedigo de la accioacuten lexicograacutefica aso-ciada a una variable contiene la informacioacuten sobre la pieza sintaacutectica que se acaba de detectar una va-riable de la expresioacuten analizada anaacutelogamente con la referencia relativa a una constante- el nombre image que aparece en las acciones lexicograacuteficas se refiere al campo de la clase Expr-MinTokenManager (analizador lexicograacutefico) noacutetese que en efecto se comunica como un paraacuteme-tro de clase StringBuffer

- la variable numero se emplea para numerar las constantes y variables encontradas

- los nombres beginLine y beginColumn son campos (puacuteblicos) de la clase Token por ello se pueden aplicar a un objeto de esa clase

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

void unaExpresion()

28

expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n StringBuffer lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

TOKEN lt variable [a-z] gt

int nLineaVar nColumnaVar numero++ nLineaVar = matchedTokenbeginLine nColumnaVar = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaVar nColumnaVar)

TOKEN lt constante ( [0-9] ) + gt

int nLineaCte nColumnaCte

29

numero++ nLineaCte = matchedTokenbeginLine nColumnaCte = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaCte nColumnaCte)

SKIP | n | t | r

La especificacioacuten precedente se puede escribir de otra manera maacutes escueta como se indica a continua-cioacuten (soacutelo se muestran las modificaciones) sobre esta nueva especificacioacuten puede mencionarse- se han eliminado por innecesarias las variables declaradas dentro de las acciones lexicograacuteficas- el nombre image que aparece en el bloque de las declaraciones compartidas se refiere al campo de la clase Token noacutetese que en efecto se aplica a un objeto de esa clase y se comunica como un paraacute -metro de tipo String

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

TOKEN lt variable [a-z] gt

numero++ grabarDatosPieza(numero matchedToken)

TOKEN lt constante ( [0-9] ) + gt

numero++ grabarDatosPieza(numero matchedToken)

loz Accioacuten lexicograacutefica comuacutenSi hubiera que realizar la misma tarea tras la deteccioacuten de todas y cada una de las piezas sintaacutecticas una posibilidad incoacutemoda seriacutea repetir el mismo coacutedigo en todas las acciones lexicograacuteficas para evitar tanta reiteracioacuten se tiene la opcioacuten Common_Token_Action su valor por defecto es false pero si se le asigna el valor true es posible definir una ldquoaccioacuten lexicograacutefica comuacutenrdquo que se escribe una uacutenica vez y se aplica a todas las piezas sintaacutecticas

30

Cuando la opcioacuten Common_Token_Action se establece con el valor true es obligado incluir el blo-que de declaraciones lexicograacuteficas compartidas (TOKEN_MGR_DECLS) Ademaacutes en ese bloque se preci-sa la definicioacuten de un meacutetodo cuyo encabezamiento sea

[ static ] void CommonTokenAction(Token t)

el coacutedigo de este meacutetodo constituye la accioacuten lexicograacutefica comuacuten que se ejecutaraacute despueacutes de la deteccioacuten de cualquier pieza sintaacutectica el meacutetodo tiene un paraacutemetro de tipo Token mediante el que se puede co-municar la informacioacuten correspondiente a la pieza que se acaba de encontrar en la entrada analizada

Si una pieza sintaacutectica tiene asociada una accioacuten lexicograacutefica propia la accioacuten lexicograacutefica comuacuten (si es-taacute definida) se ejecutaraacute inmediatamente despueacutes de realizar la accioacuten propia

La accioacuten lexicograacutefica comuacuten se aplica a todas las piezas sintaacutecticas definidas como TOKEN bien sea no-minalmente (en las reglas lexicograacuteficas) o bien anoacutenimamente (incorporadas dentro de las reglas sintaacutecti-cas) sin embargo no se aplica cuando la secuencia acoplada se corresponde con una expresioacuten regular es-pecificada como SKIP (noacutetese que por el contrario una especificacioacuten SKIP siacute puede tener asociada una accioacuten lexicograacutefica propia)

El coacutedigo del meacutetodo CommonTokenAction se traslada como un componente maacutes del bloque de decla-raciones compartidas (TOKEN_MGR_DECLS) al principio de la clase del analizador lexicograacutefico

∆ Ejemplo 7

Se pretende ahora ampliar el ejemplo de presentacioacuten con la obtencioacuten de una relacioacuten numerada de los lexemas de las piezas sintaacutecticas encontradas en la entrada analizada en el caso de las variables y de las constantes antes del lexema se mostraraacute un indicativo (Var Const) por ejemplo si la expresioacuten de la entrada es

( a + 987 ) b

se grabaraacute la salida

( 1Var a 2+ 3Const 987 4) 5 6Var b 7 8

En la solucioacuten que se propone a continuacioacuten se muestran la definicioacuten de las opciones y las reglas sin-taacutecticas y lexicograacuteficas (en el resto no hay modificaciones) puede resaltarse lo siguiente- las llamadas al meacutetodo CommonTokenAction se incorporan automaacuteticamente no hay que incluir-las en la especificacioacuten como paraacutemetro de llamada a este meacutetodo siempre se emplea el objeto que re -presenta la pieza que se acaba de detectar- la accioacuten lexicograacutefica comuacuten realiza la actualizacioacuten del contador de piezas y la grabacioacuten del lexe-ma de la pieza detectada con su nuacutemero de orden- la accioacuten lexicograacutefica comuacuten tambieacuten se aplica a las piezas sintaacutecticas anoacutenimas (pareacutentesis y ope-radores) y a la pieza especial que representa el final del fichero (EOF)

- las piezas sintaacutecticas ltconstantegt y ltvariablegt tienen asociada una accioacuten lexicograacutefica propia que se ejecuta antes de la accioacuten lexicograacutefica comuacuten- los caracteres que se saltan (SKIP) no estaacuten afectados por la accioacuten lexicograacutefica comuacuten

options Ignore_Case = true Common_Token_Action = true

31

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarIndicativo(String indicativo) Systemoutprint(indicativo + )

static void grabarLexema (int num String lexema) Systemoutprint(lexema + + num + n)

static void CommonTokenAction(Token pieza) numero++ grabarLexema(numero piezaimage)

TOKEN lt variable [a-z] gt

grabarIndicativo(Var)

32

TOKEN lt constante ( [0-9] ) + gt

grabarIndicativo(Const)

SKIP | n | t | r

A modo de ejemplo de aplicacioacuten de la accioacuten lexicograacutefica comuacuten en lo que sigue se ofrece una solucioacuten para la grabacioacuten (en el fichero predefinido de salida) del texto analizado con las liacuteneas numeradas

∆ Ejemplo 8 [ Listado del texto analizado ]Se desea completar el ejemplo de presentacioacuten con la grabacioacuten de un listado con las liacuteneas numeradas del texto analizado (noacutetese que una expresioacuten puede escribirse ocupando varias liacuteneas) No se muestra la especificacioacuten sintaacutectica debido a que no hay ninguna modificacioacuten en ella Los aspectos resentildeables de la solucioacuten que se propone son- se usa la variable numLin como contador de liacuteneas

- en la variable linea se van yuxtaponiendo los lexemas de las piezas sintaacutecticas de una liacutenea esta yuxtaposicioacuten se realiza mediante la accioacuten lexicograacutefica comuacuten- las secuencias reconocidas con los patrones especificados como SKIP no implican la ejecucioacuten de la accioacuten lexicograacutefica comuacuten por ello su lexema (almacenado en la variable image) ha de yuxtaponer-se expliacutecitamente- hay que separar el patroacuten correspondiente al fin de liacutenea para detectar el momento en que ha de gra-barse la liacutenea completa cuyo final se ha alcanzado

options Ignore_Case = true Common_Token_Action = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(nnAnalisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

TOKEN_MGR_DECLS static int numLin = 0 static String linea =

33

static void CommonTokenAction(Token pieza) linea = linea + piezaimage

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP lt | t | r gt linea = linea + image

SKIP n numLin++ Systemoutprintln(numLin + + linea) linea =

A modo de recapitulacioacutenↄ Accioacuten lexicograacutefica Accioacuten sintaacutectica

bull accioacuten lexicograacutefica bloque de coacutedigo Java asociado a una pieza sintaacutectica nominal (TOKEN) o a una secuencia de caracteres (SKIP) se ejecuta cuando se detecta en la entrada la pieza o la secuencia corres-pondiente no se puede aplicar a piezas sintaacutecticas anoacutenimas (autodefinidas con comillas)bull accioacuten sintaacutectica bloque de coacutedigo Java asociado a un punto de la estructura sintaacutectica (el punto indica-do por el sitio de la produccioacuten donde se inserta el bloque) se ejecuta cuando el anaacutelisis de la entrada pa-sa por ese punto de la estructura

ↄ Valor de una pieza sintaacutectica comunicadaEl analizador lexicograacutefico comunica al analizador sintaacutectico una pieza a traveacutes de un valor de la clase Token (fichero Tokenjava) un objeto de la clase Token representa las caracteriacutesticas de la pieza comunicada En la clase Token entre otros se tienen los campos

String imagelexema de la pieza

int beginLine beginColumn endLine endColumnposicioacuten del lexema en el fichero analizado (indicada por los nuacutemeros de fila y de columna de su comienzo y de su terminacioacuten)

ↄ Valor asociado a un siacutembolo

34

En las producciones que especifican la sintaxis se pueden incluir asignaciones que sirven para dejar ano-tado el valor asociado a un siacutembolo

bull siacutembolo no terminal valor = nombreSimbolo()el meacutetodo de anaacutelisis asociado a nombreSimbolo se ha declarado de un cierto tipo (distinto del tipo void) valor ha de ser una variable del mismo tipo que el meacutetodo

bull siacutembolo terminal dato = lt nombrePieza gtel valor asignado es el valor de la clase Token representativo de la pieza sintaacutectica (nominal) dato ha de ser una variable de tipo Token

ↄ Miradas sobre la pieza sintaacutectica comunicadaLa pieza sintaacutectica comunicada puede mirarse desde cada uno de los dos analizadores

bull analizador sintaacutecticola pieza comunicada (desde el analizador lexicograacutefico) se tiene anotada en un campo de la clase del analizador sintaacutectico declarado como

(static) public Token token

este valor es accesible desde el coacutedigo de todos los meacutetodos de anaacutelisis sintaacutectico el contenido del campo variacutea cuando se recibe una nueva pieza sintaacutectica

bull analizador lexicograacuteficola pieza dispuesta para ser comunicada (al analizador sintaacutectico) se tiene anotada en una variable local declarada como

Token matchedToken

este valor es accesible para el coacutedigo de todas las acciones lexicograacuteficas

ↄ El lexema de la pieza comunicadaEl lexema de la pieza sintaacutectica comunicada se encuentra disponible en dos sitios distintos que compar-ten el mismo nombre pero que son de distinto tipo y se usan de distinta manera

bull String image

es un campo de la clase TOKEN puede consultarse en relacioacuten con un objeto de esa clase que esteacute disponible

bull StringBuffer image

es un campo de la clase del analizador lexicograacutefico ( ∙ ∙ ∙ ∙ TokenManagerjava) es accesible desde el coacutedigo de la clase y por lo tanto desde todas las acciones lexicograacuteficas (bloques de coacute-digo que estaacuten incorporados al analizador lexicograacutefico)

se trata de un valor disponible tanto para las piezas sintaacutecticas nominales (TOKEN) como para las secuencias que se saltan (SKIP)

ↄ Resumen relativo a la lexicografiacutea

TOKEN pieza sintaacutectica nominal∙ ∙ ∙ pieza sintaacutectica anoacutenimaSKIP secuencia de caracteres que no forman pieza

35

Precisiones sobre el analizador sintaacutectico generadoPuede decirse que en su funcionamiento maacutes sencillo cuando no se emplean las posibilidades de examen por adelantado (ldquolookaheadrdquo) JavaCC comprueba que las reglas sintaacutecticas cumplen la condicioacuten LL(1) y generan un analizador descendente-predictivo-recursivo Pero esta apreciacioacuten hay que matizarla ya que el analizador sintaacutectico generado no se ajusta en sentido estricto al modelo de analizador LL(1)

En el modelo de anaacutelisis LL(1) el orden del examen de las distintas alternativas de expansioacuten de un siacutembo-lo no terminal es indiferente ya que se conocen los siacutembolos directores de cada una de ellas (que resultan ser conjuntos disjuntos) en la implementacioacuten del subprograma de anaacutelisis asociado al siacutembolo no terminal ltNombreSimbgt cuyas producciones son

ltNombreSimbgt = α1

| α2

∙ ∙ ∙

| αn

se podriacutea preguntar en cualquier orden si la pieza actual pertenece al conjunto de siacutembolos directores de las distintas reglas Sin embargo en el analizador sintaacutectico generado por JavaCC la implementacioacuten del meacute-todo asociado al siacutembolo no terminal ltNombreSimbgt no sigue esa pauta

Si las anteriores reglas sintaacutecticas se transcriben en JavaCC de la forma

void nombreSimb () α1-jcc

| α2-jcc

∙ ∙ ∙

| αn-jcc

1

accioacutenlexicograacutefica

comuacuten

Commom -TokenAction2

accioacutenlexicograacutefica

propia

(accioacuten ligadaa una pieza)3

bloque dedeclaracioneslexicograacuteficas

TOKEN_MGR_DECLS4

declaracioneslexicograacuteficaspredefinidas

(disponibilidad

de uso)TOKENsisisisi∙ ∙ ∙sino--SKIPnosisisi

Las indicaciones si de las columnas 3 y 4 son consecuencia de las indicaciones de la columna 2

La accioacuten lexicograacutefica comuacuten se ejecuta despueacutes de la accioacuten lexicograacutefica propia

36

el meacutetodo de anaacutelisis generado procede de la siguiente manera

se van considerando las alternativas sucesivamente en el mismo orden en el que estaacuten especifica-das primero la de α1 (α1-jcc) despueacutes la de α2 (α2-jcc) etc

para cada alternativa seleccionada se comprueba si la pieza por adelantado coincide con alguno de los siacutembolos iniciales de la parte derecha y si hay coincidencia se prosigue el anaacutelisis con ella si no hay coincidencia se pasa a la alternativa siguiente

siguiendo con este orden si se llega al final de las alternativas sin haberse encontrado coincidencia alguna se produce un error sintaacutectico

En el analizador generado por JavaCC hay una peculiaridad que no se tiene en los analizadores LL(1) es-trictamente considerados la palabra vaciacutea siacute se considera como posible siacutembolo inicial de la parte derecha una regla (esto ocurre siempre que la parte derecha es anulable) De esta peculiaridad se deriva una conse-cuencia que ha de tenerse en cuenta al escribir la especificacioacuten dado que siempre es posible considerar la palabra vaciacutea como la siguiente pieza por adelantado si se llega a examinar la alternativa correspondiente a una produccioacuten anulable el analizador generado siempre seleccionaraacute esa alternativa con independencia de la siguiente pieza que esteacute presente en la entrada que queda por analizar

Asiacute pues si alguna de las producciones que definen un siacutembolo no terminal es anulable es importante el or-den en que se escriben las alternativas en una especificacioacuten sintaacutectica JavaCC el funcionamiento del ana-lizador generado siacute depende de la colocacioacuten elegida para las reglas

Para ilustrar estas peculiaridades del analizador sintaacutectico generado por JavaCC a continuacioacuten se expone un ejemplo

∆ Ejemplo 9

Se quiere obtener un analizador leacutexico-sintaacutectico de textos que se ajustan al siguiente formato una se-cuencia de uno o maacutes nombres seguida del siacutembolo igual seguido de un nuacutemero entero si la secuencia tiene maacutes de un nombre han de estar separados bien por una coma en todos los casos o bien por el siacutem-bolo dos puntos en todos los casos esto es son correctas las secuencias

uno dos tres = 123exclusivo = 0Abajo Arriba = 57

y son incorrectas las secuenciasuno dos tres = 123

exclusivo inclusive = 0

fuera dentro = 56

Una gramaacutetica que define las secuencias con este formato es

ltListagt = nombre ltOtrosNombresgt = numero

ltOtrosNombresgt = nombre nombre | nombre nombre | ε

se trata de una gramaacutetica que cumple la condicioacuten LL(1)- el siacutembolo director para la alternativa vaciacutea es el siacutembolo igual es evidente pues que las tres reglas de ltOtrosNombresgt tienen siacutembolos directores distintos

37

- la decisioacuten de abandonar la repeticioacuten del anaacutelisis de un nuevo nombre (en las dos primeras alterna-tivas de ltOtrosNombresgt) se puede tomar ante la presencia del siacutembolo igual

En la implementacioacuten de un analizador sintaacutectico LL(1) en sentido estricto la seleccioacuten de la alternativa vaciacutea para expandir el siacutembolo ltOtrosNombresgt se hariacutea tras comprobar expliacutecitamente que la pie-za sintaacutectica por adelantado es el siacutembolo igual

El funcionamiento del analizador generado por JavaCC depende del orden de colocacioacuten de las reglas A continuacioacuten se exponen varias posibilidades de especificacioacuten y se comenta el analizador generado en cada caso

bull Especificacioacuten primeraSe transcriben literalmente las reglas tal y como se han escrito antes en la notacioacuten BNF noacutetese que para poder codificar la parte derecha de la regla vaciacutea ha de incluirse una accioacuten sintaacutectica vaciacutea

PARSER_BEGIN (Igualdad)

public class Igualdad

public static void main (String[] argum) throws ParseException Igualdad analisis = new Igualdad(Systemin) analisissecuencia() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (Igualdad)

void secuencia() lista() ltEOFgt

void lista() lt nombre gt otrosNombres() = lt numero gt

void otrosNombres() lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt ) |

TOKEN lt nombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN lt numero ( [ 0 - 9 ] )+ gt

SKIP | r | n

38

El analizador generado funciona correctamente puesto que en el meacutetodo otrosNombres se selec-ciona la alternativa vaciacutea despueacutes de haber comprobado que la pieza por adelantado no es siacutembolo ini-cial de ninguna de las dos alternativas precedentes (no es una coma ni un siacutembolo dos puntos)

bull Especificacioacuten segundaSe considera la misma especificacioacuten anterior pero cambiando el orden de colocacioacuten de las reglas ahora la produccioacuten vaciacutea se coloca como primera alternativa (soacutelo se muestran las partes modifica-das)

void otrosNombres() | lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt )

Con la entrada de esta especificacioacuten el generador JavaCC produce un mensaje en el que se avisa de que las dos alternativas que estaacuten colocadas detraacutes de la alternativa vaciacutea nunca podraacuten seleccionarse aun asiacute se genera un analizador Pero es un analizador que funciona incorrectamente asiacute por ejemplo la entrada

mxa nyz = 57

se considera incorrecta despueacutes de visto el primer nombre se intenta aplicar la produccioacuten vaciacutea (se espera encontrar el siacutembolo igual) pero habriacutea que aplicar la regla cuyo siacutembolo director es la coma

bull Especificacioacuten terceraSe utilizan las posibilidades de la notacioacuten BNF-Ampliada para prescindir de la especificacioacuten expliacuteci-ta de la palabra vaciacutea se aprovecha que JavaCC admite el operador + para indicar repeticiones de una o maacutes veces la alternativa vaciacutea estaacute impliacutecitamente contemplada en la posibilidad de cero repeti-ciones que contempla el operador (soacutelo se muestran las partes modificadas en la especificacioacuten)

void otrosNombres() ( lt nombre gt )+ | ( lt nombre gt )

El meacutetodo otrosNombres() generado hace las comprobaciones en el siguiente orden

- se comprueba si la pieza por adelantado es una coma (siacutembolo inicial de la primera alternativa) y en este caso se selecciona la primera regla- si no es una coma se comprueba si es un siacutembolo dos puntos (siacutembolo inicial de la segunda alter-nativa) y en este caso se selecciona la segunda regla- si no es un siacutembolo dos puntos se considera la palabra vaciacutea como siacutembolo inicial (siempre posi-ble impliacutecitamente) para la segunda alternativa en este caso para que no haya error la pieza por adelantado ha de ser un siacutembolo igual

Se trata pues de un analizador que funciona correctamente

bull Especificacioacuten cuarta

39

Se pone la misma especificacioacuten anterior pero cambiando el orden de consideracioacuten de la regla vaciacutea ahora se incluye impliacutecitamente en la primera alternativa (soacutelo se muestran las partes modificadas)

void otrosNombres() ( lt nombre gt ) | ( lt nombre gt )+

Para esta especificacioacuten el generador JavaCC emite un aviso en el que se indica que la segunda alter -nativa nunca se seleccionaraacute no obstante se genera un analizador que no funciona como se supone que deberiacutea hacerlo por ejemplo el anaacutelisis de la entrada

nyz mxa = 1

produce un error despueacutes de tratado el primer nombre se intenta aplicar la produccioacuten vaciacutea (corres-pondiente a la repeticioacuten de cero veces contemplada en la primera regla) por lo que no se encuentra el esperado siacutembolo igual

40

Page 3: JavaCC

Principios baacutesicos del generador JavaCC

loz Descripcioacuten inicialEl generador JavaCC (Java Compiler Compiler) es una herramienta para generar analizadores de lengua-jes acepta como entrada una especificacioacuten de un determinado lenguaje y produce como salida un analiza-dor para ese lenguaje el analizador generado estaacute escrito en Java La especificacioacuten proporcionada al gene-rador JavaCC puede contemplar distintos aspectos del lenguaje para el que se quiere obtener el analizador

- Caracteriacutesticas lexicograacuteficas y sintaacutecticases la forma maacutes frecuente de uso del generador la especificacioacuten proporcionada define las caracteriacutesti -cas sintaacutecticas y lexicograacuteficas de un lenguaje y se genera un analizador leacutexico-sintaacutectico del lenguaje especificado

- Caracteriacutesticas lexicograacuteficasen la especificacioacuten proporcionada al generador soacutelo se definen caracteriacutesticas lexicograacuteficas del lengua-je con el coacutedigo generado se puede obtener un analizador lexicograacutefico

- Caracteriacutesticas lexicograacuteficas y sintaacutecticas y comprobaciones semaacutenticastambieacuten es posible completar una especificacioacuten leacutexico-sintaacutectica con la inclusioacuten de coacutedigo Java com-plementario para que el programa generado (que incorpora adecuadamente ese coacutedigo auxiliar) pueda hacer un anaacutelisis completo (leacutexico sintaacutectico y semaacutentico) del lenguaje especificado

loz Instalacioacuten y documentacioacutenDado que el coacutedigo generado por JavaCC estaacute escrito en Java es necesario disponer de una versioacuten del sis-tema Java (compilador de Java e inteacuterprete de la Maacutequina Virtual Java) Son programas de libre distribu-cioacuten y faacuteciles de conseguir

El generador JavaCC tambieacuten es un programa de libre distribucioacuten se puede conseguir en

la paacutegina oficial de JavaCC httpsjavaccdevjavanet

la paacutegina de la asignatura

(se tiene la versioacuten Java Compiler Compiler version 50)

Se consigue un fichero empaquetado de nombre javacc-50zip tras desempaquetar (en un determi-nado directorio que puede elegirse como convenga) dicho fichero se tienen instalados entre otros los si-guientes ficheros (que son los que interesan para el sistema operativo windows)

middotmiddotmiddotmiddot javacc-50binjavaccbatmiddotmiddotmiddotmiddot javacc-50binjjdocbatmiddotmiddotmiddotmiddot javacc-50binjjtreebat

los nombre de estos ficheros se corresponden con los nombres de los comandos para llamar a las herra-mientas instaladas

javacc generador de analizadores

jjdoc productor de documentacioacuten

jjtree preprocesador de apoyo para tareas semaacutenticas

Para que la llamada a estos comandos pueda realizarse desde cualquier directorio ha de anotarse el camino (PATH) que lleve hasta middotmiddotmiddotmiddot javacc-50bin Tambieacuten ha de tenerse en cuenta si la instalacioacuten del sistema Java se tiene preparada para que el compilador (javac) y el inteacuterprete (java) se puedan ejecutar desde cualquier directorio

Para comprobar si la instalacioacuten del generador se ha realizado adecuadamente se puede llamar desde la liacute-nea de comandos al generador javacc y apareceraacute por pantalla una informacioacuten sobre el uso de dicho comando la primera liacutenea de esa informacioacuten es

Java Compiler Compiler Version 50 (Parser Generator)

2

Documentacioacuten sobre JavaCC

Se dispone de abundante documentacioacuten relativa a JavaCC entre otras se pueden encontrar

documentacioacuten que acompantildea a los ficheros de la versioacuten instalada se tienen diversos ficheros enmiddotmiddotmiddotmiddot javacc-50dochtml

(en javaccgrmhtml se tiene una descripcioacuten general del generador)

documentacioacuten variada en la red

JavaCC DocumentationThe JavaCC TutorialIntroduction to JavaCCThe JavaCC FAQetc

Primera prueba con JavaCC

En lo que sigue se expone un ldquoejemplo de presentacioacutenrdquo completo la generacioacuten de un analizador leacutexico-sintaacutectico para un tipo de expresiones muy sencillo Se puede empezar el estudio de JavaCC probando su funcionamiento con ese ejemplo

loz Obtencioacuten de un analizador leacutexico-sintaacutectico

bull Pasos para la generacioacuten del analizador

1- Edicioacuten de la especificacioacuten (editor de texto plano)

vi | edit |∙ ∙ ∙ NombreFicherojj

(el nombre del fichero puede tener cualquier extensioacuten suele usarse jj)

2- Ejecucioacuten del generador

javacc NombreFicherojj

Si el nombre elegido para la especificacioacuten es NombreDeLaEspecif (maacutes adelante se indica la manera de dar un nombre a la especificacioacuten) como resultado de la generacioacuten se obtiene (ademaacutes de otros ficheros auxiliares) el fichero

NombreDeLaEspecifjava

3- Compilacioacuten del analizador generado

javac NombreDeLaEspecifjava

Como resultado de la compilacioacuten se obtiene (ademaacutes de otras clases auxiliares) el ficheroNombreDeLaEspecifclass

bull Ejecucioacuten del analizador generadoSi el nombre del fichero donde se encuentra el texto fuente (escrito en el lenguaje para el que se ha generado el analizador) que se pretende analizar es Programalen

java NombreDeLaEspecif lt Programalen

Si se desea que los resultados del anaacutelisis en vez de presentarse por pantalla queden grabados en un fi -chero de nombre Salidadat

java NombreDeLaEspecif lt Programalen gt Salidadat

3

loz Ejemplo de presentacioacuten

bull Descripcioacuten del lenguaje

El lenguaje L estaacute formado por las expresiones en las que pueden aparecer- variables- constantes- operadores + y

Las variables son nombres formados por una uacutenica letra (minuacutescula o mayuacutescula) las constantes son nuacute-meros enteros de una o maacutes cifras El espacio y el tabulador pueden estar presentes pero no tienen ninguacuten significado los finales de liacutenea tampoco son significativos (una expresioacuten puede codificarse ocupando una o maacutes liacuteneas)

La sintaxis de las expresiones se especifica mediante la siguiente gramaacutetica

ltExpresiongt = ltTerminogt + ltTerminogt ltTerminogt = ltFactorgt ltFactorgt ltFactorgt = variable | constante | ( ltExpresiongt )

bull Especificacioacuten leacutexico-sintaacutectica codificada con la notacioacuten JavaCCUna manera de escribir la especificacioacuten (para la que se ha elegido el nombre ExprMin) de forma que sea aceptada por el generador es

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Anaacutelisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

4

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP | t | n | r

bull Obtencioacuten del analizadorSi la especificacioacuten precedente se tiene grabada en un fichero de nombre Ejemplojj para obtener el analizador

- se ejecuta el generador javacc Ejemplojj

- se compila el analizador generado javac ExprMinjava

bull Ejecucioacuten del analizadorSi se quiere analizar una expresioacuten grabada en un fichero de nombre PruebaExptxt

- se ejecuta el analizador obtenido java ExprMin lt PruebaExptxt

loz Analizadores generadosEn su funcionamiento maacutes sencillo y habitual JavaCC genera un analizador sintaacutectico complementado con un analizador lexicograacutefico para que conjuntamente se pueda realizar un anaacutelisis leacutexico-sintaacutectico de un texto de entrada

El analizador sintaacutectico obtenido es en general LL(k) descendente y determinista con la consulta de k siacutembolos por adelantado si la gramaacutetica proporcionada cumple la condicioacuten LL(1) se genera un analiza -dor sintaacutectico descendente-predictivo-recursivo Maacutes adelante se hacen algunas precisiones sobre esta afir-macioacuten

Si la especificacioacuten leacutexico-sintaacutectica de un lenguaje codificada en JavaCC tiene dado (como indicativo que acompantildea a las palabras reservadas PARSER_BEGIN y PARSER_END) el nombre EspLexSin y se tiene grabada en un fichero de nombre Lenguajejj cuando se ejecuta el generador tomando como entrada ese fichero

javacc Lenguajejj

se obtienen los siguientes ficheros (clases) con coacutedigo Java

Tokenjavadescripciones para la comunicacioacuten entre los analizadores leacutexico y sintaacutectico

5

TokenMgrErrorjavatratamiento de errores para el anaacutelisis lexicograacutefico

ParseExceptionjavatratamiento de errores para el anaacutelisis sintaacutectico

SimpleCharStreamjavacomponentes para la realizacioacuten de las tareas de entradasalida del analizador

EspLexSinConstantsjavadefinicioacuten de la representacioacuten interna de las piezas sintaacutecticas

EspLexSinTokenManagerjavaanalizador lexicograacutefico

EspLexSinjavaanalizador sintaacutectico

Puede apreciarse que hay dos categoriacuteas de nombres de ficheros generados los cuatro primeros nombres citados no dependen del nombre de la especificacioacuten considerada los otros nombres de ficheros se forman a partir del nombre dado a la especificacioacuten

Forma de una especificacioacuten JavaCC (versioacuten simplificada)La forma que se describe en lo que sigue es una versioacuten simplificada el generador JavaCC admite especificaciones con otras muchas posi-bilidades no mencionadas en esta introduccioacuten

Una especificacioacuten para el generador JavaCC puede considerarse dividida en cuatro secciones

loz Seccioacuten de opcionesEn esta seccioacuten cuya presencia es optativa se pueden asignar valores a diversos paraacutemetros (llamados op-ciones) que sirven para configurar ciertas caracteriacutesticas del funcionamiento del generador o del analizador generado Cada paraacutemetro (opcioacuten) tiene un valor por defecto que es el que toma cuando no se le asigna expliacutecitamente un valor Los valores de las opciones tambieacuten se pueden fijar en la liacutenea de comandos

Seccioacuten de opciones

Seccioacuten de ejecucioacuten

Seccioacuten de sintaxis

Seccioacuten de lexicografiacutea

6

cuando se ejecuta el generador (lo indicado en la liacutenea de comandos tiene prioridad sobre lo especificado en esta seccioacuten de opciones)

Algunas de las opciones para las que se puede fijar un valor son

Ignore_Case (valor por defecto false)

indica si en el texto analizado ha de distinguirse o no entre letras minuacutesculas y mayuacutesculas

Build_Parser (valor por defecto true)

indica si se genera el analizador sintaacutectico o no

Build_Token_Manager (valor por defecto true)

indica si se genera el analizador lexicograacutefico o no

Sanity_Check (valor por defecto true)

indica si se realizan comprobaciones sobre la gramaacutetica sintaacutectica

Debug_Parser (valor por defecto false)

indica si se genera una traza para el anaacutelisis leacutexico-sintaacutectico

Error_Reporting (valor por defecto true)

indica si los mensajes de error emitidos son maacutes o menos explicativos

Static (valor por defecto true)

con el valor por defecto los meacutetodos de los analizadores leacutexico y sintaacutectico se generan con el des-criptor estaacutetico (static)

Los nombres de las opciones pueden escribirse con letras minuacutesculas o mayuacutesculas los valores de las op-ciones son en la mayoriacutea de los casos un valor entero o un valor loacutegico Si se incluye la seccioacuten se empie-za poniendo la palabra reservada options en este caso al menos ha de ponerse una opcioacuten

La forma de esta seccioacuten es

options nombreOpcion1 = valorOpcion1

nombreOpcion2 = valorOpcion2

∙ ∙ ∙ ∙

nombreOpcionk = valorOpcionk

loz Seccioacuten de ejecucioacutenEn esta seccioacuten se pone el coacutedigo Java que contiene la llamada al analizador generado para que se realice el anaacutelisis de un determinado texto de entrada Tambieacuten se establece aquiacute el nombre de la especificacioacuten que es el nombre que se toma para formar los nombres de parte de los ficheros (clases) generados

La seccioacuten estaacute delimitada por dos palabras reservadas ambas acompantildeadas por un mismo nombre (puesto entre pareacutentesis) ese nombre es el que se da a la especificacioacuten Entre esas dos palabras ha de ponerse una clase directora para el proceso de anaacutelisis el nombre de esa clase directora ha de coincidir con el nombre dado a la especificacioacuten

PARSER_BEGIN (NombreDeLaEspecif)

7

∙ ∙ ∙ ∙

public class NombreDeLaEspecif

∙ ∙ ∙ ∙

∙ ∙ ∙ ∙

PARSER_END (NombreDeLaEspecif)

Una posible versioacuten sencilla de la clase directora es

public class NombreDeLaEspecif public static void main (String[] argum) throws ParseException NombreDeLaEspecif anLeSi = new NombreDeLaEspecif (Systemin) anLeSisimboloPrograma()

se crea el objeto anLeSi para poder aplicar los meacutetodos de anaacutelisis se asocia la entrada standard ( Systemin ) a la entrada para el analizador generado se aplica al objeto creado el meacutetodo de anaacutelisis correspondiente al siacutembolo inicial de la gramaacutetica

loz Seccioacuten de sintaxisEn esta seccioacuten se describe la sintaxis del lenguaje para el que se desea generar el analizador usaacutendose pa-ra ello una notacioacuten parecida a la BNF En lo que sigue se expone la forma de las producciones tal y como se escriben en JavaCC ponieacutendolas en comparacioacuten con las producciones de la notacioacuten BNF-Ampliada

bull Reglas sintaacutecticasEl conjunto de reglas que definen un siacutembolo no terminal

ltNombreSimbgt = α1

| α2

∙ ∙ ∙

| αn

se codifica de la siguiente manera

void nombreSimb () α1-jcc

| α2-jcc

∙ ∙ ∙

| αn-jcc

8

- el nombre del siacutembolo no terminal (parte izquierda de las reglas) se escribe como el encabeza-miento de un meacutetodo en Java sin argumentos y sin valor de devolucioacuten (meacutetodo de tipo void) se sigue como en Java la costumbre de escribir los nombres de los meacutetodos empezando con letra mi-nuacutescula

- el siacutembolo dos puntos representa la separacioacuten entre las partes izquierda y derechas de las reglas (en vez del siacutembolo = de la notacioacuten BNF)

- el siacutembolo barra vertical representa la separacioacuten entre distintas partes derechas con la misma parte izquierda (igual que en la notacioacuten BNF)

- despueacutes del siacutembolo dos puntos se pone un bloque de coacutedigo Java este coacutedigo se suele dedicar a la realizacioacuten de tareas semaacutenticas si se pretende hacer soacutelo un anaacutelisis sintaacutectico el bloque que-daraacute vaciacuteo (aunque siempre es obligada su presencia)

- el conjunto de las alternativas (partes derechas de las reglas) estaacute delimitado mediante llaves como si constituyese un bloque de coacutedigo Java

- αi-jcc representa la codificacioacuten de una alternativa (parte derecha) empleaacutendose una nota-cioacuten que se describe a continuacioacuten

bull Siacutembolos no terminales en las partes derechasEn la notacioacuten BNF se pone ltNombreSimbgt en la notacioacuten JavaCC se escribe

nombreSimb()

esto es se pone como si fuese una llamada en Java a un meacutetodo sin argumentos

bull Siacutembolos terminales en las partes derechasSe considera una clasificacioacuten en las piezas sintaacutecticas (siacutembolos terminales de una gramaacutetica sintaacutectica) distinguieacutendose entre piezas sintaacutecticas nominales y anoacutenimas

son nominales las piezas sintaacutecticas a las que se asocia un nombre en otra parte de la especificacioacuten (maacutes adelante se describe la manera de indicar esta asociacioacuten) ese nombre asociado a la pieza es el que se usa al escribir las partes derechas de las producciones En la notacioacuten JavaCC un siacutembolo terminal no-minal se escribe poniendo el nombre delimitado por los caracteres lt y gt

ltnombreTerminalgt

son anoacutenimas las piezas sintaacutecticas que no tienen nombre asociado no se precisa porque la pieza se re -presenta por su propia secuencia de caracteres (lexema) En la notacioacuten JavaCC un siacutembolo terminal anoacutenimo se escribe poniendo su lexema delimitado por comillas por ejemplo

= lt lt= END

bull Metasiacutembolos de opcionalidad y de repeticioacutenEn la notacioacuten BNF se emplean los corchetes para indicar opcionalidad en la notacioacuten JavaCC se utilizan tambieacuten esos mismos siacutembolos

[ α ] en ambas notaciones

En la notacioacuten BNF se emplean las llaves para indicar una repeticioacuten de cero o maacutes veces en la notacioacuten JavaCC se utilizan los pareacutentesis y un asterisco detraacutes del pareacutentesis de cerrar

α en notacioacuten BNF-Ampliada

( α ) en notacioacuten JavaCC

bull Otros metasiacutembolos de la notacioacuten JavaCC

9

La opcionalidad tambieacuten se puede indicar empleando el metasiacutembolo de interrogacioacuten

( α )

Para indicar repeticioacuten de una o maacutes veces se utiliza el metasiacutembolo operador aditivo suma

( α )+

bull Control del final de fichero de entradaPara controlar el final del fichero en el texto analizado ha de antildeadirse una regla sintaacutectica especial que tenga (seguacuten la notacioacuten BNF) la forma

ltProgramaCompletogt = ltProgramagt FF

donde

ltProgramaCompletogt es un nuevo siacutembolo (no terminal) auxiliar antildeadido

ltProgramagt es el siacutembolo inicial de la gramaacutetica sintaacutectica

FF es una representacioacuten del final del fichero

En JavaCC el final de fichero se escribe con la notacioacuten ltEOFgt siendo EOF una palabra reservada la regla sintaacutectica especial antildeadida a la especificacioacuten quedariacutea codificada asiacute

void programaCompleto() programa() ltEOFgt

bull Representacioacuten de la produccioacuten vaciacuteaLas producciones vaciacuteas (reglas sintaacutecticas cuya parte derecha es la palabra vaciacutea ε ) no se pueden repre-sentar expliacutecitamente en una especificacioacuten leacutexico-sintaacutectica escrita en JavaCC Para indicar la presencia de una produccioacuten vaciacutea han de aplicarse las posibilidades de la notacioacuten BNF-Ampliada seguacuten esta nota-cioacuten cuando se indica la opcionalidad de un componente se estaacute especificando impliacutecitamente la palabra vaciacutea (en el caso de que el componente no esteacute presente)

Asiacute pues si se quisiera escribir en JavaCC la construccioacuten sintaacutectica expresada por las reglas

ltComponentegt = α

| ε

deberiacutean de considerarse con la notacioacuten equivalente

ltComponentegt = [ α ]

y si hubiera varias alternativas no vaciacuteas

ltComponentegt = α | β

| ε

habriacutea que considerarlas como

ltComponentegt = [ α | β ]

o tambieacuten podriacutea verse como

ltComponentegt = α

10

| [ β ]

Como ejemplo sea la especificacioacuten sintaacutectica de una llamada a una funcioacuten que puede tener cero o maacutes paraacutemetros separados por comas aunque no haya paraacutemetros los pareacutentesis estaacuten presentes Las siguien-tes reglas definen esta estructura

ltLlamadagt = id ( ltParametrosgt )

ltParametrosgt = ltExpresiongt ltExpresiongt | ε

En JavaCC la definicioacuten del siacutembolo ltParametrosgt se puede escribir como sigue (noacutetese que aquiacute los pareacutentesis empleados para aplicar el operador son metasiacutembolos)

void parametros() [ expresion() ( expresion() ) ]

loz Seccioacuten de lexicografiacuteaEn esta seccioacuten se indica la lexicografiacutea del lenguaje para el que se va a generar el analizador la notacioacuten de JavaCC que representa la forma de cada una de las piezas sintaacutecticas es una variante de las bien conoci-das expresiones regulares

bull El nombre de las piezas sintaacutecticas nominalesPara asociar un nombre a una pieza sintaacutectica y para al mismo tiempo definir la forma de la pieza se em-plea la notacioacuten

TOKEN lt nombreTerminal Expresioacuten-Regular gt

- TOKEN es una palabra reservada detraacutes de ella se pone el separador dos puntos

- nombreTerminal representa el nombre que se quiere asociar a la pieza sintaacutectica

- Expresioacuten-Regular indica una expresioacuten regular que especifica la forma de la pieza

- el nombre y la expresioacuten se separan mediante el caraacutecter dos puntos y conjuntamente se deli -mitan mediante los caracteres lt y gt a su vez toda la asociacioacuten se delimita con llaves

bull Expresiones regulares en JavaCCLas expresiones regulares en una especificacioacuten JavaCC se escriben de manera parecida a la forma utiliza-da en otras conocidas notaciones aunque con ciertas peculiaridades A continuacioacuten se mencionan los as-pectos maacutes significativos de las expresiones regulares JavaCC y algunos ejemplos

Los operadores de las expresiones son

| unioacuten

repeticioacuten cero o maacutes veces

+ repeticioacuten una o maacutes veces

opcionalidad

11

Los pareacutentesis permiten fijar el orden de aplicacioacuten de los operadores Una peculiaridad de las expresiones regulares JavaCC es que los operadores de repeticioacuten y de opcionalidad requieren que el componente al que se aplican esteacute en todo caso delimitado por pareacutentesis

Se pueden citar las siguientes caracteriacutesticas baacutesicas de las expresiones regulares en JavaCC

Se tiene la posibilidad de definir conjuntos de caracteres poniendo entre corchetes la relacioacuten de carac-teres separados por comas y cada caraacutecter delimitado por comillas En un conjunto se puede fijar un ran-go de caracteres poniendo el primero y el uacuteltimo separados por el siacutembolo guioacuten (-) A veces resulta maacutes coacutemodo indicar los caracteres que no pertenecen al conjunto para ello se puede definir el comple -mentario de un conjunto poniendo el siacutembolo ~ delante del corchete inicial

Para indicar que una secuencia de caracteres colocados consecutivamente constituye por siacute misma una expresioacuten regular (o un componente de una expresioacuten) se escribe la secuencia delimitada por comillas El siacutembolo se emplea como metacaraacutecter para indicar que el siacutembolo que estaacute inmediatamente a su derecha se considere como caraacutecter del alfabeto no como posible metacaraacutecter (operador delimitador)

En una expresioacuten regular se pueden incluir como si fueran caracteres las secuencias de escape con la misma notacioacuten y el mismo significado que en Java o en C

Noacutetese que en una produccioacuten sintaacutectica la opcionalidad se puede especificar de dos formas con corchetes o con el siacutembolo de interrogacioacuten sin embargo en una expresioacuten regular la opcionalidad soacutelo se puede es-pecificar mediante el siacutembolo de interrogacioacuten (los corchetes en las expresiones regulares son para definir conjuntos de caracteres)

A continuacioacuten se muestran ejemplos de expresiones regulares escritas seguacuten la notacioacuten JavaCC

ENDsecuencia de las tres letras mayuacutesculas que conforman la palabra END

whilesecuencia de las cinco letras minuacutesculas que conforman la palabra while

ncaraacutecter representativo del fin de liacutenea (secuencia de escape)

rcaraacutecter representativo del retorno de carro (secuencia de escape)

caraacutecter dos puntos

=secuencia de dos caracteres (siacutembolo de asignacioacuten en Pascal)

gtcaraacutecter representativo del operador de relacioacuten mayor

(i | I)(f | F)palabra ldquoifrdquo escrita con letras minuacutesculas o mayuacutesculas (cuatro combinaciones)

if | IFpalabra ldquoifrdquo escrita soacutelo con minuacutesculas o soacutelo con mayuacutesculas (dos combinaciones)

12

u | o | i | e | auna cualquiera de las vocales minuacutesculas (pero soacutelo una de ellas)

[ A E I O U ]una cualquiera de las vocales mayuacutesculas (pero soacutelo una de ellas)

[ a - z ntilde ]una cualquiera de las letras minuacutesculas incluyendo la letra ntilde

[ a - z A - Z 0 - 9 ]una cualquiera de las letras o de las cifras decimales

([ 0 - 9 ])+constante entera (nuacutemero de una o maacutes cifras)

noacutetese que es incorrecto poner [ 0 - 9 ]+

( + | - )([ 0 - 9 ])+constante entera precedida opcionalmente por un signo

noacutetese que la primera cifra ha de seguir inmediatamente al signo (si existe)

([+ -])([ 0 - 9 ])+representa lo mismo que la expresioacuten anterior

[ a - z ] ([ a - z 0 - 9 ])una letra minuacutescula seguida de cero o maacutes letras minuacutesculas o cifras decimales

~[a - z ntilde A - Z Ntilde]cualquier caraacutecter que no sea una letra

noacutetese que la expresioacuten representa un uacutenico caraacutecter

~[n r]cualquier caraacutecter (un uacutenico caraacutecter) excepto el fin de liacutenea y el retorno de carro

~[ ]cualquier caraacutecter del alfabeto (un uacutenico caraacutecter)

([ 0 - 9 ])+ ( ([ 0 - 9 ])+)constante aritmeacutetica con parte decimal opcional

Los modernossecuencia de catorce caracteres Los modernos

noacutetese el uso de para imponer la condicioacuten de caraacutecter del siacutembolo comillas

nsecuencia de cuatro caracteres n

noacutetese que aquiacute no se representa la secuencia de escape

bull Piezas sintaacutecticas internas (ldquoprivaterdquo)

13

Es posible definir piezas sintaacutecticas auxiliares llamadas ldquointernasrdquo porque no se comunican al analizador sintaacutectico sino que soacutelo sirven para facilitar la escritura de la forma de las piezas sintaacutecticas que siacute se co-munican con el uso de las piezas internas es posible evitar repeticiones de escritura y hacer maacutes legible la especificacioacuten de las caracteriacutesticas lexicograacuteficas Las piezas internas permiten dar nombre a subexpre-siones regulares que a su vez pueden emplearse como componentes para escribir expresiones maacutes com-plejas que incorporan esos nombres Para indicar que una pieza sintaacutectica se considere como interna se pone el siacutembolo delante del nombre asociado a la pieza

A continuacioacuten se muestra un ejemplo tiacutepico de utilizacioacuten de las piezas internas en una especificacioacuten le-xicograacutefica en JavaCC Se quieren definir tres piezas sintaacutecticas que representan un nombre una constante entera y una constante decimal una posible especificacioacuten es

TOKEN ltnombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN ltcteEntera ( [ 0 - 9 ] )+ gt

TOKEN ltcteDecimal ( [ 0 - 9 ] )+ ( [ 0 - 9 ] )+ gt

Con la inclusioacuten de tres piezas sintaacutecticas internas auxiliares esta misma especificacioacuten podriacutea escribirse de la siguiente manera

TOKEN lt letra [ a - z A - Z ntilde Ntilde ] gt

TOKEN lt cifra [ 0 - 9 ] gt

TOKEN lt numero ( ltcifragt )+ gt

TOKEN lt nombre ( ltletragt )+ gt

TOKEN lt cteEntera ltnumerogt gt

TOKEN lt cteDecimal ltnumerogt ltnumerogt gt

Los nombres letra cifra y numero son exclusivos (internos) del analizador lexicograacutefico y no se pueden emplear como nombres de piezas en la especificacioacuten de las reglas sintaacutecticas cuando esos nom-bres se usan en la definicioacuten de otras piezas han de escribirse delimitados por los siacutembolos lt y gt

bull Distincioacuten restringida entre minuacutesculas y mayuacutesculasSe tiene la opcioacuten Ignore_Case para determinar si en el anaacutelisis del texto de entrada se distingue entre las letras minuacutesculas y las mayuacutesculas el valor por defecto false indica que siacute se diferencian si se asig-na el valor true a esta opcioacuten entonces no se diferencian en ninguacuten caso con independencia de que en los patrones que se definan para las piezas sintaacutecticas se pongan minuacutesculas o mayuacutesculas

Pero a veces conviene que la diferenciacioacuten entre minuacutesculas y mayuacutesculas no se aplique de manera ge-neral a toda la entrada sino en particular a las secuencias de caracteres que se acoplen a ciertos patrones Para este fin existe la posibilidad de establecer que en la expresioacuten regular que define el patroacuten de una determinada pieza sintaacutectica no se diferencie entre minuacutesculas y mayuacutesculas ello se consigue poniendo

14

[ IGNORE_CASE ]

inmediatamente detraacutes de la palabra TOKEN con la que empieza la especificacioacuten de la pieza sintaacutectica en la que se pretende la no distincioacuten entre minuacutesculas y mayuacutesculas

Noacutetese que ignore_case tiene dos significados

- es el nombre de un paraacutemetro de la seccioacuten de opciones (puede ponerse en minuacutesculas o mayuacutesculas)- es una palabra reservada que puede aplicarse (escribieacutendola entre corchetes y en mayuacutesculas) a la de -finicioacuten de una pieza sintaacutectica

Por ejemplo sea un lenguaje que en general distingue entre minuacutesculas y mayuacutesculas pero en la codifi -cacioacuten del exponente de una constante real exponencial puede ponerse la letra e en minuacutescula o en ma-yuacutescula En la especificacioacuten JavaCC para ese lenguaje la opcioacuten Ignore_Case ha de tomar el valor false en la definicioacuten de una constante exponencial son posibles las siguientes formas equivalentes

1) sin hacer uso de IGNORE_CASE

TOKEN lt cifras ( [ 0 - 9 ] )+ gt

TOKEN lt signo [+ -] gt

TOKEN lt constante

ltcifrasgt ( ltcifrasgt) [e E] (ltsignogt) ltcifrasgt gt

2) aplicando IGNORE_CASE

TOKEN lt cifras ( [ 0 - 9 ] )+ gt

TOKEN lt signo [+ -] gt

TOKEN [IGNORE_CASE] lt constante

ltcifrasgt ( ltcifrasgt) e (ltsignogt) ltcifrasgt gt

bull Secuencias de caracteres sin efecto en la estructura sintaacutecticaEn los lenguajes de programacioacuten suele haber secuencias de caracteres que no forman parte de la estructu-ra sintaacutectica de un programa esas secuencias son detectadas por el analizador lexicograacutefico pero no son comunicadas al sintaacutectico no han de considerarse como piezas sino que han de saltarse En JavaCC se pueden definir estas secuencias mediante la palabra reservada SKIP la notacioacuten de una especificacioacuten con la palabra SKIP de una secuencia que ha de saltarse es la misma que la notacioacuten usada para las espe-cificaciones con la palabra TOKEN de las piezas que han de comunicarse

Los dos casos maacutes habituales de secuencias que no se comunican al analizador sintaacutectico son

a) los caracteres separadores que no tienen significado en el texto analizadoen los lenguajes de codificacioacuten en formato libre hay caracteres del texto fuente que han de ignorarse en la mayoriacutea de los casos estos caracteres son el espacio en blanco el tabulador el retorno de carro y el fin de liacutenea esta caracteriacutestica se especifica

SKIP |t | r |n

b) los comentarios

15

en el anaacutelisis sintaacutectico de un programa los comentarios han de ignorarse una manera de especificar esta circunstancia es poniendo en una definicioacuten SKIP la expresioacuten regular que denota la forma de los comentarios del lenguaje por ejemplo para los comentarios de una liacutenea de Java se pondriacutea

SKIP

lt (~ [n r]) gt

Tareas asociadas a la estructura sintaacutecticaJavaCC genera un analizador sintaacutectico descendente recursivo constituido por un conjunto de meacutetodos ca-da siacutembolo no terminal de la gramaacutetica sintaacutectica tiene asociado un meacutetodo de anaacutelisis (el meacutetodo tiene el mismo nombre que el siacutembolo correspondiente) Un meacutetodo de anaacutelisis ademaacutes de la realizacioacuten de la parte del anaacutelisis sintaacutectico que le corresponde puede llevar a cabo otras tareas complementarias que sirvan para efectuar un anaacutelisis maacutes complejo del texto de entrada

loz Bloque para un siacutemboloAl describir la forma de las reglas sintaacutecticas de un siacutembolo no terminal en una especificacioacuten JavaCC ya se ha visto que detraacutes de la definicioacuten del nombre del siacutembolo y antes de empezar con las diferentes alter-nativas que tiene asociadas se incluye un bloque de coacutedigo Java (en el ejemplo de presentacioacuten este bloque estaacute vaciacuteo en todos los siacutembolos no terminales de la gramaacutetica) Cada bloque situado en esa posicioacuten puede considerarse ligado al siacutembolo no terminal en cuya descripcioacuten estaacute incluido el coacutedigo Java del bloque se traslada literalmente al principio del meacutetodo generado correspondiente al siacutembolo no terminal y por lo tan-to es lo primero que se considera (se ejecuta) cuando en el transcurso del proceso de anaacutelisis se realiza una llamada al meacutetodo Asiacute pues cada siacutembolo no terminal siempre tiene asociado un uacutenico bloque de coacute-digo Java ndashbloque de presencia obligada aunque pueda estar vaciacuteo- en este bloque se pueden poner de-claraciones y sentencias

loz Acciones sintaacutecticasAdemaacutes del bloque asociado a cada uno de los siacutembolos no terminales de la gramaacutetica se pueden incluir otros bloques de coacutedigo Java intercalados en cualquiera de las partes derechas de las reglas sintaacutecticas Los bloques incluidos en las partes derechas se denominan ldquoacciones sintaacutecticasrdquo (parser actions) ya que son trozos de coacutedigo que pueden considerarse asociados a los puntos de la estructura sintaacutectica donde estaacuten co-locados Una accioacuten sintaacutectica se puede poner en cualquier posicioacuten al principio al final o en el medio de la parte derecha de una regla precedida o seguida de siacutembolos terminales o no terminales el coacutedigo Java de una accioacuten sintaacutectica se traslada literalmente al meacutetodo asociado al siacutembolo no terminal de la parte izquierda de la regla de manera que se ejecuta cada vez que el analizador transcurre por el punto de la estructura sintaacutectica donde se ha incorporado la accioacuten (esto es las acciones sintaacutecticas se incorporan al coacutedigo del analizador sintaacutectico) No obstante su nombre en los bloques de las acciones sintaacutecticas suele ponerse coacutedigo dedicado a la realizacioacuten de tareas semaacutenticas

Si en el bloque asociado a un siacutembolo no terminal se ponen declaraciones los nombres ahiacute definidos son accesibles en todas las acciones sintaacutecticas intercaladas en las partes derechas de las reglas de ese siacutembolo la justificacioacuten es sencilla el coacutedigo del bloque asociado al siacutembolo se incluye al principio de un meacutetodo y el coacutedigo de las acciones pertenece a ese mismo meacutetodo

∆ Ejemplo 1

Se considera de nuevo el ejemplo de presentacioacuten ahora se pretende completar el anaacutelisis leacutexico-sintaacutec-tico con la realizacioacuten de las siguientes tareas

- cada vez que se analiza una expresioacuten un teacutermino o un factor se graba el nombre del siacutembolo no terminal correspondiente y

16

- cuando se analiza un teacutermino en el que hay operadores multiplicativos se cuenta su nuacutemero y se graba el resultado (noacutetese que se cuentan los operadores de cada teacutermino pero no se acumula la cuenta de todos los operadores de la expresioacuten)

Para la realizacioacuten de estas tareas se antildeaden bloques de coacutedigo Java En la especificacioacuten que se describe a continuacioacuten puede apreciarse- para los siacutembolos no terminales expresion termino y factor se ha incluido en los blo-ques correspondientes la operacioacuten de grabacioacuten de su nombre que se ejecutaraacute cada vez que se efec-tuacutee una llamada al meacutetodo- en el bloque asociado a termino se tiene la declaracioacuten del contador de operadores (que seraacute una variable local del meacutetodo)- en la parte derecha de la regla que define la estructura de un teacutermino se han incluido dos acciones sintaacutecticas una para contar la cantidad de operadores (situada detraacutes de la pieza sintaacutectica del opera-dor) y otra para grabar la cantidad de operadores del teacutermino (situada al final de la parte derecha)- se ha definido el meacutetodo grabar para facilitar la escritura del coacutedigo Java

options Ignore_Case = true

PARSER_BEGIN (ExprMin) public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

private static void grabar(String nombre) Systemoutprintln ( -gt + nombre + n)

PARSER_END (ExprMin)

void unaExpresion()

expresion() ltEOFgt

void expresion() grabar(Expresion)

termino() ( + termino() )

void termino() int nAst = 0 grabar(Termino)

17

factor() ( nAst++ factor() ) if (nAst gt 0) Systemoutprintln (n Asteriscos + nAst + n)

void factor() grabar(Factor)

ltconstantegt | ltvariablegt | ( expresion() )

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP | t | n | r

Cuando se incluyen acciones sintaacutecticas la legibilidad del aspecto sintaacutectico de la especificacioacuten queda oscurecida para paliar ese inconveniente puede antildeadirse un comentario que muestre la regla sintaacutectica escueta sin las acciones por ejemplo podriacutea escribirse

void termino() int nAst = 0 grabar(Termino)

factor() ( factor() )

factor()

( nAst++ factor() )

if (nAst gt 0) Systemoutprintln (n Asteriscos + nAst + n)

loz Valor comunicado por un meacutetodoEn todo lo visto hasta ahora los meacutetodos de anaacutelisis sintaacutectico generados por JavaCC no devuelven valor alguno al terminar su tarea de anaacutelisis ya que en la especificacioacuten siempre se ha declarado el nombre de un meacutetodo precedido de void (son meacutetodos de tipo void) Pero en general un meacutetodo de anaacutelisis sintaacutec-tico puede ser de cualquier tipo (primitivo o no) por ello un meacutetodo puede devolver un valor de cualquier tipo (de cualquier clase) al meacutetodo desde el que se efectuoacute la llamada

Esta posibilidad de devolucioacuten de valores tiene una utilidad fundamental en la implementacioacuten de analiza-dores de lenguajes permite la comunicacioacuten de datos (atributos de las entidades analizadas) entre los dife -rentes meacutetodos que colaboran en el anaacutelisis del programa completo

En una especificacioacuten JavaCC la llamada a un meacutetodo ocurre cuando en la parte derecha de una regla estaacute el nombre del siacutembolo no terminal asociado al meacutetodo Cuando un meacutetodo devuelve un valor (tiene un tipo distinto de void) es habitual la recepcioacuten del valor devuelto en una variable en la propia especificacioacuten

18

se puede indicar el nombre de la variable a la que se asignaraacute el valor devuelto tras la llamada para ello se emplea una notacioacuten ideacutentica a la forma que tienen las sentencias de asignacioacuten en el lenguaje Java

dato = nombreSimb()

donde dato es el nombre de la variable a la que se asigna el valor devuelto por el meacutetodo llamado esa va -riable ha de estar oportunamente declarada del mismo tipo que dicho meacutetodo

∆ Ejemplo 2

En el mismo ejemplo de presentacioacuten se desea ademaacutes del anaacutelisis leacutexico-sintaacutectico de la entrada contar la cantidad de operadores que hay en la expresioacuten A continuacioacuten se expone una especificacioacuten JavaCC que es una posible solucioacuten soacutelo se muestran las reglas sintaacutecticas (el resto de la especificacioacuten es igual que en el ejemplo de presentacioacuten)

En la especificacioacuten que se propone puede apreciarse- los siacutembolos no terminales expresion termino y factor se han declarado de tipo int los meacutetodos correspondientes devuelven un valor de tipo entero- el valor devuelto por los meacutetodos sirve para propagar a traveacutes de la estructura sintaacutectica la cuenta de los operadores que hay en el texto analizado- por tratarse de meacutetodos que devuelven un valor es preciso incluir las correspondientes sentencias de devolucioacuten return

void unaExpresion() int nOper nOper = expresion() ltEOFgt Systemoutprintln (Cantidad de operadores = + nOper)

int expresion() int n i n = termino() ( + n++ i = termino() n = n + i )

return n

int termino() int n i n = factor() ( n++ i = factor() n = n + i )

return n

int factor() int n ltconstantegt return 0 | ltvariablegt return 0 | ( n = expresion() ) return n

La especificacioacuten de un factor tambieacuten podriacutea escribirse de esta otra manera

19

int factor() int n = 0

( ltconstantegt | ltvariablegt | ( n = expresion() ) )

return n

A continuacioacuten se expone un nuevo ejemplo para ilustrar la utilidad que proporciona el valor devuelto por un meacutetodo de anaacutelisis sintaacutectico para la propagacioacuten (ascendente) de atributos (propiedades de las entida-des analizadas)

∆ Ejemplo 3

Se pretende ampliar el analizador del ejemplo de presentacioacuten para que se emita un mensaje indicativo cuando la expresioacuten analizada sea una expresioacuten en la que soacutelo aparecen constantes en la solucioacuten que se muestra soacutelo se incluyen las reglas sintaacutecticas (en el resto de la especificacioacuten no hay modificacio-nes)

void unaExpresion() boolean esCte

expresion() ltEOFgt

esCte = expresion() ltEOFgt

if (esCte) Systemoutprintln(nExpresion constanten)

boolean expresion() boolean esCte esTambienCte

termino() ( + termino() )

esCte = termino() ( + esTambienCte = termino() esCte = esCte ampamp esTambienCte )

return esCte

boolean termino() boolean esCte esTambienCte

factor() ( factor() )

esCte = factor() ( esTambienCte = factor() esCte = esCte ampamp esTambienCte )

return esCte

20

boolean factor() boolean esCte

ltconstantegt return true | ltvariablegt return false | ( esCte = expresion() ) return esCte

bull Reconsideracioacuten sobre las producciones vaciacuteasAnteriormente se ha comentado que para representar una produccioacuten vaciacutea en una especificacioacuten JavaCC hay que hacer uso de los metasiacutembolos de opcionalidad para ilustrar esta idea se ha incluido como ejem-plo la especificacioacuten de una lista de paraacutemetros compuesta por cero o maacutes expresiones separadas entre siacute por el caraacutecter coma

void parametros() [ expresion() ( expresion() ) ]

Pero si se aprovecha la posibilidad de incorporar acciones sintaacutecticas se tiene una manera de especificar directamente producciones vaciacuteas para conseguirlo basta considerar una produccioacuten vaciacutea (no tiene nada en su parte derecha) acompantildeada de una accioacuten sintaacutectica cuyo bloque de coacutedigo esteacute vaciacuteo Seguacuten esta posibilidad la especificacioacuten anterior podriacutea tambieacuten escribirse asiacute

void parametros() expresion() ( expresion() ) |

En general la accioacuten sintaacutectica asociada a una produccioacuten vaciacutea no tiene por queacute ser un bloque vaciacuteo de coacutedigo por ejemplo si se quiere que el meacutetodo que analiza los paraacutemetros de una llamada devuelva la cantidad de paraacutemetros encontrados se podriacutea escribir la siguiente especificacioacuten

int parametros()

int cantidad

( expresion() cantidad = 1

( expresion() cantidad++ )

| cantidad = 0 )

return cantidad

y si no quisiera ponerse expliacutecitamente la alternativa de la produccioacuten vaciacutea podriacutea escribirse

int parametros() int cantidad = 0

21

[ expresion() cantidad++ ( expresion() cantidad++ ) ]

return cantidad

Comunicacioacuten entre los analizadores leacutexico y sintaacutecticoCada vez que el analizador sintaacutectico realiza una llamada al analizador lexicograacutefico recibe como resultado de la llamada una representacioacuten de la pieza sintaacutectica encontrada en el texto analizado En los analizadores generados por JavaCC la comunicacioacuten de la pieza se efectuacutea mediante un valor de clase Token Esta cla-se estaacute definida en el fichero Tokenjava que se obtiene en todo caso siempre con el mismo contenido cualquiera que sea la especificacioacuten proporcionada como entrada al generador

Entre los campos de la clase Token se encuentran los siguientes

public int kind

nuacutemero entero que sirve de representacioacuten interna de la pieza sintaacutectica estos nuacutemeros asociados a las piezas son asignados automaacuteticamente por JavaCC los valores considerados pueden consultar-se en el fichero generado de nombre Constantsjava

public String image

cadena que contiene de la secuencia de caracteres (lexema) que constituyen la pieza sintaacutectica co-municada

public int beginLine beginColumn endLine endColumn

posiciones ocupadas (nuacutemeros de liacutenea y columna en el fichero de entrada) por el comienzo y el fi-nal del lexema de la pieza sintaacutectica comunicada

La clase Token tiene otros campos y meacutetodos en el fichero Tokenjava se tienen comentarios des-criptivos sobre todos los campos y meacutetodos que forman parte de la clase todos los campos de la clase se declaran como public

Ya se ha comentado anteriormente que cuando un meacutetodo de anaacutelisis sintaacutectico (asociado a un siacutembolo no terminal) tiene tipo el valor devuelto tras una llamada se puede almacenar en una variable por si se desea utilizar en alguna accioacuten sintaacutectica para ello se escribe

valor = nombreSimbolo()

(siendo valor el nombre de la variable y nombreSimbolo el nombre del siacutembolo no terminal) De manera anaacuteloga el objeto de la clase Token representativo de la pieza sintaacutectica encontrada en la entrada por el analizador lexicograacutefico puede asignarse a una variable del mismo tipo para tener accesibles las ca -racteriacutesticas de la pieza por si se precisaran en alguna accioacuten sintaacutectica para ello en la parte de la especifi -cacioacuten sintaacutectica donde aparezca la pieza sintaacutectica se escribe

dato = lt nombrePieza gt

donde nombrePieza es el nombre de la pieza sintaacutectica y dato es el nombre de una variable de tipo Token en la que se anota el valor indicativo de las propiedades de la pieza sintaacutectica comunicada

Tambieacuten existe otra manera de acceder a las caracteriacutesticas de la pieza sintaacutectica que el analizador lexico -graacutefico comunica al analizador sintaacutectico en la clase del analizador sintaacutectico se tiene declarado el campo

[ static ] public Token token

22

cada vez que el analizador sintaacutectico recibe una pieza sintaacutectica comunicada por el analizador lexicograacutefico se deja anotada en el campo token y ese contenido no se altera hasta que sea proporcionada la siguiente pieza de la entrada

∆ Ejemplo 4

Se considera una ampliacioacuten del ejemplo de presentacioacuten ademaacutes del anaacutelisis de la entrada se pretende obtener una relacioacuten numerada de las variables y las constantes indicando el nuacutemero de liacutenea y el nuacuteme-ro de columna donde estaacuten situadas por ejemplo si en la entrada se tiene la expresioacuten (grabada en dos liacuteneas)

x (y + 11) + 22

en la salida se tendraacute

1- x linea 1 columna 1

2- y linea 1 columna 6

3- 11 linea 1 columna 10

4- 22 linea 2 columna 5

En la especificacioacuten que se propone como solucioacuten puede apreciarse- en la clase del analizador sintaacutectico se han incluido meacutetodos auxiliares en los que se usan los nom-bres image beginLine beginColumn que son campos de un objeto de clase Token

- en la especificacioacuten sintaacutectica de factor se emplea la variable pieza de tipo Token para reco-ger las caracteriacutesticas de las piezas sintaacutecticas detectadas- la variable numero hace de contador para numerar las constantes y variables

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin

static int numero = 0

private static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

private static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

private static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

23

PARSER_END (ExprMin)

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() Token pieza pieza = ltconstantegt numero++ grabarDatosPieza(numero pieza) | pieza = ltvariablegt numero++ grabarDatosPieza(numero pieza) | ( expresion() )

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP | n | t | r

Si se agrupan las dos acciones sintaacutecticas comunes la especificacioacuten de factor tambieacuten puede escri-birse de esta forma

void factor() Token pieza ( pieza = ltconstantegt | pieza = ltvariablegt

) numero++ grabarDatosPieza(numero pieza)

| ( expresion() )

24

Si se accede al campo token de la clase del analizador sintaacutectico se puede resolver este ejemplo pres-cindiendo de la variable pieza

void factor() ( ltconstantegt | ltvariablegt

) numero++ grabarDatosPieza(numero token)

| ( expresion() )

Tareas asociadas a las piezas sintaacutecticasYa se ha visto que es posible asociar bloques de coacutedigo Java a puntos de la estructura sintaacutectica para que se realicen ciertas operaciones cuando el analizador sintaacutectico pase por esa zona de la estructura Tambieacuten es posible asociar coacutedigo Java a las piezas sintaacutecticas para que el analizador lexicograacutefico efectuacutee determina-das tareas cuando detecte en la entrada analizada piezas que tengan asociado coacutedigo

loz Accioacuten ligada a una pieza sintaacutecticaSi se asocia un bloque de coacutedigo a una pieza sintaacutectica se consigue que cada vez que se detecta en la en-trada analizada la presencia de esa pieza se ejecute en ese preciso momento el coacutedigo del bloque asociado a la pieza Estas acciones se denominan ldquoacciones lexicograacuteficasrdquo (lexical actions) el coacutedigo del bloque se traslada literalmente al coacutedigo del analizador lexicograacutefico generado para que se ejecute oportunamente cuando se detecte la pieza ligada al bloque de coacutedigo La accioacuten lexicograacutefica se ejecuta inmediatamente antes que la pieza sea comunicada al analizador sintaacutectico

Se puede asociar una accioacuten lexicograacutefica a una pieza sintaacutectica declarada con TOKEN y tambieacuten a una se-cuencia de caracteres declarada con SKIP ademaacutes de eacutestas hay otras dos formas de declaraciones lexico-graacuteficas (no mencionadas en esta introduccioacuten) que tambieacuten pueden llevar una accioacuten asociada

En una especificacioacuten JavaCC la forma de una pieza sintaacutectica (o bien de una secuencia de caracteres) se define poniendo su descripcioacuten delimitada entre llaves detraacutes de la indicacioacuten TOKEN (o bien SKIP) si se quiere asociar una accioacuten lexicograacutefica a la pieza (o a la secuencia) hay que colocar el bloque de coacutedigo de la accioacuten a continuacioacuten de la descripcioacuten de la forma de la pieza (o de la secuencia) el bloque (delimi-tado por llaves) queda situado entre la descripcioacuten y la llave que cierra la especificacioacuten

La pieza sintaacutectica especial que representa el final del fichero (EOF) tambieacuten puede llevar asociada una ac-cioacuten lexicograacutefica para ello hay que poner

ltgt TOKEN lt EOF gt coacutedigo de la accioacuten asociada

para justificar la presencia de los caracteres ltgt delante de la palabra TOKEN hay que basarse en el con-cepto de ldquoestado lexicograacuteficordquo (cuya explicacioacuten queda fuera del alcance de esta introduccioacuten)

∆ Ejemplo 5

Se considera nuevamente el ejemplo de presentacioacuten como resultado del anaacutelisis de una expresioacuten se pretende obtener una relacioacuten de los pareacutentesis contenidos en la expresioacuten en el mismo orden en que se encuentran ademaacutes por cada salto de liacutenea del texto de entrada se grabaraacute en la salida el caraacutecter y

25

se pasaraacute a una nueva liacutenea tambieacuten se anotaraacute la frase Final del trabajo cuando se alcance el final del fichero de entrada Asiacute por ejemplo si la entrada analizada es

(a + b) (( x )) (y + 7)

los resultados obtenidos seraacuten

() (())()

Final del trabajo

En la especificacioacuten JavaCC que se propone como solucioacuten soacutelo se muestran las reglas sintaacutecticas y le -xicograacuteficas (en el resto no hay cambios) en esta solucioacuten puede apreciarse- las piezas sintaacutecticas correspondientes a los pareacutentesis se han definido de manera nominal para po-der asociarles una accioacuten lexicograacutefica- los caracteres incluidos en la pieza de tipo SKIP se han descompuesto en dos grupos ya que la ac-cioacuten lexicograacutefica incorporada soacutelo afecta a uno de ellos

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ltpAbrirgt expresion() ltpCerrargt

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

TOKEN

26

lt pAbrir ( gt Systemoutprint(()

TOKEN lt pCerrar ) gt Systemoutprint())

ltgtTOKEN lt EOF gt Systemoutprintln ( Final del trabajo )

SKIP n Systemoutprint( n)

SKIP | t | r

loz Bloque de declaraciones lexicograacuteficasCada definicioacuten de una pieza sintaacutectica puede llevar asociada una accioacuten lexicograacutefica que se ejecuta in-mediatamente despueacutes de detectar en la entrada un lexema que se ajusta a la definicioacuten de la pieza cada accioacuten lexicograacutefica es un bloque de coacutedigo Java que puede contener sus propias declaraciones Pero a ve-ces interesa tener declaraciones que sean compartidas por todas las acciones lexicograacuteficas con indepen-dencia de la pieza a la que esteacute asociada cada una de ellas

Para incluir declaraciones que puedan usarse desde el coacutedigo asociado a cualquiera de las piezas sintaacutecti -cas se dispone de una construccioacuten con el siguiente formato

TOKEN_MGR_DECLS

que consta de una palabra reservada seguida del siacutembolo dos puntos seguido de un bloque de coacutedigo Java (delimitado por llaves) esta declaracioacuten se puede colocar en cualquier parte de una especificacioacuten JavaCC si se incluye soacutelo puede ponerse una vez

Todas las declaraciones de variables o de meacutetodos puestas en el bloque de declaraciones lexicograacuteficas compartidas son accesibles desde el coacutedigo de cualquier accioacuten lexicograacutefica la justificacioacuten es sencilla el bloque de declaraciones se traslada al principio del coacutedigo del analizador lexicograacutefico generado y las ac-ciones lexicograacuteficas tambieacuten se trasladan a ese analizador

loz Declaraciones lexicograacuteficas predefinidasDe lo expuesto hasta ahora se aprecia que en una accioacuten lexicograacutefica asociada a una pieza sintaacutectica re-sultan accesibles

- las declaraciones de su propio bloque (si tiene declaraciones)- los componentes del bloque de declaraciones compartidas (si estaacute incluido en la especificacioacuten)

Ademaacutes de esto se tienen otras declaraciones pertenecientes a la clase TokenManager que constituye el analizador lexicograacutefico que tambieacuten son utilizables desde cualquiera de las acciones lexico-graacuteficas estas declaraciones pueden considerarse como ldquopredefinidasrdquo ya que son incorporadas automaacuteti-camente por el generador Entre estas declaraciones accesibles se encuentran las siguientes

[ static ] StringBuffer imagecontiene la cadena de caracteres representativa de la pieza sintaacutectica actual que se acaba de recono-cer en le entrada se trata de un campo de la clase y se declara como static cuando el analizador

27

se ha generado con la opcioacuten (por defecto) Static = true si se generase con la opcioacuten Sta-tic = false no quedariacutea declarada como static

Token matchedToken

variable de clase Token que representa la pieza sintaacutectica actual la que se acaba de encontrar en la entrada se trata de una variable local del meacutetodo al que se ha trasladado el coacutedigo de la accioacuten (y por ello accesible desde ese coacutedigo)

El campo image puede usarse tanto si la accioacuten lexicograacutefica estaacute asociada a una pieza declarada con TOKEN como si lo estaacute a una secuencia declarada con SKIP Sin embargo la variable matchedToken no puede usarse en una accioacuten asociada a un patroacuten especificado como SKIP el motivo es que lo declara-do como SKIP no constituye propiamente una pieza sintaacutectica ya que no se va a comunicar al analizador sintaacutectico

∆ Ejemplo 6

Se propone aquiacute una nueva solucioacuten para el ejemplo nordm 4 expuesto anteriormente

Esta nueva solucioacuten tal y como se expone aquiacute admite algunas mejoras ya que su pretensioacuten soacutelo es ser-vir de ilustracioacuten para algunos aspectos que se acaban de citar (maacutes adelante se propone otra solucioacuten al-ternativa)

En la especificacioacuten de la solucioacuten es posible apreciar esto- las acciones lexicograacuteficas pueden llevar sus propias declaraciones las variables nLineaVar nColumnaVar nLineaCte y nColumnaCte se declaran dentro de las acciones

- la variable matchedToken a la que se hace referencia en el coacutedigo de la accioacuten lexicograacutefica aso-ciada a una variable contiene la informacioacuten sobre la pieza sintaacutectica que se acaba de detectar una va-riable de la expresioacuten analizada anaacutelogamente con la referencia relativa a una constante- el nombre image que aparece en las acciones lexicograacuteficas se refiere al campo de la clase Expr-MinTokenManager (analizador lexicograacutefico) noacutetese que en efecto se comunica como un paraacuteme-tro de clase StringBuffer

- la variable numero se emplea para numerar las constantes y variables encontradas

- los nombres beginLine y beginColumn son campos (puacuteblicos) de la clase Token por ello se pueden aplicar a un objeto de esa clase

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

void unaExpresion()

28

expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n StringBuffer lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

TOKEN lt variable [a-z] gt

int nLineaVar nColumnaVar numero++ nLineaVar = matchedTokenbeginLine nColumnaVar = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaVar nColumnaVar)

TOKEN lt constante ( [0-9] ) + gt

int nLineaCte nColumnaCte

29

numero++ nLineaCte = matchedTokenbeginLine nColumnaCte = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaCte nColumnaCte)

SKIP | n | t | r

La especificacioacuten precedente se puede escribir de otra manera maacutes escueta como se indica a continua-cioacuten (soacutelo se muestran las modificaciones) sobre esta nueva especificacioacuten puede mencionarse- se han eliminado por innecesarias las variables declaradas dentro de las acciones lexicograacuteficas- el nombre image que aparece en el bloque de las declaraciones compartidas se refiere al campo de la clase Token noacutetese que en efecto se aplica a un objeto de esa clase y se comunica como un paraacute -metro de tipo String

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

TOKEN lt variable [a-z] gt

numero++ grabarDatosPieza(numero matchedToken)

TOKEN lt constante ( [0-9] ) + gt

numero++ grabarDatosPieza(numero matchedToken)

loz Accioacuten lexicograacutefica comuacutenSi hubiera que realizar la misma tarea tras la deteccioacuten de todas y cada una de las piezas sintaacutecticas una posibilidad incoacutemoda seriacutea repetir el mismo coacutedigo en todas las acciones lexicograacuteficas para evitar tanta reiteracioacuten se tiene la opcioacuten Common_Token_Action su valor por defecto es false pero si se le asigna el valor true es posible definir una ldquoaccioacuten lexicograacutefica comuacutenrdquo que se escribe una uacutenica vez y se aplica a todas las piezas sintaacutecticas

30

Cuando la opcioacuten Common_Token_Action se establece con el valor true es obligado incluir el blo-que de declaraciones lexicograacuteficas compartidas (TOKEN_MGR_DECLS) Ademaacutes en ese bloque se preci-sa la definicioacuten de un meacutetodo cuyo encabezamiento sea

[ static ] void CommonTokenAction(Token t)

el coacutedigo de este meacutetodo constituye la accioacuten lexicograacutefica comuacuten que se ejecutaraacute despueacutes de la deteccioacuten de cualquier pieza sintaacutectica el meacutetodo tiene un paraacutemetro de tipo Token mediante el que se puede co-municar la informacioacuten correspondiente a la pieza que se acaba de encontrar en la entrada analizada

Si una pieza sintaacutectica tiene asociada una accioacuten lexicograacutefica propia la accioacuten lexicograacutefica comuacuten (si es-taacute definida) se ejecutaraacute inmediatamente despueacutes de realizar la accioacuten propia

La accioacuten lexicograacutefica comuacuten se aplica a todas las piezas sintaacutecticas definidas como TOKEN bien sea no-minalmente (en las reglas lexicograacuteficas) o bien anoacutenimamente (incorporadas dentro de las reglas sintaacutecti-cas) sin embargo no se aplica cuando la secuencia acoplada se corresponde con una expresioacuten regular es-pecificada como SKIP (noacutetese que por el contrario una especificacioacuten SKIP siacute puede tener asociada una accioacuten lexicograacutefica propia)

El coacutedigo del meacutetodo CommonTokenAction se traslada como un componente maacutes del bloque de decla-raciones compartidas (TOKEN_MGR_DECLS) al principio de la clase del analizador lexicograacutefico

∆ Ejemplo 7

Se pretende ahora ampliar el ejemplo de presentacioacuten con la obtencioacuten de una relacioacuten numerada de los lexemas de las piezas sintaacutecticas encontradas en la entrada analizada en el caso de las variables y de las constantes antes del lexema se mostraraacute un indicativo (Var Const) por ejemplo si la expresioacuten de la entrada es

( a + 987 ) b

se grabaraacute la salida

( 1Var a 2+ 3Const 987 4) 5 6Var b 7 8

En la solucioacuten que se propone a continuacioacuten se muestran la definicioacuten de las opciones y las reglas sin-taacutecticas y lexicograacuteficas (en el resto no hay modificaciones) puede resaltarse lo siguiente- las llamadas al meacutetodo CommonTokenAction se incorporan automaacuteticamente no hay que incluir-las en la especificacioacuten como paraacutemetro de llamada a este meacutetodo siempre se emplea el objeto que re -presenta la pieza que se acaba de detectar- la accioacuten lexicograacutefica comuacuten realiza la actualizacioacuten del contador de piezas y la grabacioacuten del lexe-ma de la pieza detectada con su nuacutemero de orden- la accioacuten lexicograacutefica comuacuten tambieacuten se aplica a las piezas sintaacutecticas anoacutenimas (pareacutentesis y ope-radores) y a la pieza especial que representa el final del fichero (EOF)

- las piezas sintaacutecticas ltconstantegt y ltvariablegt tienen asociada una accioacuten lexicograacutefica propia que se ejecuta antes de la accioacuten lexicograacutefica comuacuten- los caracteres que se saltan (SKIP) no estaacuten afectados por la accioacuten lexicograacutefica comuacuten

options Ignore_Case = true Common_Token_Action = true

31

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarIndicativo(String indicativo) Systemoutprint(indicativo + )

static void grabarLexema (int num String lexema) Systemoutprint(lexema + + num + n)

static void CommonTokenAction(Token pieza) numero++ grabarLexema(numero piezaimage)

TOKEN lt variable [a-z] gt

grabarIndicativo(Var)

32

TOKEN lt constante ( [0-9] ) + gt

grabarIndicativo(Const)

SKIP | n | t | r

A modo de ejemplo de aplicacioacuten de la accioacuten lexicograacutefica comuacuten en lo que sigue se ofrece una solucioacuten para la grabacioacuten (en el fichero predefinido de salida) del texto analizado con las liacuteneas numeradas

∆ Ejemplo 8 [ Listado del texto analizado ]Se desea completar el ejemplo de presentacioacuten con la grabacioacuten de un listado con las liacuteneas numeradas del texto analizado (noacutetese que una expresioacuten puede escribirse ocupando varias liacuteneas) No se muestra la especificacioacuten sintaacutectica debido a que no hay ninguna modificacioacuten en ella Los aspectos resentildeables de la solucioacuten que se propone son- se usa la variable numLin como contador de liacuteneas

- en la variable linea se van yuxtaponiendo los lexemas de las piezas sintaacutecticas de una liacutenea esta yuxtaposicioacuten se realiza mediante la accioacuten lexicograacutefica comuacuten- las secuencias reconocidas con los patrones especificados como SKIP no implican la ejecucioacuten de la accioacuten lexicograacutefica comuacuten por ello su lexema (almacenado en la variable image) ha de yuxtaponer-se expliacutecitamente- hay que separar el patroacuten correspondiente al fin de liacutenea para detectar el momento en que ha de gra-barse la liacutenea completa cuyo final se ha alcanzado

options Ignore_Case = true Common_Token_Action = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(nnAnalisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

TOKEN_MGR_DECLS static int numLin = 0 static String linea =

33

static void CommonTokenAction(Token pieza) linea = linea + piezaimage

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP lt | t | r gt linea = linea + image

SKIP n numLin++ Systemoutprintln(numLin + + linea) linea =

A modo de recapitulacioacutenↄ Accioacuten lexicograacutefica Accioacuten sintaacutectica

bull accioacuten lexicograacutefica bloque de coacutedigo Java asociado a una pieza sintaacutectica nominal (TOKEN) o a una secuencia de caracteres (SKIP) se ejecuta cuando se detecta en la entrada la pieza o la secuencia corres-pondiente no se puede aplicar a piezas sintaacutecticas anoacutenimas (autodefinidas con comillas)bull accioacuten sintaacutectica bloque de coacutedigo Java asociado a un punto de la estructura sintaacutectica (el punto indica-do por el sitio de la produccioacuten donde se inserta el bloque) se ejecuta cuando el anaacutelisis de la entrada pa-sa por ese punto de la estructura

ↄ Valor de una pieza sintaacutectica comunicadaEl analizador lexicograacutefico comunica al analizador sintaacutectico una pieza a traveacutes de un valor de la clase Token (fichero Tokenjava) un objeto de la clase Token representa las caracteriacutesticas de la pieza comunicada En la clase Token entre otros se tienen los campos

String imagelexema de la pieza

int beginLine beginColumn endLine endColumnposicioacuten del lexema en el fichero analizado (indicada por los nuacutemeros de fila y de columna de su comienzo y de su terminacioacuten)

ↄ Valor asociado a un siacutembolo

34

En las producciones que especifican la sintaxis se pueden incluir asignaciones que sirven para dejar ano-tado el valor asociado a un siacutembolo

bull siacutembolo no terminal valor = nombreSimbolo()el meacutetodo de anaacutelisis asociado a nombreSimbolo se ha declarado de un cierto tipo (distinto del tipo void) valor ha de ser una variable del mismo tipo que el meacutetodo

bull siacutembolo terminal dato = lt nombrePieza gtel valor asignado es el valor de la clase Token representativo de la pieza sintaacutectica (nominal) dato ha de ser una variable de tipo Token

ↄ Miradas sobre la pieza sintaacutectica comunicadaLa pieza sintaacutectica comunicada puede mirarse desde cada uno de los dos analizadores

bull analizador sintaacutecticola pieza comunicada (desde el analizador lexicograacutefico) se tiene anotada en un campo de la clase del analizador sintaacutectico declarado como

(static) public Token token

este valor es accesible desde el coacutedigo de todos los meacutetodos de anaacutelisis sintaacutectico el contenido del campo variacutea cuando se recibe una nueva pieza sintaacutectica

bull analizador lexicograacuteficola pieza dispuesta para ser comunicada (al analizador sintaacutectico) se tiene anotada en una variable local declarada como

Token matchedToken

este valor es accesible para el coacutedigo de todas las acciones lexicograacuteficas

ↄ El lexema de la pieza comunicadaEl lexema de la pieza sintaacutectica comunicada se encuentra disponible en dos sitios distintos que compar-ten el mismo nombre pero que son de distinto tipo y se usan de distinta manera

bull String image

es un campo de la clase TOKEN puede consultarse en relacioacuten con un objeto de esa clase que esteacute disponible

bull StringBuffer image

es un campo de la clase del analizador lexicograacutefico ( ∙ ∙ ∙ ∙ TokenManagerjava) es accesible desde el coacutedigo de la clase y por lo tanto desde todas las acciones lexicograacuteficas (bloques de coacute-digo que estaacuten incorporados al analizador lexicograacutefico)

se trata de un valor disponible tanto para las piezas sintaacutecticas nominales (TOKEN) como para las secuencias que se saltan (SKIP)

ↄ Resumen relativo a la lexicografiacutea

TOKEN pieza sintaacutectica nominal∙ ∙ ∙ pieza sintaacutectica anoacutenimaSKIP secuencia de caracteres que no forman pieza

35

Precisiones sobre el analizador sintaacutectico generadoPuede decirse que en su funcionamiento maacutes sencillo cuando no se emplean las posibilidades de examen por adelantado (ldquolookaheadrdquo) JavaCC comprueba que las reglas sintaacutecticas cumplen la condicioacuten LL(1) y generan un analizador descendente-predictivo-recursivo Pero esta apreciacioacuten hay que matizarla ya que el analizador sintaacutectico generado no se ajusta en sentido estricto al modelo de analizador LL(1)

En el modelo de anaacutelisis LL(1) el orden del examen de las distintas alternativas de expansioacuten de un siacutembo-lo no terminal es indiferente ya que se conocen los siacutembolos directores de cada una de ellas (que resultan ser conjuntos disjuntos) en la implementacioacuten del subprograma de anaacutelisis asociado al siacutembolo no terminal ltNombreSimbgt cuyas producciones son

ltNombreSimbgt = α1

| α2

∙ ∙ ∙

| αn

se podriacutea preguntar en cualquier orden si la pieza actual pertenece al conjunto de siacutembolos directores de las distintas reglas Sin embargo en el analizador sintaacutectico generado por JavaCC la implementacioacuten del meacute-todo asociado al siacutembolo no terminal ltNombreSimbgt no sigue esa pauta

Si las anteriores reglas sintaacutecticas se transcriben en JavaCC de la forma

void nombreSimb () α1-jcc

| α2-jcc

∙ ∙ ∙

| αn-jcc

1

accioacutenlexicograacutefica

comuacuten

Commom -TokenAction2

accioacutenlexicograacutefica

propia

(accioacuten ligadaa una pieza)3

bloque dedeclaracioneslexicograacuteficas

TOKEN_MGR_DECLS4

declaracioneslexicograacuteficaspredefinidas

(disponibilidad

de uso)TOKENsisisisi∙ ∙ ∙sino--SKIPnosisisi

Las indicaciones si de las columnas 3 y 4 son consecuencia de las indicaciones de la columna 2

La accioacuten lexicograacutefica comuacuten se ejecuta despueacutes de la accioacuten lexicograacutefica propia

36

el meacutetodo de anaacutelisis generado procede de la siguiente manera

se van considerando las alternativas sucesivamente en el mismo orden en el que estaacuten especifica-das primero la de α1 (α1-jcc) despueacutes la de α2 (α2-jcc) etc

para cada alternativa seleccionada se comprueba si la pieza por adelantado coincide con alguno de los siacutembolos iniciales de la parte derecha y si hay coincidencia se prosigue el anaacutelisis con ella si no hay coincidencia se pasa a la alternativa siguiente

siguiendo con este orden si se llega al final de las alternativas sin haberse encontrado coincidencia alguna se produce un error sintaacutectico

En el analizador generado por JavaCC hay una peculiaridad que no se tiene en los analizadores LL(1) es-trictamente considerados la palabra vaciacutea siacute se considera como posible siacutembolo inicial de la parte derecha una regla (esto ocurre siempre que la parte derecha es anulable) De esta peculiaridad se deriva una conse-cuencia que ha de tenerse en cuenta al escribir la especificacioacuten dado que siempre es posible considerar la palabra vaciacutea como la siguiente pieza por adelantado si se llega a examinar la alternativa correspondiente a una produccioacuten anulable el analizador generado siempre seleccionaraacute esa alternativa con independencia de la siguiente pieza que esteacute presente en la entrada que queda por analizar

Asiacute pues si alguna de las producciones que definen un siacutembolo no terminal es anulable es importante el or-den en que se escriben las alternativas en una especificacioacuten sintaacutectica JavaCC el funcionamiento del ana-lizador generado siacute depende de la colocacioacuten elegida para las reglas

Para ilustrar estas peculiaridades del analizador sintaacutectico generado por JavaCC a continuacioacuten se expone un ejemplo

∆ Ejemplo 9

Se quiere obtener un analizador leacutexico-sintaacutectico de textos que se ajustan al siguiente formato una se-cuencia de uno o maacutes nombres seguida del siacutembolo igual seguido de un nuacutemero entero si la secuencia tiene maacutes de un nombre han de estar separados bien por una coma en todos los casos o bien por el siacutem-bolo dos puntos en todos los casos esto es son correctas las secuencias

uno dos tres = 123exclusivo = 0Abajo Arriba = 57

y son incorrectas las secuenciasuno dos tres = 123

exclusivo inclusive = 0

fuera dentro = 56

Una gramaacutetica que define las secuencias con este formato es

ltListagt = nombre ltOtrosNombresgt = numero

ltOtrosNombresgt = nombre nombre | nombre nombre | ε

se trata de una gramaacutetica que cumple la condicioacuten LL(1)- el siacutembolo director para la alternativa vaciacutea es el siacutembolo igual es evidente pues que las tres reglas de ltOtrosNombresgt tienen siacutembolos directores distintos

37

- la decisioacuten de abandonar la repeticioacuten del anaacutelisis de un nuevo nombre (en las dos primeras alterna-tivas de ltOtrosNombresgt) se puede tomar ante la presencia del siacutembolo igual

En la implementacioacuten de un analizador sintaacutectico LL(1) en sentido estricto la seleccioacuten de la alternativa vaciacutea para expandir el siacutembolo ltOtrosNombresgt se hariacutea tras comprobar expliacutecitamente que la pie-za sintaacutectica por adelantado es el siacutembolo igual

El funcionamiento del analizador generado por JavaCC depende del orden de colocacioacuten de las reglas A continuacioacuten se exponen varias posibilidades de especificacioacuten y se comenta el analizador generado en cada caso

bull Especificacioacuten primeraSe transcriben literalmente las reglas tal y como se han escrito antes en la notacioacuten BNF noacutetese que para poder codificar la parte derecha de la regla vaciacutea ha de incluirse una accioacuten sintaacutectica vaciacutea

PARSER_BEGIN (Igualdad)

public class Igualdad

public static void main (String[] argum) throws ParseException Igualdad analisis = new Igualdad(Systemin) analisissecuencia() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (Igualdad)

void secuencia() lista() ltEOFgt

void lista() lt nombre gt otrosNombres() = lt numero gt

void otrosNombres() lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt ) |

TOKEN lt nombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN lt numero ( [ 0 - 9 ] )+ gt

SKIP | r | n

38

El analizador generado funciona correctamente puesto que en el meacutetodo otrosNombres se selec-ciona la alternativa vaciacutea despueacutes de haber comprobado que la pieza por adelantado no es siacutembolo ini-cial de ninguna de las dos alternativas precedentes (no es una coma ni un siacutembolo dos puntos)

bull Especificacioacuten segundaSe considera la misma especificacioacuten anterior pero cambiando el orden de colocacioacuten de las reglas ahora la produccioacuten vaciacutea se coloca como primera alternativa (soacutelo se muestran las partes modifica-das)

void otrosNombres() | lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt )

Con la entrada de esta especificacioacuten el generador JavaCC produce un mensaje en el que se avisa de que las dos alternativas que estaacuten colocadas detraacutes de la alternativa vaciacutea nunca podraacuten seleccionarse aun asiacute se genera un analizador Pero es un analizador que funciona incorrectamente asiacute por ejemplo la entrada

mxa nyz = 57

se considera incorrecta despueacutes de visto el primer nombre se intenta aplicar la produccioacuten vaciacutea (se espera encontrar el siacutembolo igual) pero habriacutea que aplicar la regla cuyo siacutembolo director es la coma

bull Especificacioacuten terceraSe utilizan las posibilidades de la notacioacuten BNF-Ampliada para prescindir de la especificacioacuten expliacuteci-ta de la palabra vaciacutea se aprovecha que JavaCC admite el operador + para indicar repeticiones de una o maacutes veces la alternativa vaciacutea estaacute impliacutecitamente contemplada en la posibilidad de cero repeti-ciones que contempla el operador (soacutelo se muestran las partes modificadas en la especificacioacuten)

void otrosNombres() ( lt nombre gt )+ | ( lt nombre gt )

El meacutetodo otrosNombres() generado hace las comprobaciones en el siguiente orden

- se comprueba si la pieza por adelantado es una coma (siacutembolo inicial de la primera alternativa) y en este caso se selecciona la primera regla- si no es una coma se comprueba si es un siacutembolo dos puntos (siacutembolo inicial de la segunda alter-nativa) y en este caso se selecciona la segunda regla- si no es un siacutembolo dos puntos se considera la palabra vaciacutea como siacutembolo inicial (siempre posi-ble impliacutecitamente) para la segunda alternativa en este caso para que no haya error la pieza por adelantado ha de ser un siacutembolo igual

Se trata pues de un analizador que funciona correctamente

bull Especificacioacuten cuarta

39

Se pone la misma especificacioacuten anterior pero cambiando el orden de consideracioacuten de la regla vaciacutea ahora se incluye impliacutecitamente en la primera alternativa (soacutelo se muestran las partes modificadas)

void otrosNombres() ( lt nombre gt ) | ( lt nombre gt )+

Para esta especificacioacuten el generador JavaCC emite un aviso en el que se indica que la segunda alter -nativa nunca se seleccionaraacute no obstante se genera un analizador que no funciona como se supone que deberiacutea hacerlo por ejemplo el anaacutelisis de la entrada

nyz mxa = 1

produce un error despueacutes de tratado el primer nombre se intenta aplicar la produccioacuten vaciacutea (corres-pondiente a la repeticioacuten de cero veces contemplada en la primera regla) por lo que no se encuentra el esperado siacutembolo igual

40

Page 4: JavaCC

Documentacioacuten sobre JavaCC

Se dispone de abundante documentacioacuten relativa a JavaCC entre otras se pueden encontrar

documentacioacuten que acompantildea a los ficheros de la versioacuten instalada se tienen diversos ficheros enmiddotmiddotmiddotmiddot javacc-50dochtml

(en javaccgrmhtml se tiene una descripcioacuten general del generador)

documentacioacuten variada en la red

JavaCC DocumentationThe JavaCC TutorialIntroduction to JavaCCThe JavaCC FAQetc

Primera prueba con JavaCC

En lo que sigue se expone un ldquoejemplo de presentacioacutenrdquo completo la generacioacuten de un analizador leacutexico-sintaacutectico para un tipo de expresiones muy sencillo Se puede empezar el estudio de JavaCC probando su funcionamiento con ese ejemplo

loz Obtencioacuten de un analizador leacutexico-sintaacutectico

bull Pasos para la generacioacuten del analizador

1- Edicioacuten de la especificacioacuten (editor de texto plano)

vi | edit |∙ ∙ ∙ NombreFicherojj

(el nombre del fichero puede tener cualquier extensioacuten suele usarse jj)

2- Ejecucioacuten del generador

javacc NombreFicherojj

Si el nombre elegido para la especificacioacuten es NombreDeLaEspecif (maacutes adelante se indica la manera de dar un nombre a la especificacioacuten) como resultado de la generacioacuten se obtiene (ademaacutes de otros ficheros auxiliares) el fichero

NombreDeLaEspecifjava

3- Compilacioacuten del analizador generado

javac NombreDeLaEspecifjava

Como resultado de la compilacioacuten se obtiene (ademaacutes de otras clases auxiliares) el ficheroNombreDeLaEspecifclass

bull Ejecucioacuten del analizador generadoSi el nombre del fichero donde se encuentra el texto fuente (escrito en el lenguaje para el que se ha generado el analizador) que se pretende analizar es Programalen

java NombreDeLaEspecif lt Programalen

Si se desea que los resultados del anaacutelisis en vez de presentarse por pantalla queden grabados en un fi -chero de nombre Salidadat

java NombreDeLaEspecif lt Programalen gt Salidadat

3

loz Ejemplo de presentacioacuten

bull Descripcioacuten del lenguaje

El lenguaje L estaacute formado por las expresiones en las que pueden aparecer- variables- constantes- operadores + y

Las variables son nombres formados por una uacutenica letra (minuacutescula o mayuacutescula) las constantes son nuacute-meros enteros de una o maacutes cifras El espacio y el tabulador pueden estar presentes pero no tienen ninguacuten significado los finales de liacutenea tampoco son significativos (una expresioacuten puede codificarse ocupando una o maacutes liacuteneas)

La sintaxis de las expresiones se especifica mediante la siguiente gramaacutetica

ltExpresiongt = ltTerminogt + ltTerminogt ltTerminogt = ltFactorgt ltFactorgt ltFactorgt = variable | constante | ( ltExpresiongt )

bull Especificacioacuten leacutexico-sintaacutectica codificada con la notacioacuten JavaCCUna manera de escribir la especificacioacuten (para la que se ha elegido el nombre ExprMin) de forma que sea aceptada por el generador es

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Anaacutelisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

4

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP | t | n | r

bull Obtencioacuten del analizadorSi la especificacioacuten precedente se tiene grabada en un fichero de nombre Ejemplojj para obtener el analizador

- se ejecuta el generador javacc Ejemplojj

- se compila el analizador generado javac ExprMinjava

bull Ejecucioacuten del analizadorSi se quiere analizar una expresioacuten grabada en un fichero de nombre PruebaExptxt

- se ejecuta el analizador obtenido java ExprMin lt PruebaExptxt

loz Analizadores generadosEn su funcionamiento maacutes sencillo y habitual JavaCC genera un analizador sintaacutectico complementado con un analizador lexicograacutefico para que conjuntamente se pueda realizar un anaacutelisis leacutexico-sintaacutectico de un texto de entrada

El analizador sintaacutectico obtenido es en general LL(k) descendente y determinista con la consulta de k siacutembolos por adelantado si la gramaacutetica proporcionada cumple la condicioacuten LL(1) se genera un analiza -dor sintaacutectico descendente-predictivo-recursivo Maacutes adelante se hacen algunas precisiones sobre esta afir-macioacuten

Si la especificacioacuten leacutexico-sintaacutectica de un lenguaje codificada en JavaCC tiene dado (como indicativo que acompantildea a las palabras reservadas PARSER_BEGIN y PARSER_END) el nombre EspLexSin y se tiene grabada en un fichero de nombre Lenguajejj cuando se ejecuta el generador tomando como entrada ese fichero

javacc Lenguajejj

se obtienen los siguientes ficheros (clases) con coacutedigo Java

Tokenjavadescripciones para la comunicacioacuten entre los analizadores leacutexico y sintaacutectico

5

TokenMgrErrorjavatratamiento de errores para el anaacutelisis lexicograacutefico

ParseExceptionjavatratamiento de errores para el anaacutelisis sintaacutectico

SimpleCharStreamjavacomponentes para la realizacioacuten de las tareas de entradasalida del analizador

EspLexSinConstantsjavadefinicioacuten de la representacioacuten interna de las piezas sintaacutecticas

EspLexSinTokenManagerjavaanalizador lexicograacutefico

EspLexSinjavaanalizador sintaacutectico

Puede apreciarse que hay dos categoriacuteas de nombres de ficheros generados los cuatro primeros nombres citados no dependen del nombre de la especificacioacuten considerada los otros nombres de ficheros se forman a partir del nombre dado a la especificacioacuten

Forma de una especificacioacuten JavaCC (versioacuten simplificada)La forma que se describe en lo que sigue es una versioacuten simplificada el generador JavaCC admite especificaciones con otras muchas posi-bilidades no mencionadas en esta introduccioacuten

Una especificacioacuten para el generador JavaCC puede considerarse dividida en cuatro secciones

loz Seccioacuten de opcionesEn esta seccioacuten cuya presencia es optativa se pueden asignar valores a diversos paraacutemetros (llamados op-ciones) que sirven para configurar ciertas caracteriacutesticas del funcionamiento del generador o del analizador generado Cada paraacutemetro (opcioacuten) tiene un valor por defecto que es el que toma cuando no se le asigna expliacutecitamente un valor Los valores de las opciones tambieacuten se pueden fijar en la liacutenea de comandos

Seccioacuten de opciones

Seccioacuten de ejecucioacuten

Seccioacuten de sintaxis

Seccioacuten de lexicografiacutea

6

cuando se ejecuta el generador (lo indicado en la liacutenea de comandos tiene prioridad sobre lo especificado en esta seccioacuten de opciones)

Algunas de las opciones para las que se puede fijar un valor son

Ignore_Case (valor por defecto false)

indica si en el texto analizado ha de distinguirse o no entre letras minuacutesculas y mayuacutesculas

Build_Parser (valor por defecto true)

indica si se genera el analizador sintaacutectico o no

Build_Token_Manager (valor por defecto true)

indica si se genera el analizador lexicograacutefico o no

Sanity_Check (valor por defecto true)

indica si se realizan comprobaciones sobre la gramaacutetica sintaacutectica

Debug_Parser (valor por defecto false)

indica si se genera una traza para el anaacutelisis leacutexico-sintaacutectico

Error_Reporting (valor por defecto true)

indica si los mensajes de error emitidos son maacutes o menos explicativos

Static (valor por defecto true)

con el valor por defecto los meacutetodos de los analizadores leacutexico y sintaacutectico se generan con el des-criptor estaacutetico (static)

Los nombres de las opciones pueden escribirse con letras minuacutesculas o mayuacutesculas los valores de las op-ciones son en la mayoriacutea de los casos un valor entero o un valor loacutegico Si se incluye la seccioacuten se empie-za poniendo la palabra reservada options en este caso al menos ha de ponerse una opcioacuten

La forma de esta seccioacuten es

options nombreOpcion1 = valorOpcion1

nombreOpcion2 = valorOpcion2

∙ ∙ ∙ ∙

nombreOpcionk = valorOpcionk

loz Seccioacuten de ejecucioacutenEn esta seccioacuten se pone el coacutedigo Java que contiene la llamada al analizador generado para que se realice el anaacutelisis de un determinado texto de entrada Tambieacuten se establece aquiacute el nombre de la especificacioacuten que es el nombre que se toma para formar los nombres de parte de los ficheros (clases) generados

La seccioacuten estaacute delimitada por dos palabras reservadas ambas acompantildeadas por un mismo nombre (puesto entre pareacutentesis) ese nombre es el que se da a la especificacioacuten Entre esas dos palabras ha de ponerse una clase directora para el proceso de anaacutelisis el nombre de esa clase directora ha de coincidir con el nombre dado a la especificacioacuten

PARSER_BEGIN (NombreDeLaEspecif)

7

∙ ∙ ∙ ∙

public class NombreDeLaEspecif

∙ ∙ ∙ ∙

∙ ∙ ∙ ∙

PARSER_END (NombreDeLaEspecif)

Una posible versioacuten sencilla de la clase directora es

public class NombreDeLaEspecif public static void main (String[] argum) throws ParseException NombreDeLaEspecif anLeSi = new NombreDeLaEspecif (Systemin) anLeSisimboloPrograma()

se crea el objeto anLeSi para poder aplicar los meacutetodos de anaacutelisis se asocia la entrada standard ( Systemin ) a la entrada para el analizador generado se aplica al objeto creado el meacutetodo de anaacutelisis correspondiente al siacutembolo inicial de la gramaacutetica

loz Seccioacuten de sintaxisEn esta seccioacuten se describe la sintaxis del lenguaje para el que se desea generar el analizador usaacutendose pa-ra ello una notacioacuten parecida a la BNF En lo que sigue se expone la forma de las producciones tal y como se escriben en JavaCC ponieacutendolas en comparacioacuten con las producciones de la notacioacuten BNF-Ampliada

bull Reglas sintaacutecticasEl conjunto de reglas que definen un siacutembolo no terminal

ltNombreSimbgt = α1

| α2

∙ ∙ ∙

| αn

se codifica de la siguiente manera

void nombreSimb () α1-jcc

| α2-jcc

∙ ∙ ∙

| αn-jcc

8

- el nombre del siacutembolo no terminal (parte izquierda de las reglas) se escribe como el encabeza-miento de un meacutetodo en Java sin argumentos y sin valor de devolucioacuten (meacutetodo de tipo void) se sigue como en Java la costumbre de escribir los nombres de los meacutetodos empezando con letra mi-nuacutescula

- el siacutembolo dos puntos representa la separacioacuten entre las partes izquierda y derechas de las reglas (en vez del siacutembolo = de la notacioacuten BNF)

- el siacutembolo barra vertical representa la separacioacuten entre distintas partes derechas con la misma parte izquierda (igual que en la notacioacuten BNF)

- despueacutes del siacutembolo dos puntos se pone un bloque de coacutedigo Java este coacutedigo se suele dedicar a la realizacioacuten de tareas semaacutenticas si se pretende hacer soacutelo un anaacutelisis sintaacutectico el bloque que-daraacute vaciacuteo (aunque siempre es obligada su presencia)

- el conjunto de las alternativas (partes derechas de las reglas) estaacute delimitado mediante llaves como si constituyese un bloque de coacutedigo Java

- αi-jcc representa la codificacioacuten de una alternativa (parte derecha) empleaacutendose una nota-cioacuten que se describe a continuacioacuten

bull Siacutembolos no terminales en las partes derechasEn la notacioacuten BNF se pone ltNombreSimbgt en la notacioacuten JavaCC se escribe

nombreSimb()

esto es se pone como si fuese una llamada en Java a un meacutetodo sin argumentos

bull Siacutembolos terminales en las partes derechasSe considera una clasificacioacuten en las piezas sintaacutecticas (siacutembolos terminales de una gramaacutetica sintaacutectica) distinguieacutendose entre piezas sintaacutecticas nominales y anoacutenimas

son nominales las piezas sintaacutecticas a las que se asocia un nombre en otra parte de la especificacioacuten (maacutes adelante se describe la manera de indicar esta asociacioacuten) ese nombre asociado a la pieza es el que se usa al escribir las partes derechas de las producciones En la notacioacuten JavaCC un siacutembolo terminal no-minal se escribe poniendo el nombre delimitado por los caracteres lt y gt

ltnombreTerminalgt

son anoacutenimas las piezas sintaacutecticas que no tienen nombre asociado no se precisa porque la pieza se re -presenta por su propia secuencia de caracteres (lexema) En la notacioacuten JavaCC un siacutembolo terminal anoacutenimo se escribe poniendo su lexema delimitado por comillas por ejemplo

= lt lt= END

bull Metasiacutembolos de opcionalidad y de repeticioacutenEn la notacioacuten BNF se emplean los corchetes para indicar opcionalidad en la notacioacuten JavaCC se utilizan tambieacuten esos mismos siacutembolos

[ α ] en ambas notaciones

En la notacioacuten BNF se emplean las llaves para indicar una repeticioacuten de cero o maacutes veces en la notacioacuten JavaCC se utilizan los pareacutentesis y un asterisco detraacutes del pareacutentesis de cerrar

α en notacioacuten BNF-Ampliada

( α ) en notacioacuten JavaCC

bull Otros metasiacutembolos de la notacioacuten JavaCC

9

La opcionalidad tambieacuten se puede indicar empleando el metasiacutembolo de interrogacioacuten

( α )

Para indicar repeticioacuten de una o maacutes veces se utiliza el metasiacutembolo operador aditivo suma

( α )+

bull Control del final de fichero de entradaPara controlar el final del fichero en el texto analizado ha de antildeadirse una regla sintaacutectica especial que tenga (seguacuten la notacioacuten BNF) la forma

ltProgramaCompletogt = ltProgramagt FF

donde

ltProgramaCompletogt es un nuevo siacutembolo (no terminal) auxiliar antildeadido

ltProgramagt es el siacutembolo inicial de la gramaacutetica sintaacutectica

FF es una representacioacuten del final del fichero

En JavaCC el final de fichero se escribe con la notacioacuten ltEOFgt siendo EOF una palabra reservada la regla sintaacutectica especial antildeadida a la especificacioacuten quedariacutea codificada asiacute

void programaCompleto() programa() ltEOFgt

bull Representacioacuten de la produccioacuten vaciacuteaLas producciones vaciacuteas (reglas sintaacutecticas cuya parte derecha es la palabra vaciacutea ε ) no se pueden repre-sentar expliacutecitamente en una especificacioacuten leacutexico-sintaacutectica escrita en JavaCC Para indicar la presencia de una produccioacuten vaciacutea han de aplicarse las posibilidades de la notacioacuten BNF-Ampliada seguacuten esta nota-cioacuten cuando se indica la opcionalidad de un componente se estaacute especificando impliacutecitamente la palabra vaciacutea (en el caso de que el componente no esteacute presente)

Asiacute pues si se quisiera escribir en JavaCC la construccioacuten sintaacutectica expresada por las reglas

ltComponentegt = α

| ε

deberiacutean de considerarse con la notacioacuten equivalente

ltComponentegt = [ α ]

y si hubiera varias alternativas no vaciacuteas

ltComponentegt = α | β

| ε

habriacutea que considerarlas como

ltComponentegt = [ α | β ]

o tambieacuten podriacutea verse como

ltComponentegt = α

10

| [ β ]

Como ejemplo sea la especificacioacuten sintaacutectica de una llamada a una funcioacuten que puede tener cero o maacutes paraacutemetros separados por comas aunque no haya paraacutemetros los pareacutentesis estaacuten presentes Las siguien-tes reglas definen esta estructura

ltLlamadagt = id ( ltParametrosgt )

ltParametrosgt = ltExpresiongt ltExpresiongt | ε

En JavaCC la definicioacuten del siacutembolo ltParametrosgt se puede escribir como sigue (noacutetese que aquiacute los pareacutentesis empleados para aplicar el operador son metasiacutembolos)

void parametros() [ expresion() ( expresion() ) ]

loz Seccioacuten de lexicografiacuteaEn esta seccioacuten se indica la lexicografiacutea del lenguaje para el que se va a generar el analizador la notacioacuten de JavaCC que representa la forma de cada una de las piezas sintaacutecticas es una variante de las bien conoci-das expresiones regulares

bull El nombre de las piezas sintaacutecticas nominalesPara asociar un nombre a una pieza sintaacutectica y para al mismo tiempo definir la forma de la pieza se em-plea la notacioacuten

TOKEN lt nombreTerminal Expresioacuten-Regular gt

- TOKEN es una palabra reservada detraacutes de ella se pone el separador dos puntos

- nombreTerminal representa el nombre que se quiere asociar a la pieza sintaacutectica

- Expresioacuten-Regular indica una expresioacuten regular que especifica la forma de la pieza

- el nombre y la expresioacuten se separan mediante el caraacutecter dos puntos y conjuntamente se deli -mitan mediante los caracteres lt y gt a su vez toda la asociacioacuten se delimita con llaves

bull Expresiones regulares en JavaCCLas expresiones regulares en una especificacioacuten JavaCC se escriben de manera parecida a la forma utiliza-da en otras conocidas notaciones aunque con ciertas peculiaridades A continuacioacuten se mencionan los as-pectos maacutes significativos de las expresiones regulares JavaCC y algunos ejemplos

Los operadores de las expresiones son

| unioacuten

repeticioacuten cero o maacutes veces

+ repeticioacuten una o maacutes veces

opcionalidad

11

Los pareacutentesis permiten fijar el orden de aplicacioacuten de los operadores Una peculiaridad de las expresiones regulares JavaCC es que los operadores de repeticioacuten y de opcionalidad requieren que el componente al que se aplican esteacute en todo caso delimitado por pareacutentesis

Se pueden citar las siguientes caracteriacutesticas baacutesicas de las expresiones regulares en JavaCC

Se tiene la posibilidad de definir conjuntos de caracteres poniendo entre corchetes la relacioacuten de carac-teres separados por comas y cada caraacutecter delimitado por comillas En un conjunto se puede fijar un ran-go de caracteres poniendo el primero y el uacuteltimo separados por el siacutembolo guioacuten (-) A veces resulta maacutes coacutemodo indicar los caracteres que no pertenecen al conjunto para ello se puede definir el comple -mentario de un conjunto poniendo el siacutembolo ~ delante del corchete inicial

Para indicar que una secuencia de caracteres colocados consecutivamente constituye por siacute misma una expresioacuten regular (o un componente de una expresioacuten) se escribe la secuencia delimitada por comillas El siacutembolo se emplea como metacaraacutecter para indicar que el siacutembolo que estaacute inmediatamente a su derecha se considere como caraacutecter del alfabeto no como posible metacaraacutecter (operador delimitador)

En una expresioacuten regular se pueden incluir como si fueran caracteres las secuencias de escape con la misma notacioacuten y el mismo significado que en Java o en C

Noacutetese que en una produccioacuten sintaacutectica la opcionalidad se puede especificar de dos formas con corchetes o con el siacutembolo de interrogacioacuten sin embargo en una expresioacuten regular la opcionalidad soacutelo se puede es-pecificar mediante el siacutembolo de interrogacioacuten (los corchetes en las expresiones regulares son para definir conjuntos de caracteres)

A continuacioacuten se muestran ejemplos de expresiones regulares escritas seguacuten la notacioacuten JavaCC

ENDsecuencia de las tres letras mayuacutesculas que conforman la palabra END

whilesecuencia de las cinco letras minuacutesculas que conforman la palabra while

ncaraacutecter representativo del fin de liacutenea (secuencia de escape)

rcaraacutecter representativo del retorno de carro (secuencia de escape)

caraacutecter dos puntos

=secuencia de dos caracteres (siacutembolo de asignacioacuten en Pascal)

gtcaraacutecter representativo del operador de relacioacuten mayor

(i | I)(f | F)palabra ldquoifrdquo escrita con letras minuacutesculas o mayuacutesculas (cuatro combinaciones)

if | IFpalabra ldquoifrdquo escrita soacutelo con minuacutesculas o soacutelo con mayuacutesculas (dos combinaciones)

12

u | o | i | e | auna cualquiera de las vocales minuacutesculas (pero soacutelo una de ellas)

[ A E I O U ]una cualquiera de las vocales mayuacutesculas (pero soacutelo una de ellas)

[ a - z ntilde ]una cualquiera de las letras minuacutesculas incluyendo la letra ntilde

[ a - z A - Z 0 - 9 ]una cualquiera de las letras o de las cifras decimales

([ 0 - 9 ])+constante entera (nuacutemero de una o maacutes cifras)

noacutetese que es incorrecto poner [ 0 - 9 ]+

( + | - )([ 0 - 9 ])+constante entera precedida opcionalmente por un signo

noacutetese que la primera cifra ha de seguir inmediatamente al signo (si existe)

([+ -])([ 0 - 9 ])+representa lo mismo que la expresioacuten anterior

[ a - z ] ([ a - z 0 - 9 ])una letra minuacutescula seguida de cero o maacutes letras minuacutesculas o cifras decimales

~[a - z ntilde A - Z Ntilde]cualquier caraacutecter que no sea una letra

noacutetese que la expresioacuten representa un uacutenico caraacutecter

~[n r]cualquier caraacutecter (un uacutenico caraacutecter) excepto el fin de liacutenea y el retorno de carro

~[ ]cualquier caraacutecter del alfabeto (un uacutenico caraacutecter)

([ 0 - 9 ])+ ( ([ 0 - 9 ])+)constante aritmeacutetica con parte decimal opcional

Los modernossecuencia de catorce caracteres Los modernos

noacutetese el uso de para imponer la condicioacuten de caraacutecter del siacutembolo comillas

nsecuencia de cuatro caracteres n

noacutetese que aquiacute no se representa la secuencia de escape

bull Piezas sintaacutecticas internas (ldquoprivaterdquo)

13

Es posible definir piezas sintaacutecticas auxiliares llamadas ldquointernasrdquo porque no se comunican al analizador sintaacutectico sino que soacutelo sirven para facilitar la escritura de la forma de las piezas sintaacutecticas que siacute se co-munican con el uso de las piezas internas es posible evitar repeticiones de escritura y hacer maacutes legible la especificacioacuten de las caracteriacutesticas lexicograacuteficas Las piezas internas permiten dar nombre a subexpre-siones regulares que a su vez pueden emplearse como componentes para escribir expresiones maacutes com-plejas que incorporan esos nombres Para indicar que una pieza sintaacutectica se considere como interna se pone el siacutembolo delante del nombre asociado a la pieza

A continuacioacuten se muestra un ejemplo tiacutepico de utilizacioacuten de las piezas internas en una especificacioacuten le-xicograacutefica en JavaCC Se quieren definir tres piezas sintaacutecticas que representan un nombre una constante entera y una constante decimal una posible especificacioacuten es

TOKEN ltnombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN ltcteEntera ( [ 0 - 9 ] )+ gt

TOKEN ltcteDecimal ( [ 0 - 9 ] )+ ( [ 0 - 9 ] )+ gt

Con la inclusioacuten de tres piezas sintaacutecticas internas auxiliares esta misma especificacioacuten podriacutea escribirse de la siguiente manera

TOKEN lt letra [ a - z A - Z ntilde Ntilde ] gt

TOKEN lt cifra [ 0 - 9 ] gt

TOKEN lt numero ( ltcifragt )+ gt

TOKEN lt nombre ( ltletragt )+ gt

TOKEN lt cteEntera ltnumerogt gt

TOKEN lt cteDecimal ltnumerogt ltnumerogt gt

Los nombres letra cifra y numero son exclusivos (internos) del analizador lexicograacutefico y no se pueden emplear como nombres de piezas en la especificacioacuten de las reglas sintaacutecticas cuando esos nom-bres se usan en la definicioacuten de otras piezas han de escribirse delimitados por los siacutembolos lt y gt

bull Distincioacuten restringida entre minuacutesculas y mayuacutesculasSe tiene la opcioacuten Ignore_Case para determinar si en el anaacutelisis del texto de entrada se distingue entre las letras minuacutesculas y las mayuacutesculas el valor por defecto false indica que siacute se diferencian si se asig-na el valor true a esta opcioacuten entonces no se diferencian en ninguacuten caso con independencia de que en los patrones que se definan para las piezas sintaacutecticas se pongan minuacutesculas o mayuacutesculas

Pero a veces conviene que la diferenciacioacuten entre minuacutesculas y mayuacutesculas no se aplique de manera ge-neral a toda la entrada sino en particular a las secuencias de caracteres que se acoplen a ciertos patrones Para este fin existe la posibilidad de establecer que en la expresioacuten regular que define el patroacuten de una determinada pieza sintaacutectica no se diferencie entre minuacutesculas y mayuacutesculas ello se consigue poniendo

14

[ IGNORE_CASE ]

inmediatamente detraacutes de la palabra TOKEN con la que empieza la especificacioacuten de la pieza sintaacutectica en la que se pretende la no distincioacuten entre minuacutesculas y mayuacutesculas

Noacutetese que ignore_case tiene dos significados

- es el nombre de un paraacutemetro de la seccioacuten de opciones (puede ponerse en minuacutesculas o mayuacutesculas)- es una palabra reservada que puede aplicarse (escribieacutendola entre corchetes y en mayuacutesculas) a la de -finicioacuten de una pieza sintaacutectica

Por ejemplo sea un lenguaje que en general distingue entre minuacutesculas y mayuacutesculas pero en la codifi -cacioacuten del exponente de una constante real exponencial puede ponerse la letra e en minuacutescula o en ma-yuacutescula En la especificacioacuten JavaCC para ese lenguaje la opcioacuten Ignore_Case ha de tomar el valor false en la definicioacuten de una constante exponencial son posibles las siguientes formas equivalentes

1) sin hacer uso de IGNORE_CASE

TOKEN lt cifras ( [ 0 - 9 ] )+ gt

TOKEN lt signo [+ -] gt

TOKEN lt constante

ltcifrasgt ( ltcifrasgt) [e E] (ltsignogt) ltcifrasgt gt

2) aplicando IGNORE_CASE

TOKEN lt cifras ( [ 0 - 9 ] )+ gt

TOKEN lt signo [+ -] gt

TOKEN [IGNORE_CASE] lt constante

ltcifrasgt ( ltcifrasgt) e (ltsignogt) ltcifrasgt gt

bull Secuencias de caracteres sin efecto en la estructura sintaacutecticaEn los lenguajes de programacioacuten suele haber secuencias de caracteres que no forman parte de la estructu-ra sintaacutectica de un programa esas secuencias son detectadas por el analizador lexicograacutefico pero no son comunicadas al sintaacutectico no han de considerarse como piezas sino que han de saltarse En JavaCC se pueden definir estas secuencias mediante la palabra reservada SKIP la notacioacuten de una especificacioacuten con la palabra SKIP de una secuencia que ha de saltarse es la misma que la notacioacuten usada para las espe-cificaciones con la palabra TOKEN de las piezas que han de comunicarse

Los dos casos maacutes habituales de secuencias que no se comunican al analizador sintaacutectico son

a) los caracteres separadores que no tienen significado en el texto analizadoen los lenguajes de codificacioacuten en formato libre hay caracteres del texto fuente que han de ignorarse en la mayoriacutea de los casos estos caracteres son el espacio en blanco el tabulador el retorno de carro y el fin de liacutenea esta caracteriacutestica se especifica

SKIP |t | r |n

b) los comentarios

15

en el anaacutelisis sintaacutectico de un programa los comentarios han de ignorarse una manera de especificar esta circunstancia es poniendo en una definicioacuten SKIP la expresioacuten regular que denota la forma de los comentarios del lenguaje por ejemplo para los comentarios de una liacutenea de Java se pondriacutea

SKIP

lt (~ [n r]) gt

Tareas asociadas a la estructura sintaacutecticaJavaCC genera un analizador sintaacutectico descendente recursivo constituido por un conjunto de meacutetodos ca-da siacutembolo no terminal de la gramaacutetica sintaacutectica tiene asociado un meacutetodo de anaacutelisis (el meacutetodo tiene el mismo nombre que el siacutembolo correspondiente) Un meacutetodo de anaacutelisis ademaacutes de la realizacioacuten de la parte del anaacutelisis sintaacutectico que le corresponde puede llevar a cabo otras tareas complementarias que sirvan para efectuar un anaacutelisis maacutes complejo del texto de entrada

loz Bloque para un siacutemboloAl describir la forma de las reglas sintaacutecticas de un siacutembolo no terminal en una especificacioacuten JavaCC ya se ha visto que detraacutes de la definicioacuten del nombre del siacutembolo y antes de empezar con las diferentes alter-nativas que tiene asociadas se incluye un bloque de coacutedigo Java (en el ejemplo de presentacioacuten este bloque estaacute vaciacuteo en todos los siacutembolos no terminales de la gramaacutetica) Cada bloque situado en esa posicioacuten puede considerarse ligado al siacutembolo no terminal en cuya descripcioacuten estaacute incluido el coacutedigo Java del bloque se traslada literalmente al principio del meacutetodo generado correspondiente al siacutembolo no terminal y por lo tan-to es lo primero que se considera (se ejecuta) cuando en el transcurso del proceso de anaacutelisis se realiza una llamada al meacutetodo Asiacute pues cada siacutembolo no terminal siempre tiene asociado un uacutenico bloque de coacute-digo Java ndashbloque de presencia obligada aunque pueda estar vaciacuteo- en este bloque se pueden poner de-claraciones y sentencias

loz Acciones sintaacutecticasAdemaacutes del bloque asociado a cada uno de los siacutembolos no terminales de la gramaacutetica se pueden incluir otros bloques de coacutedigo Java intercalados en cualquiera de las partes derechas de las reglas sintaacutecticas Los bloques incluidos en las partes derechas se denominan ldquoacciones sintaacutecticasrdquo (parser actions) ya que son trozos de coacutedigo que pueden considerarse asociados a los puntos de la estructura sintaacutectica donde estaacuten co-locados Una accioacuten sintaacutectica se puede poner en cualquier posicioacuten al principio al final o en el medio de la parte derecha de una regla precedida o seguida de siacutembolos terminales o no terminales el coacutedigo Java de una accioacuten sintaacutectica se traslada literalmente al meacutetodo asociado al siacutembolo no terminal de la parte izquierda de la regla de manera que se ejecuta cada vez que el analizador transcurre por el punto de la estructura sintaacutectica donde se ha incorporado la accioacuten (esto es las acciones sintaacutecticas se incorporan al coacutedigo del analizador sintaacutectico) No obstante su nombre en los bloques de las acciones sintaacutecticas suele ponerse coacutedigo dedicado a la realizacioacuten de tareas semaacutenticas

Si en el bloque asociado a un siacutembolo no terminal se ponen declaraciones los nombres ahiacute definidos son accesibles en todas las acciones sintaacutecticas intercaladas en las partes derechas de las reglas de ese siacutembolo la justificacioacuten es sencilla el coacutedigo del bloque asociado al siacutembolo se incluye al principio de un meacutetodo y el coacutedigo de las acciones pertenece a ese mismo meacutetodo

∆ Ejemplo 1

Se considera de nuevo el ejemplo de presentacioacuten ahora se pretende completar el anaacutelisis leacutexico-sintaacutec-tico con la realizacioacuten de las siguientes tareas

- cada vez que se analiza una expresioacuten un teacutermino o un factor se graba el nombre del siacutembolo no terminal correspondiente y

16

- cuando se analiza un teacutermino en el que hay operadores multiplicativos se cuenta su nuacutemero y se graba el resultado (noacutetese que se cuentan los operadores de cada teacutermino pero no se acumula la cuenta de todos los operadores de la expresioacuten)

Para la realizacioacuten de estas tareas se antildeaden bloques de coacutedigo Java En la especificacioacuten que se describe a continuacioacuten puede apreciarse- para los siacutembolos no terminales expresion termino y factor se ha incluido en los blo-ques correspondientes la operacioacuten de grabacioacuten de su nombre que se ejecutaraacute cada vez que se efec-tuacutee una llamada al meacutetodo- en el bloque asociado a termino se tiene la declaracioacuten del contador de operadores (que seraacute una variable local del meacutetodo)- en la parte derecha de la regla que define la estructura de un teacutermino se han incluido dos acciones sintaacutecticas una para contar la cantidad de operadores (situada detraacutes de la pieza sintaacutectica del opera-dor) y otra para grabar la cantidad de operadores del teacutermino (situada al final de la parte derecha)- se ha definido el meacutetodo grabar para facilitar la escritura del coacutedigo Java

options Ignore_Case = true

PARSER_BEGIN (ExprMin) public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

private static void grabar(String nombre) Systemoutprintln ( -gt + nombre + n)

PARSER_END (ExprMin)

void unaExpresion()

expresion() ltEOFgt

void expresion() grabar(Expresion)

termino() ( + termino() )

void termino() int nAst = 0 grabar(Termino)

17

factor() ( nAst++ factor() ) if (nAst gt 0) Systemoutprintln (n Asteriscos + nAst + n)

void factor() grabar(Factor)

ltconstantegt | ltvariablegt | ( expresion() )

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP | t | n | r

Cuando se incluyen acciones sintaacutecticas la legibilidad del aspecto sintaacutectico de la especificacioacuten queda oscurecida para paliar ese inconveniente puede antildeadirse un comentario que muestre la regla sintaacutectica escueta sin las acciones por ejemplo podriacutea escribirse

void termino() int nAst = 0 grabar(Termino)

factor() ( factor() )

factor()

( nAst++ factor() )

if (nAst gt 0) Systemoutprintln (n Asteriscos + nAst + n)

loz Valor comunicado por un meacutetodoEn todo lo visto hasta ahora los meacutetodos de anaacutelisis sintaacutectico generados por JavaCC no devuelven valor alguno al terminar su tarea de anaacutelisis ya que en la especificacioacuten siempre se ha declarado el nombre de un meacutetodo precedido de void (son meacutetodos de tipo void) Pero en general un meacutetodo de anaacutelisis sintaacutec-tico puede ser de cualquier tipo (primitivo o no) por ello un meacutetodo puede devolver un valor de cualquier tipo (de cualquier clase) al meacutetodo desde el que se efectuoacute la llamada

Esta posibilidad de devolucioacuten de valores tiene una utilidad fundamental en la implementacioacuten de analiza-dores de lenguajes permite la comunicacioacuten de datos (atributos de las entidades analizadas) entre los dife -rentes meacutetodos que colaboran en el anaacutelisis del programa completo

En una especificacioacuten JavaCC la llamada a un meacutetodo ocurre cuando en la parte derecha de una regla estaacute el nombre del siacutembolo no terminal asociado al meacutetodo Cuando un meacutetodo devuelve un valor (tiene un tipo distinto de void) es habitual la recepcioacuten del valor devuelto en una variable en la propia especificacioacuten

18

se puede indicar el nombre de la variable a la que se asignaraacute el valor devuelto tras la llamada para ello se emplea una notacioacuten ideacutentica a la forma que tienen las sentencias de asignacioacuten en el lenguaje Java

dato = nombreSimb()

donde dato es el nombre de la variable a la que se asigna el valor devuelto por el meacutetodo llamado esa va -riable ha de estar oportunamente declarada del mismo tipo que dicho meacutetodo

∆ Ejemplo 2

En el mismo ejemplo de presentacioacuten se desea ademaacutes del anaacutelisis leacutexico-sintaacutectico de la entrada contar la cantidad de operadores que hay en la expresioacuten A continuacioacuten se expone una especificacioacuten JavaCC que es una posible solucioacuten soacutelo se muestran las reglas sintaacutecticas (el resto de la especificacioacuten es igual que en el ejemplo de presentacioacuten)

En la especificacioacuten que se propone puede apreciarse- los siacutembolos no terminales expresion termino y factor se han declarado de tipo int los meacutetodos correspondientes devuelven un valor de tipo entero- el valor devuelto por los meacutetodos sirve para propagar a traveacutes de la estructura sintaacutectica la cuenta de los operadores que hay en el texto analizado- por tratarse de meacutetodos que devuelven un valor es preciso incluir las correspondientes sentencias de devolucioacuten return

void unaExpresion() int nOper nOper = expresion() ltEOFgt Systemoutprintln (Cantidad de operadores = + nOper)

int expresion() int n i n = termino() ( + n++ i = termino() n = n + i )

return n

int termino() int n i n = factor() ( n++ i = factor() n = n + i )

return n

int factor() int n ltconstantegt return 0 | ltvariablegt return 0 | ( n = expresion() ) return n

La especificacioacuten de un factor tambieacuten podriacutea escribirse de esta otra manera

19

int factor() int n = 0

( ltconstantegt | ltvariablegt | ( n = expresion() ) )

return n

A continuacioacuten se expone un nuevo ejemplo para ilustrar la utilidad que proporciona el valor devuelto por un meacutetodo de anaacutelisis sintaacutectico para la propagacioacuten (ascendente) de atributos (propiedades de las entida-des analizadas)

∆ Ejemplo 3

Se pretende ampliar el analizador del ejemplo de presentacioacuten para que se emita un mensaje indicativo cuando la expresioacuten analizada sea una expresioacuten en la que soacutelo aparecen constantes en la solucioacuten que se muestra soacutelo se incluyen las reglas sintaacutecticas (en el resto de la especificacioacuten no hay modificacio-nes)

void unaExpresion() boolean esCte

expresion() ltEOFgt

esCte = expresion() ltEOFgt

if (esCte) Systemoutprintln(nExpresion constanten)

boolean expresion() boolean esCte esTambienCte

termino() ( + termino() )

esCte = termino() ( + esTambienCte = termino() esCte = esCte ampamp esTambienCte )

return esCte

boolean termino() boolean esCte esTambienCte

factor() ( factor() )

esCte = factor() ( esTambienCte = factor() esCte = esCte ampamp esTambienCte )

return esCte

20

boolean factor() boolean esCte

ltconstantegt return true | ltvariablegt return false | ( esCte = expresion() ) return esCte

bull Reconsideracioacuten sobre las producciones vaciacuteasAnteriormente se ha comentado que para representar una produccioacuten vaciacutea en una especificacioacuten JavaCC hay que hacer uso de los metasiacutembolos de opcionalidad para ilustrar esta idea se ha incluido como ejem-plo la especificacioacuten de una lista de paraacutemetros compuesta por cero o maacutes expresiones separadas entre siacute por el caraacutecter coma

void parametros() [ expresion() ( expresion() ) ]

Pero si se aprovecha la posibilidad de incorporar acciones sintaacutecticas se tiene una manera de especificar directamente producciones vaciacuteas para conseguirlo basta considerar una produccioacuten vaciacutea (no tiene nada en su parte derecha) acompantildeada de una accioacuten sintaacutectica cuyo bloque de coacutedigo esteacute vaciacuteo Seguacuten esta posibilidad la especificacioacuten anterior podriacutea tambieacuten escribirse asiacute

void parametros() expresion() ( expresion() ) |

En general la accioacuten sintaacutectica asociada a una produccioacuten vaciacutea no tiene por queacute ser un bloque vaciacuteo de coacutedigo por ejemplo si se quiere que el meacutetodo que analiza los paraacutemetros de una llamada devuelva la cantidad de paraacutemetros encontrados se podriacutea escribir la siguiente especificacioacuten

int parametros()

int cantidad

( expresion() cantidad = 1

( expresion() cantidad++ )

| cantidad = 0 )

return cantidad

y si no quisiera ponerse expliacutecitamente la alternativa de la produccioacuten vaciacutea podriacutea escribirse

int parametros() int cantidad = 0

21

[ expresion() cantidad++ ( expresion() cantidad++ ) ]

return cantidad

Comunicacioacuten entre los analizadores leacutexico y sintaacutecticoCada vez que el analizador sintaacutectico realiza una llamada al analizador lexicograacutefico recibe como resultado de la llamada una representacioacuten de la pieza sintaacutectica encontrada en el texto analizado En los analizadores generados por JavaCC la comunicacioacuten de la pieza se efectuacutea mediante un valor de clase Token Esta cla-se estaacute definida en el fichero Tokenjava que se obtiene en todo caso siempre con el mismo contenido cualquiera que sea la especificacioacuten proporcionada como entrada al generador

Entre los campos de la clase Token se encuentran los siguientes

public int kind

nuacutemero entero que sirve de representacioacuten interna de la pieza sintaacutectica estos nuacutemeros asociados a las piezas son asignados automaacuteticamente por JavaCC los valores considerados pueden consultar-se en el fichero generado de nombre Constantsjava

public String image

cadena que contiene de la secuencia de caracteres (lexema) que constituyen la pieza sintaacutectica co-municada

public int beginLine beginColumn endLine endColumn

posiciones ocupadas (nuacutemeros de liacutenea y columna en el fichero de entrada) por el comienzo y el fi-nal del lexema de la pieza sintaacutectica comunicada

La clase Token tiene otros campos y meacutetodos en el fichero Tokenjava se tienen comentarios des-criptivos sobre todos los campos y meacutetodos que forman parte de la clase todos los campos de la clase se declaran como public

Ya se ha comentado anteriormente que cuando un meacutetodo de anaacutelisis sintaacutectico (asociado a un siacutembolo no terminal) tiene tipo el valor devuelto tras una llamada se puede almacenar en una variable por si se desea utilizar en alguna accioacuten sintaacutectica para ello se escribe

valor = nombreSimbolo()

(siendo valor el nombre de la variable y nombreSimbolo el nombre del siacutembolo no terminal) De manera anaacuteloga el objeto de la clase Token representativo de la pieza sintaacutectica encontrada en la entrada por el analizador lexicograacutefico puede asignarse a una variable del mismo tipo para tener accesibles las ca -racteriacutesticas de la pieza por si se precisaran en alguna accioacuten sintaacutectica para ello en la parte de la especifi -cacioacuten sintaacutectica donde aparezca la pieza sintaacutectica se escribe

dato = lt nombrePieza gt

donde nombrePieza es el nombre de la pieza sintaacutectica y dato es el nombre de una variable de tipo Token en la que se anota el valor indicativo de las propiedades de la pieza sintaacutectica comunicada

Tambieacuten existe otra manera de acceder a las caracteriacutesticas de la pieza sintaacutectica que el analizador lexico -graacutefico comunica al analizador sintaacutectico en la clase del analizador sintaacutectico se tiene declarado el campo

[ static ] public Token token

22

cada vez que el analizador sintaacutectico recibe una pieza sintaacutectica comunicada por el analizador lexicograacutefico se deja anotada en el campo token y ese contenido no se altera hasta que sea proporcionada la siguiente pieza de la entrada

∆ Ejemplo 4

Se considera una ampliacioacuten del ejemplo de presentacioacuten ademaacutes del anaacutelisis de la entrada se pretende obtener una relacioacuten numerada de las variables y las constantes indicando el nuacutemero de liacutenea y el nuacuteme-ro de columna donde estaacuten situadas por ejemplo si en la entrada se tiene la expresioacuten (grabada en dos liacuteneas)

x (y + 11) + 22

en la salida se tendraacute

1- x linea 1 columna 1

2- y linea 1 columna 6

3- 11 linea 1 columna 10

4- 22 linea 2 columna 5

En la especificacioacuten que se propone como solucioacuten puede apreciarse- en la clase del analizador sintaacutectico se han incluido meacutetodos auxiliares en los que se usan los nom-bres image beginLine beginColumn que son campos de un objeto de clase Token

- en la especificacioacuten sintaacutectica de factor se emplea la variable pieza de tipo Token para reco-ger las caracteriacutesticas de las piezas sintaacutecticas detectadas- la variable numero hace de contador para numerar las constantes y variables

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin

static int numero = 0

private static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

private static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

private static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

23

PARSER_END (ExprMin)

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() Token pieza pieza = ltconstantegt numero++ grabarDatosPieza(numero pieza) | pieza = ltvariablegt numero++ grabarDatosPieza(numero pieza) | ( expresion() )

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP | n | t | r

Si se agrupan las dos acciones sintaacutecticas comunes la especificacioacuten de factor tambieacuten puede escri-birse de esta forma

void factor() Token pieza ( pieza = ltconstantegt | pieza = ltvariablegt

) numero++ grabarDatosPieza(numero pieza)

| ( expresion() )

24

Si se accede al campo token de la clase del analizador sintaacutectico se puede resolver este ejemplo pres-cindiendo de la variable pieza

void factor() ( ltconstantegt | ltvariablegt

) numero++ grabarDatosPieza(numero token)

| ( expresion() )

Tareas asociadas a las piezas sintaacutecticasYa se ha visto que es posible asociar bloques de coacutedigo Java a puntos de la estructura sintaacutectica para que se realicen ciertas operaciones cuando el analizador sintaacutectico pase por esa zona de la estructura Tambieacuten es posible asociar coacutedigo Java a las piezas sintaacutecticas para que el analizador lexicograacutefico efectuacutee determina-das tareas cuando detecte en la entrada analizada piezas que tengan asociado coacutedigo

loz Accioacuten ligada a una pieza sintaacutecticaSi se asocia un bloque de coacutedigo a una pieza sintaacutectica se consigue que cada vez que se detecta en la en-trada analizada la presencia de esa pieza se ejecute en ese preciso momento el coacutedigo del bloque asociado a la pieza Estas acciones se denominan ldquoacciones lexicograacuteficasrdquo (lexical actions) el coacutedigo del bloque se traslada literalmente al coacutedigo del analizador lexicograacutefico generado para que se ejecute oportunamente cuando se detecte la pieza ligada al bloque de coacutedigo La accioacuten lexicograacutefica se ejecuta inmediatamente antes que la pieza sea comunicada al analizador sintaacutectico

Se puede asociar una accioacuten lexicograacutefica a una pieza sintaacutectica declarada con TOKEN y tambieacuten a una se-cuencia de caracteres declarada con SKIP ademaacutes de eacutestas hay otras dos formas de declaraciones lexico-graacuteficas (no mencionadas en esta introduccioacuten) que tambieacuten pueden llevar una accioacuten asociada

En una especificacioacuten JavaCC la forma de una pieza sintaacutectica (o bien de una secuencia de caracteres) se define poniendo su descripcioacuten delimitada entre llaves detraacutes de la indicacioacuten TOKEN (o bien SKIP) si se quiere asociar una accioacuten lexicograacutefica a la pieza (o a la secuencia) hay que colocar el bloque de coacutedigo de la accioacuten a continuacioacuten de la descripcioacuten de la forma de la pieza (o de la secuencia) el bloque (delimi-tado por llaves) queda situado entre la descripcioacuten y la llave que cierra la especificacioacuten

La pieza sintaacutectica especial que representa el final del fichero (EOF) tambieacuten puede llevar asociada una ac-cioacuten lexicograacutefica para ello hay que poner

ltgt TOKEN lt EOF gt coacutedigo de la accioacuten asociada

para justificar la presencia de los caracteres ltgt delante de la palabra TOKEN hay que basarse en el con-cepto de ldquoestado lexicograacuteficordquo (cuya explicacioacuten queda fuera del alcance de esta introduccioacuten)

∆ Ejemplo 5

Se considera nuevamente el ejemplo de presentacioacuten como resultado del anaacutelisis de una expresioacuten se pretende obtener una relacioacuten de los pareacutentesis contenidos en la expresioacuten en el mismo orden en que se encuentran ademaacutes por cada salto de liacutenea del texto de entrada se grabaraacute en la salida el caraacutecter y

25

se pasaraacute a una nueva liacutenea tambieacuten se anotaraacute la frase Final del trabajo cuando se alcance el final del fichero de entrada Asiacute por ejemplo si la entrada analizada es

(a + b) (( x )) (y + 7)

los resultados obtenidos seraacuten

() (())()

Final del trabajo

En la especificacioacuten JavaCC que se propone como solucioacuten soacutelo se muestran las reglas sintaacutecticas y le -xicograacuteficas (en el resto no hay cambios) en esta solucioacuten puede apreciarse- las piezas sintaacutecticas correspondientes a los pareacutentesis se han definido de manera nominal para po-der asociarles una accioacuten lexicograacutefica- los caracteres incluidos en la pieza de tipo SKIP se han descompuesto en dos grupos ya que la ac-cioacuten lexicograacutefica incorporada soacutelo afecta a uno de ellos

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ltpAbrirgt expresion() ltpCerrargt

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

TOKEN

26

lt pAbrir ( gt Systemoutprint(()

TOKEN lt pCerrar ) gt Systemoutprint())

ltgtTOKEN lt EOF gt Systemoutprintln ( Final del trabajo )

SKIP n Systemoutprint( n)

SKIP | t | r

loz Bloque de declaraciones lexicograacuteficasCada definicioacuten de una pieza sintaacutectica puede llevar asociada una accioacuten lexicograacutefica que se ejecuta in-mediatamente despueacutes de detectar en la entrada un lexema que se ajusta a la definicioacuten de la pieza cada accioacuten lexicograacutefica es un bloque de coacutedigo Java que puede contener sus propias declaraciones Pero a ve-ces interesa tener declaraciones que sean compartidas por todas las acciones lexicograacuteficas con indepen-dencia de la pieza a la que esteacute asociada cada una de ellas

Para incluir declaraciones que puedan usarse desde el coacutedigo asociado a cualquiera de las piezas sintaacutecti -cas se dispone de una construccioacuten con el siguiente formato

TOKEN_MGR_DECLS

que consta de una palabra reservada seguida del siacutembolo dos puntos seguido de un bloque de coacutedigo Java (delimitado por llaves) esta declaracioacuten se puede colocar en cualquier parte de una especificacioacuten JavaCC si se incluye soacutelo puede ponerse una vez

Todas las declaraciones de variables o de meacutetodos puestas en el bloque de declaraciones lexicograacuteficas compartidas son accesibles desde el coacutedigo de cualquier accioacuten lexicograacutefica la justificacioacuten es sencilla el bloque de declaraciones se traslada al principio del coacutedigo del analizador lexicograacutefico generado y las ac-ciones lexicograacuteficas tambieacuten se trasladan a ese analizador

loz Declaraciones lexicograacuteficas predefinidasDe lo expuesto hasta ahora se aprecia que en una accioacuten lexicograacutefica asociada a una pieza sintaacutectica re-sultan accesibles

- las declaraciones de su propio bloque (si tiene declaraciones)- los componentes del bloque de declaraciones compartidas (si estaacute incluido en la especificacioacuten)

Ademaacutes de esto se tienen otras declaraciones pertenecientes a la clase TokenManager que constituye el analizador lexicograacutefico que tambieacuten son utilizables desde cualquiera de las acciones lexico-graacuteficas estas declaraciones pueden considerarse como ldquopredefinidasrdquo ya que son incorporadas automaacuteti-camente por el generador Entre estas declaraciones accesibles se encuentran las siguientes

[ static ] StringBuffer imagecontiene la cadena de caracteres representativa de la pieza sintaacutectica actual que se acaba de recono-cer en le entrada se trata de un campo de la clase y se declara como static cuando el analizador

27

se ha generado con la opcioacuten (por defecto) Static = true si se generase con la opcioacuten Sta-tic = false no quedariacutea declarada como static

Token matchedToken

variable de clase Token que representa la pieza sintaacutectica actual la que se acaba de encontrar en la entrada se trata de una variable local del meacutetodo al que se ha trasladado el coacutedigo de la accioacuten (y por ello accesible desde ese coacutedigo)

El campo image puede usarse tanto si la accioacuten lexicograacutefica estaacute asociada a una pieza declarada con TOKEN como si lo estaacute a una secuencia declarada con SKIP Sin embargo la variable matchedToken no puede usarse en una accioacuten asociada a un patroacuten especificado como SKIP el motivo es que lo declara-do como SKIP no constituye propiamente una pieza sintaacutectica ya que no se va a comunicar al analizador sintaacutectico

∆ Ejemplo 6

Se propone aquiacute una nueva solucioacuten para el ejemplo nordm 4 expuesto anteriormente

Esta nueva solucioacuten tal y como se expone aquiacute admite algunas mejoras ya que su pretensioacuten soacutelo es ser-vir de ilustracioacuten para algunos aspectos que se acaban de citar (maacutes adelante se propone otra solucioacuten al-ternativa)

En la especificacioacuten de la solucioacuten es posible apreciar esto- las acciones lexicograacuteficas pueden llevar sus propias declaraciones las variables nLineaVar nColumnaVar nLineaCte y nColumnaCte se declaran dentro de las acciones

- la variable matchedToken a la que se hace referencia en el coacutedigo de la accioacuten lexicograacutefica aso-ciada a una variable contiene la informacioacuten sobre la pieza sintaacutectica que se acaba de detectar una va-riable de la expresioacuten analizada anaacutelogamente con la referencia relativa a una constante- el nombre image que aparece en las acciones lexicograacuteficas se refiere al campo de la clase Expr-MinTokenManager (analizador lexicograacutefico) noacutetese que en efecto se comunica como un paraacuteme-tro de clase StringBuffer

- la variable numero se emplea para numerar las constantes y variables encontradas

- los nombres beginLine y beginColumn son campos (puacuteblicos) de la clase Token por ello se pueden aplicar a un objeto de esa clase

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

void unaExpresion()

28

expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n StringBuffer lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

TOKEN lt variable [a-z] gt

int nLineaVar nColumnaVar numero++ nLineaVar = matchedTokenbeginLine nColumnaVar = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaVar nColumnaVar)

TOKEN lt constante ( [0-9] ) + gt

int nLineaCte nColumnaCte

29

numero++ nLineaCte = matchedTokenbeginLine nColumnaCte = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaCte nColumnaCte)

SKIP | n | t | r

La especificacioacuten precedente se puede escribir de otra manera maacutes escueta como se indica a continua-cioacuten (soacutelo se muestran las modificaciones) sobre esta nueva especificacioacuten puede mencionarse- se han eliminado por innecesarias las variables declaradas dentro de las acciones lexicograacuteficas- el nombre image que aparece en el bloque de las declaraciones compartidas se refiere al campo de la clase Token noacutetese que en efecto se aplica a un objeto de esa clase y se comunica como un paraacute -metro de tipo String

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

TOKEN lt variable [a-z] gt

numero++ grabarDatosPieza(numero matchedToken)

TOKEN lt constante ( [0-9] ) + gt

numero++ grabarDatosPieza(numero matchedToken)

loz Accioacuten lexicograacutefica comuacutenSi hubiera que realizar la misma tarea tras la deteccioacuten de todas y cada una de las piezas sintaacutecticas una posibilidad incoacutemoda seriacutea repetir el mismo coacutedigo en todas las acciones lexicograacuteficas para evitar tanta reiteracioacuten se tiene la opcioacuten Common_Token_Action su valor por defecto es false pero si se le asigna el valor true es posible definir una ldquoaccioacuten lexicograacutefica comuacutenrdquo que se escribe una uacutenica vez y se aplica a todas las piezas sintaacutecticas

30

Cuando la opcioacuten Common_Token_Action se establece con el valor true es obligado incluir el blo-que de declaraciones lexicograacuteficas compartidas (TOKEN_MGR_DECLS) Ademaacutes en ese bloque se preci-sa la definicioacuten de un meacutetodo cuyo encabezamiento sea

[ static ] void CommonTokenAction(Token t)

el coacutedigo de este meacutetodo constituye la accioacuten lexicograacutefica comuacuten que se ejecutaraacute despueacutes de la deteccioacuten de cualquier pieza sintaacutectica el meacutetodo tiene un paraacutemetro de tipo Token mediante el que se puede co-municar la informacioacuten correspondiente a la pieza que se acaba de encontrar en la entrada analizada

Si una pieza sintaacutectica tiene asociada una accioacuten lexicograacutefica propia la accioacuten lexicograacutefica comuacuten (si es-taacute definida) se ejecutaraacute inmediatamente despueacutes de realizar la accioacuten propia

La accioacuten lexicograacutefica comuacuten se aplica a todas las piezas sintaacutecticas definidas como TOKEN bien sea no-minalmente (en las reglas lexicograacuteficas) o bien anoacutenimamente (incorporadas dentro de las reglas sintaacutecti-cas) sin embargo no se aplica cuando la secuencia acoplada se corresponde con una expresioacuten regular es-pecificada como SKIP (noacutetese que por el contrario una especificacioacuten SKIP siacute puede tener asociada una accioacuten lexicograacutefica propia)

El coacutedigo del meacutetodo CommonTokenAction se traslada como un componente maacutes del bloque de decla-raciones compartidas (TOKEN_MGR_DECLS) al principio de la clase del analizador lexicograacutefico

∆ Ejemplo 7

Se pretende ahora ampliar el ejemplo de presentacioacuten con la obtencioacuten de una relacioacuten numerada de los lexemas de las piezas sintaacutecticas encontradas en la entrada analizada en el caso de las variables y de las constantes antes del lexema se mostraraacute un indicativo (Var Const) por ejemplo si la expresioacuten de la entrada es

( a + 987 ) b

se grabaraacute la salida

( 1Var a 2+ 3Const 987 4) 5 6Var b 7 8

En la solucioacuten que se propone a continuacioacuten se muestran la definicioacuten de las opciones y las reglas sin-taacutecticas y lexicograacuteficas (en el resto no hay modificaciones) puede resaltarse lo siguiente- las llamadas al meacutetodo CommonTokenAction se incorporan automaacuteticamente no hay que incluir-las en la especificacioacuten como paraacutemetro de llamada a este meacutetodo siempre se emplea el objeto que re -presenta la pieza que se acaba de detectar- la accioacuten lexicograacutefica comuacuten realiza la actualizacioacuten del contador de piezas y la grabacioacuten del lexe-ma de la pieza detectada con su nuacutemero de orden- la accioacuten lexicograacutefica comuacuten tambieacuten se aplica a las piezas sintaacutecticas anoacutenimas (pareacutentesis y ope-radores) y a la pieza especial que representa el final del fichero (EOF)

- las piezas sintaacutecticas ltconstantegt y ltvariablegt tienen asociada una accioacuten lexicograacutefica propia que se ejecuta antes de la accioacuten lexicograacutefica comuacuten- los caracteres que se saltan (SKIP) no estaacuten afectados por la accioacuten lexicograacutefica comuacuten

options Ignore_Case = true Common_Token_Action = true

31

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarIndicativo(String indicativo) Systemoutprint(indicativo + )

static void grabarLexema (int num String lexema) Systemoutprint(lexema + + num + n)

static void CommonTokenAction(Token pieza) numero++ grabarLexema(numero piezaimage)

TOKEN lt variable [a-z] gt

grabarIndicativo(Var)

32

TOKEN lt constante ( [0-9] ) + gt

grabarIndicativo(Const)

SKIP | n | t | r

A modo de ejemplo de aplicacioacuten de la accioacuten lexicograacutefica comuacuten en lo que sigue se ofrece una solucioacuten para la grabacioacuten (en el fichero predefinido de salida) del texto analizado con las liacuteneas numeradas

∆ Ejemplo 8 [ Listado del texto analizado ]Se desea completar el ejemplo de presentacioacuten con la grabacioacuten de un listado con las liacuteneas numeradas del texto analizado (noacutetese que una expresioacuten puede escribirse ocupando varias liacuteneas) No se muestra la especificacioacuten sintaacutectica debido a que no hay ninguna modificacioacuten en ella Los aspectos resentildeables de la solucioacuten que se propone son- se usa la variable numLin como contador de liacuteneas

- en la variable linea se van yuxtaponiendo los lexemas de las piezas sintaacutecticas de una liacutenea esta yuxtaposicioacuten se realiza mediante la accioacuten lexicograacutefica comuacuten- las secuencias reconocidas con los patrones especificados como SKIP no implican la ejecucioacuten de la accioacuten lexicograacutefica comuacuten por ello su lexema (almacenado en la variable image) ha de yuxtaponer-se expliacutecitamente- hay que separar el patroacuten correspondiente al fin de liacutenea para detectar el momento en que ha de gra-barse la liacutenea completa cuyo final se ha alcanzado

options Ignore_Case = true Common_Token_Action = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(nnAnalisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

TOKEN_MGR_DECLS static int numLin = 0 static String linea =

33

static void CommonTokenAction(Token pieza) linea = linea + piezaimage

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP lt | t | r gt linea = linea + image

SKIP n numLin++ Systemoutprintln(numLin + + linea) linea =

A modo de recapitulacioacutenↄ Accioacuten lexicograacutefica Accioacuten sintaacutectica

bull accioacuten lexicograacutefica bloque de coacutedigo Java asociado a una pieza sintaacutectica nominal (TOKEN) o a una secuencia de caracteres (SKIP) se ejecuta cuando se detecta en la entrada la pieza o la secuencia corres-pondiente no se puede aplicar a piezas sintaacutecticas anoacutenimas (autodefinidas con comillas)bull accioacuten sintaacutectica bloque de coacutedigo Java asociado a un punto de la estructura sintaacutectica (el punto indica-do por el sitio de la produccioacuten donde se inserta el bloque) se ejecuta cuando el anaacutelisis de la entrada pa-sa por ese punto de la estructura

ↄ Valor de una pieza sintaacutectica comunicadaEl analizador lexicograacutefico comunica al analizador sintaacutectico una pieza a traveacutes de un valor de la clase Token (fichero Tokenjava) un objeto de la clase Token representa las caracteriacutesticas de la pieza comunicada En la clase Token entre otros se tienen los campos

String imagelexema de la pieza

int beginLine beginColumn endLine endColumnposicioacuten del lexema en el fichero analizado (indicada por los nuacutemeros de fila y de columna de su comienzo y de su terminacioacuten)

ↄ Valor asociado a un siacutembolo

34

En las producciones que especifican la sintaxis se pueden incluir asignaciones que sirven para dejar ano-tado el valor asociado a un siacutembolo

bull siacutembolo no terminal valor = nombreSimbolo()el meacutetodo de anaacutelisis asociado a nombreSimbolo se ha declarado de un cierto tipo (distinto del tipo void) valor ha de ser una variable del mismo tipo que el meacutetodo

bull siacutembolo terminal dato = lt nombrePieza gtel valor asignado es el valor de la clase Token representativo de la pieza sintaacutectica (nominal) dato ha de ser una variable de tipo Token

ↄ Miradas sobre la pieza sintaacutectica comunicadaLa pieza sintaacutectica comunicada puede mirarse desde cada uno de los dos analizadores

bull analizador sintaacutecticola pieza comunicada (desde el analizador lexicograacutefico) se tiene anotada en un campo de la clase del analizador sintaacutectico declarado como

(static) public Token token

este valor es accesible desde el coacutedigo de todos los meacutetodos de anaacutelisis sintaacutectico el contenido del campo variacutea cuando se recibe una nueva pieza sintaacutectica

bull analizador lexicograacuteficola pieza dispuesta para ser comunicada (al analizador sintaacutectico) se tiene anotada en una variable local declarada como

Token matchedToken

este valor es accesible para el coacutedigo de todas las acciones lexicograacuteficas

ↄ El lexema de la pieza comunicadaEl lexema de la pieza sintaacutectica comunicada se encuentra disponible en dos sitios distintos que compar-ten el mismo nombre pero que son de distinto tipo y se usan de distinta manera

bull String image

es un campo de la clase TOKEN puede consultarse en relacioacuten con un objeto de esa clase que esteacute disponible

bull StringBuffer image

es un campo de la clase del analizador lexicograacutefico ( ∙ ∙ ∙ ∙ TokenManagerjava) es accesible desde el coacutedigo de la clase y por lo tanto desde todas las acciones lexicograacuteficas (bloques de coacute-digo que estaacuten incorporados al analizador lexicograacutefico)

se trata de un valor disponible tanto para las piezas sintaacutecticas nominales (TOKEN) como para las secuencias que se saltan (SKIP)

ↄ Resumen relativo a la lexicografiacutea

TOKEN pieza sintaacutectica nominal∙ ∙ ∙ pieza sintaacutectica anoacutenimaSKIP secuencia de caracteres que no forman pieza

35

Precisiones sobre el analizador sintaacutectico generadoPuede decirse que en su funcionamiento maacutes sencillo cuando no se emplean las posibilidades de examen por adelantado (ldquolookaheadrdquo) JavaCC comprueba que las reglas sintaacutecticas cumplen la condicioacuten LL(1) y generan un analizador descendente-predictivo-recursivo Pero esta apreciacioacuten hay que matizarla ya que el analizador sintaacutectico generado no se ajusta en sentido estricto al modelo de analizador LL(1)

En el modelo de anaacutelisis LL(1) el orden del examen de las distintas alternativas de expansioacuten de un siacutembo-lo no terminal es indiferente ya que se conocen los siacutembolos directores de cada una de ellas (que resultan ser conjuntos disjuntos) en la implementacioacuten del subprograma de anaacutelisis asociado al siacutembolo no terminal ltNombreSimbgt cuyas producciones son

ltNombreSimbgt = α1

| α2

∙ ∙ ∙

| αn

se podriacutea preguntar en cualquier orden si la pieza actual pertenece al conjunto de siacutembolos directores de las distintas reglas Sin embargo en el analizador sintaacutectico generado por JavaCC la implementacioacuten del meacute-todo asociado al siacutembolo no terminal ltNombreSimbgt no sigue esa pauta

Si las anteriores reglas sintaacutecticas se transcriben en JavaCC de la forma

void nombreSimb () α1-jcc

| α2-jcc

∙ ∙ ∙

| αn-jcc

1

accioacutenlexicograacutefica

comuacuten

Commom -TokenAction2

accioacutenlexicograacutefica

propia

(accioacuten ligadaa una pieza)3

bloque dedeclaracioneslexicograacuteficas

TOKEN_MGR_DECLS4

declaracioneslexicograacuteficaspredefinidas

(disponibilidad

de uso)TOKENsisisisi∙ ∙ ∙sino--SKIPnosisisi

Las indicaciones si de las columnas 3 y 4 son consecuencia de las indicaciones de la columna 2

La accioacuten lexicograacutefica comuacuten se ejecuta despueacutes de la accioacuten lexicograacutefica propia

36

el meacutetodo de anaacutelisis generado procede de la siguiente manera

se van considerando las alternativas sucesivamente en el mismo orden en el que estaacuten especifica-das primero la de α1 (α1-jcc) despueacutes la de α2 (α2-jcc) etc

para cada alternativa seleccionada se comprueba si la pieza por adelantado coincide con alguno de los siacutembolos iniciales de la parte derecha y si hay coincidencia se prosigue el anaacutelisis con ella si no hay coincidencia se pasa a la alternativa siguiente

siguiendo con este orden si se llega al final de las alternativas sin haberse encontrado coincidencia alguna se produce un error sintaacutectico

En el analizador generado por JavaCC hay una peculiaridad que no se tiene en los analizadores LL(1) es-trictamente considerados la palabra vaciacutea siacute se considera como posible siacutembolo inicial de la parte derecha una regla (esto ocurre siempre que la parte derecha es anulable) De esta peculiaridad se deriva una conse-cuencia que ha de tenerse en cuenta al escribir la especificacioacuten dado que siempre es posible considerar la palabra vaciacutea como la siguiente pieza por adelantado si se llega a examinar la alternativa correspondiente a una produccioacuten anulable el analizador generado siempre seleccionaraacute esa alternativa con independencia de la siguiente pieza que esteacute presente en la entrada que queda por analizar

Asiacute pues si alguna de las producciones que definen un siacutembolo no terminal es anulable es importante el or-den en que se escriben las alternativas en una especificacioacuten sintaacutectica JavaCC el funcionamiento del ana-lizador generado siacute depende de la colocacioacuten elegida para las reglas

Para ilustrar estas peculiaridades del analizador sintaacutectico generado por JavaCC a continuacioacuten se expone un ejemplo

∆ Ejemplo 9

Se quiere obtener un analizador leacutexico-sintaacutectico de textos que se ajustan al siguiente formato una se-cuencia de uno o maacutes nombres seguida del siacutembolo igual seguido de un nuacutemero entero si la secuencia tiene maacutes de un nombre han de estar separados bien por una coma en todos los casos o bien por el siacutem-bolo dos puntos en todos los casos esto es son correctas las secuencias

uno dos tres = 123exclusivo = 0Abajo Arriba = 57

y son incorrectas las secuenciasuno dos tres = 123

exclusivo inclusive = 0

fuera dentro = 56

Una gramaacutetica que define las secuencias con este formato es

ltListagt = nombre ltOtrosNombresgt = numero

ltOtrosNombresgt = nombre nombre | nombre nombre | ε

se trata de una gramaacutetica que cumple la condicioacuten LL(1)- el siacutembolo director para la alternativa vaciacutea es el siacutembolo igual es evidente pues que las tres reglas de ltOtrosNombresgt tienen siacutembolos directores distintos

37

- la decisioacuten de abandonar la repeticioacuten del anaacutelisis de un nuevo nombre (en las dos primeras alterna-tivas de ltOtrosNombresgt) se puede tomar ante la presencia del siacutembolo igual

En la implementacioacuten de un analizador sintaacutectico LL(1) en sentido estricto la seleccioacuten de la alternativa vaciacutea para expandir el siacutembolo ltOtrosNombresgt se hariacutea tras comprobar expliacutecitamente que la pie-za sintaacutectica por adelantado es el siacutembolo igual

El funcionamiento del analizador generado por JavaCC depende del orden de colocacioacuten de las reglas A continuacioacuten se exponen varias posibilidades de especificacioacuten y se comenta el analizador generado en cada caso

bull Especificacioacuten primeraSe transcriben literalmente las reglas tal y como se han escrito antes en la notacioacuten BNF noacutetese que para poder codificar la parte derecha de la regla vaciacutea ha de incluirse una accioacuten sintaacutectica vaciacutea

PARSER_BEGIN (Igualdad)

public class Igualdad

public static void main (String[] argum) throws ParseException Igualdad analisis = new Igualdad(Systemin) analisissecuencia() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (Igualdad)

void secuencia() lista() ltEOFgt

void lista() lt nombre gt otrosNombres() = lt numero gt

void otrosNombres() lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt ) |

TOKEN lt nombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN lt numero ( [ 0 - 9 ] )+ gt

SKIP | r | n

38

El analizador generado funciona correctamente puesto que en el meacutetodo otrosNombres se selec-ciona la alternativa vaciacutea despueacutes de haber comprobado que la pieza por adelantado no es siacutembolo ini-cial de ninguna de las dos alternativas precedentes (no es una coma ni un siacutembolo dos puntos)

bull Especificacioacuten segundaSe considera la misma especificacioacuten anterior pero cambiando el orden de colocacioacuten de las reglas ahora la produccioacuten vaciacutea se coloca como primera alternativa (soacutelo se muestran las partes modifica-das)

void otrosNombres() | lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt )

Con la entrada de esta especificacioacuten el generador JavaCC produce un mensaje en el que se avisa de que las dos alternativas que estaacuten colocadas detraacutes de la alternativa vaciacutea nunca podraacuten seleccionarse aun asiacute se genera un analizador Pero es un analizador que funciona incorrectamente asiacute por ejemplo la entrada

mxa nyz = 57

se considera incorrecta despueacutes de visto el primer nombre se intenta aplicar la produccioacuten vaciacutea (se espera encontrar el siacutembolo igual) pero habriacutea que aplicar la regla cuyo siacutembolo director es la coma

bull Especificacioacuten terceraSe utilizan las posibilidades de la notacioacuten BNF-Ampliada para prescindir de la especificacioacuten expliacuteci-ta de la palabra vaciacutea se aprovecha que JavaCC admite el operador + para indicar repeticiones de una o maacutes veces la alternativa vaciacutea estaacute impliacutecitamente contemplada en la posibilidad de cero repeti-ciones que contempla el operador (soacutelo se muestran las partes modificadas en la especificacioacuten)

void otrosNombres() ( lt nombre gt )+ | ( lt nombre gt )

El meacutetodo otrosNombres() generado hace las comprobaciones en el siguiente orden

- se comprueba si la pieza por adelantado es una coma (siacutembolo inicial de la primera alternativa) y en este caso se selecciona la primera regla- si no es una coma se comprueba si es un siacutembolo dos puntos (siacutembolo inicial de la segunda alter-nativa) y en este caso se selecciona la segunda regla- si no es un siacutembolo dos puntos se considera la palabra vaciacutea como siacutembolo inicial (siempre posi-ble impliacutecitamente) para la segunda alternativa en este caso para que no haya error la pieza por adelantado ha de ser un siacutembolo igual

Se trata pues de un analizador que funciona correctamente

bull Especificacioacuten cuarta

39

Se pone la misma especificacioacuten anterior pero cambiando el orden de consideracioacuten de la regla vaciacutea ahora se incluye impliacutecitamente en la primera alternativa (soacutelo se muestran las partes modificadas)

void otrosNombres() ( lt nombre gt ) | ( lt nombre gt )+

Para esta especificacioacuten el generador JavaCC emite un aviso en el que se indica que la segunda alter -nativa nunca se seleccionaraacute no obstante se genera un analizador que no funciona como se supone que deberiacutea hacerlo por ejemplo el anaacutelisis de la entrada

nyz mxa = 1

produce un error despueacutes de tratado el primer nombre se intenta aplicar la produccioacuten vaciacutea (corres-pondiente a la repeticioacuten de cero veces contemplada en la primera regla) por lo que no se encuentra el esperado siacutembolo igual

40

Page 5: JavaCC

loz Ejemplo de presentacioacuten

bull Descripcioacuten del lenguaje

El lenguaje L estaacute formado por las expresiones en las que pueden aparecer- variables- constantes- operadores + y

Las variables son nombres formados por una uacutenica letra (minuacutescula o mayuacutescula) las constantes son nuacute-meros enteros de una o maacutes cifras El espacio y el tabulador pueden estar presentes pero no tienen ninguacuten significado los finales de liacutenea tampoco son significativos (una expresioacuten puede codificarse ocupando una o maacutes liacuteneas)

La sintaxis de las expresiones se especifica mediante la siguiente gramaacutetica

ltExpresiongt = ltTerminogt + ltTerminogt ltTerminogt = ltFactorgt ltFactorgt ltFactorgt = variable | constante | ( ltExpresiongt )

bull Especificacioacuten leacutexico-sintaacutectica codificada con la notacioacuten JavaCCUna manera de escribir la especificacioacuten (para la que se ha elegido el nombre ExprMin) de forma que sea aceptada por el generador es

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Anaacutelisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

4

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP | t | n | r

bull Obtencioacuten del analizadorSi la especificacioacuten precedente se tiene grabada en un fichero de nombre Ejemplojj para obtener el analizador

- se ejecuta el generador javacc Ejemplojj

- se compila el analizador generado javac ExprMinjava

bull Ejecucioacuten del analizadorSi se quiere analizar una expresioacuten grabada en un fichero de nombre PruebaExptxt

- se ejecuta el analizador obtenido java ExprMin lt PruebaExptxt

loz Analizadores generadosEn su funcionamiento maacutes sencillo y habitual JavaCC genera un analizador sintaacutectico complementado con un analizador lexicograacutefico para que conjuntamente se pueda realizar un anaacutelisis leacutexico-sintaacutectico de un texto de entrada

El analizador sintaacutectico obtenido es en general LL(k) descendente y determinista con la consulta de k siacutembolos por adelantado si la gramaacutetica proporcionada cumple la condicioacuten LL(1) se genera un analiza -dor sintaacutectico descendente-predictivo-recursivo Maacutes adelante se hacen algunas precisiones sobre esta afir-macioacuten

Si la especificacioacuten leacutexico-sintaacutectica de un lenguaje codificada en JavaCC tiene dado (como indicativo que acompantildea a las palabras reservadas PARSER_BEGIN y PARSER_END) el nombre EspLexSin y se tiene grabada en un fichero de nombre Lenguajejj cuando se ejecuta el generador tomando como entrada ese fichero

javacc Lenguajejj

se obtienen los siguientes ficheros (clases) con coacutedigo Java

Tokenjavadescripciones para la comunicacioacuten entre los analizadores leacutexico y sintaacutectico

5

TokenMgrErrorjavatratamiento de errores para el anaacutelisis lexicograacutefico

ParseExceptionjavatratamiento de errores para el anaacutelisis sintaacutectico

SimpleCharStreamjavacomponentes para la realizacioacuten de las tareas de entradasalida del analizador

EspLexSinConstantsjavadefinicioacuten de la representacioacuten interna de las piezas sintaacutecticas

EspLexSinTokenManagerjavaanalizador lexicograacutefico

EspLexSinjavaanalizador sintaacutectico

Puede apreciarse que hay dos categoriacuteas de nombres de ficheros generados los cuatro primeros nombres citados no dependen del nombre de la especificacioacuten considerada los otros nombres de ficheros se forman a partir del nombre dado a la especificacioacuten

Forma de una especificacioacuten JavaCC (versioacuten simplificada)La forma que se describe en lo que sigue es una versioacuten simplificada el generador JavaCC admite especificaciones con otras muchas posi-bilidades no mencionadas en esta introduccioacuten

Una especificacioacuten para el generador JavaCC puede considerarse dividida en cuatro secciones

loz Seccioacuten de opcionesEn esta seccioacuten cuya presencia es optativa se pueden asignar valores a diversos paraacutemetros (llamados op-ciones) que sirven para configurar ciertas caracteriacutesticas del funcionamiento del generador o del analizador generado Cada paraacutemetro (opcioacuten) tiene un valor por defecto que es el que toma cuando no se le asigna expliacutecitamente un valor Los valores de las opciones tambieacuten se pueden fijar en la liacutenea de comandos

Seccioacuten de opciones

Seccioacuten de ejecucioacuten

Seccioacuten de sintaxis

Seccioacuten de lexicografiacutea

6

cuando se ejecuta el generador (lo indicado en la liacutenea de comandos tiene prioridad sobre lo especificado en esta seccioacuten de opciones)

Algunas de las opciones para las que se puede fijar un valor son

Ignore_Case (valor por defecto false)

indica si en el texto analizado ha de distinguirse o no entre letras minuacutesculas y mayuacutesculas

Build_Parser (valor por defecto true)

indica si se genera el analizador sintaacutectico o no

Build_Token_Manager (valor por defecto true)

indica si se genera el analizador lexicograacutefico o no

Sanity_Check (valor por defecto true)

indica si se realizan comprobaciones sobre la gramaacutetica sintaacutectica

Debug_Parser (valor por defecto false)

indica si se genera una traza para el anaacutelisis leacutexico-sintaacutectico

Error_Reporting (valor por defecto true)

indica si los mensajes de error emitidos son maacutes o menos explicativos

Static (valor por defecto true)

con el valor por defecto los meacutetodos de los analizadores leacutexico y sintaacutectico se generan con el des-criptor estaacutetico (static)

Los nombres de las opciones pueden escribirse con letras minuacutesculas o mayuacutesculas los valores de las op-ciones son en la mayoriacutea de los casos un valor entero o un valor loacutegico Si se incluye la seccioacuten se empie-za poniendo la palabra reservada options en este caso al menos ha de ponerse una opcioacuten

La forma de esta seccioacuten es

options nombreOpcion1 = valorOpcion1

nombreOpcion2 = valorOpcion2

∙ ∙ ∙ ∙

nombreOpcionk = valorOpcionk

loz Seccioacuten de ejecucioacutenEn esta seccioacuten se pone el coacutedigo Java que contiene la llamada al analizador generado para que se realice el anaacutelisis de un determinado texto de entrada Tambieacuten se establece aquiacute el nombre de la especificacioacuten que es el nombre que se toma para formar los nombres de parte de los ficheros (clases) generados

La seccioacuten estaacute delimitada por dos palabras reservadas ambas acompantildeadas por un mismo nombre (puesto entre pareacutentesis) ese nombre es el que se da a la especificacioacuten Entre esas dos palabras ha de ponerse una clase directora para el proceso de anaacutelisis el nombre de esa clase directora ha de coincidir con el nombre dado a la especificacioacuten

PARSER_BEGIN (NombreDeLaEspecif)

7

∙ ∙ ∙ ∙

public class NombreDeLaEspecif

∙ ∙ ∙ ∙

∙ ∙ ∙ ∙

PARSER_END (NombreDeLaEspecif)

Una posible versioacuten sencilla de la clase directora es

public class NombreDeLaEspecif public static void main (String[] argum) throws ParseException NombreDeLaEspecif anLeSi = new NombreDeLaEspecif (Systemin) anLeSisimboloPrograma()

se crea el objeto anLeSi para poder aplicar los meacutetodos de anaacutelisis se asocia la entrada standard ( Systemin ) a la entrada para el analizador generado se aplica al objeto creado el meacutetodo de anaacutelisis correspondiente al siacutembolo inicial de la gramaacutetica

loz Seccioacuten de sintaxisEn esta seccioacuten se describe la sintaxis del lenguaje para el que se desea generar el analizador usaacutendose pa-ra ello una notacioacuten parecida a la BNF En lo que sigue se expone la forma de las producciones tal y como se escriben en JavaCC ponieacutendolas en comparacioacuten con las producciones de la notacioacuten BNF-Ampliada

bull Reglas sintaacutecticasEl conjunto de reglas que definen un siacutembolo no terminal

ltNombreSimbgt = α1

| α2

∙ ∙ ∙

| αn

se codifica de la siguiente manera

void nombreSimb () α1-jcc

| α2-jcc

∙ ∙ ∙

| αn-jcc

8

- el nombre del siacutembolo no terminal (parte izquierda de las reglas) se escribe como el encabeza-miento de un meacutetodo en Java sin argumentos y sin valor de devolucioacuten (meacutetodo de tipo void) se sigue como en Java la costumbre de escribir los nombres de los meacutetodos empezando con letra mi-nuacutescula

- el siacutembolo dos puntos representa la separacioacuten entre las partes izquierda y derechas de las reglas (en vez del siacutembolo = de la notacioacuten BNF)

- el siacutembolo barra vertical representa la separacioacuten entre distintas partes derechas con la misma parte izquierda (igual que en la notacioacuten BNF)

- despueacutes del siacutembolo dos puntos se pone un bloque de coacutedigo Java este coacutedigo se suele dedicar a la realizacioacuten de tareas semaacutenticas si se pretende hacer soacutelo un anaacutelisis sintaacutectico el bloque que-daraacute vaciacuteo (aunque siempre es obligada su presencia)

- el conjunto de las alternativas (partes derechas de las reglas) estaacute delimitado mediante llaves como si constituyese un bloque de coacutedigo Java

- αi-jcc representa la codificacioacuten de una alternativa (parte derecha) empleaacutendose una nota-cioacuten que se describe a continuacioacuten

bull Siacutembolos no terminales en las partes derechasEn la notacioacuten BNF se pone ltNombreSimbgt en la notacioacuten JavaCC se escribe

nombreSimb()

esto es se pone como si fuese una llamada en Java a un meacutetodo sin argumentos

bull Siacutembolos terminales en las partes derechasSe considera una clasificacioacuten en las piezas sintaacutecticas (siacutembolos terminales de una gramaacutetica sintaacutectica) distinguieacutendose entre piezas sintaacutecticas nominales y anoacutenimas

son nominales las piezas sintaacutecticas a las que se asocia un nombre en otra parte de la especificacioacuten (maacutes adelante se describe la manera de indicar esta asociacioacuten) ese nombre asociado a la pieza es el que se usa al escribir las partes derechas de las producciones En la notacioacuten JavaCC un siacutembolo terminal no-minal se escribe poniendo el nombre delimitado por los caracteres lt y gt

ltnombreTerminalgt

son anoacutenimas las piezas sintaacutecticas que no tienen nombre asociado no se precisa porque la pieza se re -presenta por su propia secuencia de caracteres (lexema) En la notacioacuten JavaCC un siacutembolo terminal anoacutenimo se escribe poniendo su lexema delimitado por comillas por ejemplo

= lt lt= END

bull Metasiacutembolos de opcionalidad y de repeticioacutenEn la notacioacuten BNF se emplean los corchetes para indicar opcionalidad en la notacioacuten JavaCC se utilizan tambieacuten esos mismos siacutembolos

[ α ] en ambas notaciones

En la notacioacuten BNF se emplean las llaves para indicar una repeticioacuten de cero o maacutes veces en la notacioacuten JavaCC se utilizan los pareacutentesis y un asterisco detraacutes del pareacutentesis de cerrar

α en notacioacuten BNF-Ampliada

( α ) en notacioacuten JavaCC

bull Otros metasiacutembolos de la notacioacuten JavaCC

9

La opcionalidad tambieacuten se puede indicar empleando el metasiacutembolo de interrogacioacuten

( α )

Para indicar repeticioacuten de una o maacutes veces se utiliza el metasiacutembolo operador aditivo suma

( α )+

bull Control del final de fichero de entradaPara controlar el final del fichero en el texto analizado ha de antildeadirse una regla sintaacutectica especial que tenga (seguacuten la notacioacuten BNF) la forma

ltProgramaCompletogt = ltProgramagt FF

donde

ltProgramaCompletogt es un nuevo siacutembolo (no terminal) auxiliar antildeadido

ltProgramagt es el siacutembolo inicial de la gramaacutetica sintaacutectica

FF es una representacioacuten del final del fichero

En JavaCC el final de fichero se escribe con la notacioacuten ltEOFgt siendo EOF una palabra reservada la regla sintaacutectica especial antildeadida a la especificacioacuten quedariacutea codificada asiacute

void programaCompleto() programa() ltEOFgt

bull Representacioacuten de la produccioacuten vaciacuteaLas producciones vaciacuteas (reglas sintaacutecticas cuya parte derecha es la palabra vaciacutea ε ) no se pueden repre-sentar expliacutecitamente en una especificacioacuten leacutexico-sintaacutectica escrita en JavaCC Para indicar la presencia de una produccioacuten vaciacutea han de aplicarse las posibilidades de la notacioacuten BNF-Ampliada seguacuten esta nota-cioacuten cuando se indica la opcionalidad de un componente se estaacute especificando impliacutecitamente la palabra vaciacutea (en el caso de que el componente no esteacute presente)

Asiacute pues si se quisiera escribir en JavaCC la construccioacuten sintaacutectica expresada por las reglas

ltComponentegt = α

| ε

deberiacutean de considerarse con la notacioacuten equivalente

ltComponentegt = [ α ]

y si hubiera varias alternativas no vaciacuteas

ltComponentegt = α | β

| ε

habriacutea que considerarlas como

ltComponentegt = [ α | β ]

o tambieacuten podriacutea verse como

ltComponentegt = α

10

| [ β ]

Como ejemplo sea la especificacioacuten sintaacutectica de una llamada a una funcioacuten que puede tener cero o maacutes paraacutemetros separados por comas aunque no haya paraacutemetros los pareacutentesis estaacuten presentes Las siguien-tes reglas definen esta estructura

ltLlamadagt = id ( ltParametrosgt )

ltParametrosgt = ltExpresiongt ltExpresiongt | ε

En JavaCC la definicioacuten del siacutembolo ltParametrosgt se puede escribir como sigue (noacutetese que aquiacute los pareacutentesis empleados para aplicar el operador son metasiacutembolos)

void parametros() [ expresion() ( expresion() ) ]

loz Seccioacuten de lexicografiacuteaEn esta seccioacuten se indica la lexicografiacutea del lenguaje para el que se va a generar el analizador la notacioacuten de JavaCC que representa la forma de cada una de las piezas sintaacutecticas es una variante de las bien conoci-das expresiones regulares

bull El nombre de las piezas sintaacutecticas nominalesPara asociar un nombre a una pieza sintaacutectica y para al mismo tiempo definir la forma de la pieza se em-plea la notacioacuten

TOKEN lt nombreTerminal Expresioacuten-Regular gt

- TOKEN es una palabra reservada detraacutes de ella se pone el separador dos puntos

- nombreTerminal representa el nombre que se quiere asociar a la pieza sintaacutectica

- Expresioacuten-Regular indica una expresioacuten regular que especifica la forma de la pieza

- el nombre y la expresioacuten se separan mediante el caraacutecter dos puntos y conjuntamente se deli -mitan mediante los caracteres lt y gt a su vez toda la asociacioacuten se delimita con llaves

bull Expresiones regulares en JavaCCLas expresiones regulares en una especificacioacuten JavaCC se escriben de manera parecida a la forma utiliza-da en otras conocidas notaciones aunque con ciertas peculiaridades A continuacioacuten se mencionan los as-pectos maacutes significativos de las expresiones regulares JavaCC y algunos ejemplos

Los operadores de las expresiones son

| unioacuten

repeticioacuten cero o maacutes veces

+ repeticioacuten una o maacutes veces

opcionalidad

11

Los pareacutentesis permiten fijar el orden de aplicacioacuten de los operadores Una peculiaridad de las expresiones regulares JavaCC es que los operadores de repeticioacuten y de opcionalidad requieren que el componente al que se aplican esteacute en todo caso delimitado por pareacutentesis

Se pueden citar las siguientes caracteriacutesticas baacutesicas de las expresiones regulares en JavaCC

Se tiene la posibilidad de definir conjuntos de caracteres poniendo entre corchetes la relacioacuten de carac-teres separados por comas y cada caraacutecter delimitado por comillas En un conjunto se puede fijar un ran-go de caracteres poniendo el primero y el uacuteltimo separados por el siacutembolo guioacuten (-) A veces resulta maacutes coacutemodo indicar los caracteres que no pertenecen al conjunto para ello se puede definir el comple -mentario de un conjunto poniendo el siacutembolo ~ delante del corchete inicial

Para indicar que una secuencia de caracteres colocados consecutivamente constituye por siacute misma una expresioacuten regular (o un componente de una expresioacuten) se escribe la secuencia delimitada por comillas El siacutembolo se emplea como metacaraacutecter para indicar que el siacutembolo que estaacute inmediatamente a su derecha se considere como caraacutecter del alfabeto no como posible metacaraacutecter (operador delimitador)

En una expresioacuten regular se pueden incluir como si fueran caracteres las secuencias de escape con la misma notacioacuten y el mismo significado que en Java o en C

Noacutetese que en una produccioacuten sintaacutectica la opcionalidad se puede especificar de dos formas con corchetes o con el siacutembolo de interrogacioacuten sin embargo en una expresioacuten regular la opcionalidad soacutelo se puede es-pecificar mediante el siacutembolo de interrogacioacuten (los corchetes en las expresiones regulares son para definir conjuntos de caracteres)

A continuacioacuten se muestran ejemplos de expresiones regulares escritas seguacuten la notacioacuten JavaCC

ENDsecuencia de las tres letras mayuacutesculas que conforman la palabra END

whilesecuencia de las cinco letras minuacutesculas que conforman la palabra while

ncaraacutecter representativo del fin de liacutenea (secuencia de escape)

rcaraacutecter representativo del retorno de carro (secuencia de escape)

caraacutecter dos puntos

=secuencia de dos caracteres (siacutembolo de asignacioacuten en Pascal)

gtcaraacutecter representativo del operador de relacioacuten mayor

(i | I)(f | F)palabra ldquoifrdquo escrita con letras minuacutesculas o mayuacutesculas (cuatro combinaciones)

if | IFpalabra ldquoifrdquo escrita soacutelo con minuacutesculas o soacutelo con mayuacutesculas (dos combinaciones)

12

u | o | i | e | auna cualquiera de las vocales minuacutesculas (pero soacutelo una de ellas)

[ A E I O U ]una cualquiera de las vocales mayuacutesculas (pero soacutelo una de ellas)

[ a - z ntilde ]una cualquiera de las letras minuacutesculas incluyendo la letra ntilde

[ a - z A - Z 0 - 9 ]una cualquiera de las letras o de las cifras decimales

([ 0 - 9 ])+constante entera (nuacutemero de una o maacutes cifras)

noacutetese que es incorrecto poner [ 0 - 9 ]+

( + | - )([ 0 - 9 ])+constante entera precedida opcionalmente por un signo

noacutetese que la primera cifra ha de seguir inmediatamente al signo (si existe)

([+ -])([ 0 - 9 ])+representa lo mismo que la expresioacuten anterior

[ a - z ] ([ a - z 0 - 9 ])una letra minuacutescula seguida de cero o maacutes letras minuacutesculas o cifras decimales

~[a - z ntilde A - Z Ntilde]cualquier caraacutecter que no sea una letra

noacutetese que la expresioacuten representa un uacutenico caraacutecter

~[n r]cualquier caraacutecter (un uacutenico caraacutecter) excepto el fin de liacutenea y el retorno de carro

~[ ]cualquier caraacutecter del alfabeto (un uacutenico caraacutecter)

([ 0 - 9 ])+ ( ([ 0 - 9 ])+)constante aritmeacutetica con parte decimal opcional

Los modernossecuencia de catorce caracteres Los modernos

noacutetese el uso de para imponer la condicioacuten de caraacutecter del siacutembolo comillas

nsecuencia de cuatro caracteres n

noacutetese que aquiacute no se representa la secuencia de escape

bull Piezas sintaacutecticas internas (ldquoprivaterdquo)

13

Es posible definir piezas sintaacutecticas auxiliares llamadas ldquointernasrdquo porque no se comunican al analizador sintaacutectico sino que soacutelo sirven para facilitar la escritura de la forma de las piezas sintaacutecticas que siacute se co-munican con el uso de las piezas internas es posible evitar repeticiones de escritura y hacer maacutes legible la especificacioacuten de las caracteriacutesticas lexicograacuteficas Las piezas internas permiten dar nombre a subexpre-siones regulares que a su vez pueden emplearse como componentes para escribir expresiones maacutes com-plejas que incorporan esos nombres Para indicar que una pieza sintaacutectica se considere como interna se pone el siacutembolo delante del nombre asociado a la pieza

A continuacioacuten se muestra un ejemplo tiacutepico de utilizacioacuten de las piezas internas en una especificacioacuten le-xicograacutefica en JavaCC Se quieren definir tres piezas sintaacutecticas que representan un nombre una constante entera y una constante decimal una posible especificacioacuten es

TOKEN ltnombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN ltcteEntera ( [ 0 - 9 ] )+ gt

TOKEN ltcteDecimal ( [ 0 - 9 ] )+ ( [ 0 - 9 ] )+ gt

Con la inclusioacuten de tres piezas sintaacutecticas internas auxiliares esta misma especificacioacuten podriacutea escribirse de la siguiente manera

TOKEN lt letra [ a - z A - Z ntilde Ntilde ] gt

TOKEN lt cifra [ 0 - 9 ] gt

TOKEN lt numero ( ltcifragt )+ gt

TOKEN lt nombre ( ltletragt )+ gt

TOKEN lt cteEntera ltnumerogt gt

TOKEN lt cteDecimal ltnumerogt ltnumerogt gt

Los nombres letra cifra y numero son exclusivos (internos) del analizador lexicograacutefico y no se pueden emplear como nombres de piezas en la especificacioacuten de las reglas sintaacutecticas cuando esos nom-bres se usan en la definicioacuten de otras piezas han de escribirse delimitados por los siacutembolos lt y gt

bull Distincioacuten restringida entre minuacutesculas y mayuacutesculasSe tiene la opcioacuten Ignore_Case para determinar si en el anaacutelisis del texto de entrada se distingue entre las letras minuacutesculas y las mayuacutesculas el valor por defecto false indica que siacute se diferencian si se asig-na el valor true a esta opcioacuten entonces no se diferencian en ninguacuten caso con independencia de que en los patrones que se definan para las piezas sintaacutecticas se pongan minuacutesculas o mayuacutesculas

Pero a veces conviene que la diferenciacioacuten entre minuacutesculas y mayuacutesculas no se aplique de manera ge-neral a toda la entrada sino en particular a las secuencias de caracteres que se acoplen a ciertos patrones Para este fin existe la posibilidad de establecer que en la expresioacuten regular que define el patroacuten de una determinada pieza sintaacutectica no se diferencie entre minuacutesculas y mayuacutesculas ello se consigue poniendo

14

[ IGNORE_CASE ]

inmediatamente detraacutes de la palabra TOKEN con la que empieza la especificacioacuten de la pieza sintaacutectica en la que se pretende la no distincioacuten entre minuacutesculas y mayuacutesculas

Noacutetese que ignore_case tiene dos significados

- es el nombre de un paraacutemetro de la seccioacuten de opciones (puede ponerse en minuacutesculas o mayuacutesculas)- es una palabra reservada que puede aplicarse (escribieacutendola entre corchetes y en mayuacutesculas) a la de -finicioacuten de una pieza sintaacutectica

Por ejemplo sea un lenguaje que en general distingue entre minuacutesculas y mayuacutesculas pero en la codifi -cacioacuten del exponente de una constante real exponencial puede ponerse la letra e en minuacutescula o en ma-yuacutescula En la especificacioacuten JavaCC para ese lenguaje la opcioacuten Ignore_Case ha de tomar el valor false en la definicioacuten de una constante exponencial son posibles las siguientes formas equivalentes

1) sin hacer uso de IGNORE_CASE

TOKEN lt cifras ( [ 0 - 9 ] )+ gt

TOKEN lt signo [+ -] gt

TOKEN lt constante

ltcifrasgt ( ltcifrasgt) [e E] (ltsignogt) ltcifrasgt gt

2) aplicando IGNORE_CASE

TOKEN lt cifras ( [ 0 - 9 ] )+ gt

TOKEN lt signo [+ -] gt

TOKEN [IGNORE_CASE] lt constante

ltcifrasgt ( ltcifrasgt) e (ltsignogt) ltcifrasgt gt

bull Secuencias de caracteres sin efecto en la estructura sintaacutecticaEn los lenguajes de programacioacuten suele haber secuencias de caracteres que no forman parte de la estructu-ra sintaacutectica de un programa esas secuencias son detectadas por el analizador lexicograacutefico pero no son comunicadas al sintaacutectico no han de considerarse como piezas sino que han de saltarse En JavaCC se pueden definir estas secuencias mediante la palabra reservada SKIP la notacioacuten de una especificacioacuten con la palabra SKIP de una secuencia que ha de saltarse es la misma que la notacioacuten usada para las espe-cificaciones con la palabra TOKEN de las piezas que han de comunicarse

Los dos casos maacutes habituales de secuencias que no se comunican al analizador sintaacutectico son

a) los caracteres separadores que no tienen significado en el texto analizadoen los lenguajes de codificacioacuten en formato libre hay caracteres del texto fuente que han de ignorarse en la mayoriacutea de los casos estos caracteres son el espacio en blanco el tabulador el retorno de carro y el fin de liacutenea esta caracteriacutestica se especifica

SKIP |t | r |n

b) los comentarios

15

en el anaacutelisis sintaacutectico de un programa los comentarios han de ignorarse una manera de especificar esta circunstancia es poniendo en una definicioacuten SKIP la expresioacuten regular que denota la forma de los comentarios del lenguaje por ejemplo para los comentarios de una liacutenea de Java se pondriacutea

SKIP

lt (~ [n r]) gt

Tareas asociadas a la estructura sintaacutecticaJavaCC genera un analizador sintaacutectico descendente recursivo constituido por un conjunto de meacutetodos ca-da siacutembolo no terminal de la gramaacutetica sintaacutectica tiene asociado un meacutetodo de anaacutelisis (el meacutetodo tiene el mismo nombre que el siacutembolo correspondiente) Un meacutetodo de anaacutelisis ademaacutes de la realizacioacuten de la parte del anaacutelisis sintaacutectico que le corresponde puede llevar a cabo otras tareas complementarias que sirvan para efectuar un anaacutelisis maacutes complejo del texto de entrada

loz Bloque para un siacutemboloAl describir la forma de las reglas sintaacutecticas de un siacutembolo no terminal en una especificacioacuten JavaCC ya se ha visto que detraacutes de la definicioacuten del nombre del siacutembolo y antes de empezar con las diferentes alter-nativas que tiene asociadas se incluye un bloque de coacutedigo Java (en el ejemplo de presentacioacuten este bloque estaacute vaciacuteo en todos los siacutembolos no terminales de la gramaacutetica) Cada bloque situado en esa posicioacuten puede considerarse ligado al siacutembolo no terminal en cuya descripcioacuten estaacute incluido el coacutedigo Java del bloque se traslada literalmente al principio del meacutetodo generado correspondiente al siacutembolo no terminal y por lo tan-to es lo primero que se considera (se ejecuta) cuando en el transcurso del proceso de anaacutelisis se realiza una llamada al meacutetodo Asiacute pues cada siacutembolo no terminal siempre tiene asociado un uacutenico bloque de coacute-digo Java ndashbloque de presencia obligada aunque pueda estar vaciacuteo- en este bloque se pueden poner de-claraciones y sentencias

loz Acciones sintaacutecticasAdemaacutes del bloque asociado a cada uno de los siacutembolos no terminales de la gramaacutetica se pueden incluir otros bloques de coacutedigo Java intercalados en cualquiera de las partes derechas de las reglas sintaacutecticas Los bloques incluidos en las partes derechas se denominan ldquoacciones sintaacutecticasrdquo (parser actions) ya que son trozos de coacutedigo que pueden considerarse asociados a los puntos de la estructura sintaacutectica donde estaacuten co-locados Una accioacuten sintaacutectica se puede poner en cualquier posicioacuten al principio al final o en el medio de la parte derecha de una regla precedida o seguida de siacutembolos terminales o no terminales el coacutedigo Java de una accioacuten sintaacutectica se traslada literalmente al meacutetodo asociado al siacutembolo no terminal de la parte izquierda de la regla de manera que se ejecuta cada vez que el analizador transcurre por el punto de la estructura sintaacutectica donde se ha incorporado la accioacuten (esto es las acciones sintaacutecticas se incorporan al coacutedigo del analizador sintaacutectico) No obstante su nombre en los bloques de las acciones sintaacutecticas suele ponerse coacutedigo dedicado a la realizacioacuten de tareas semaacutenticas

Si en el bloque asociado a un siacutembolo no terminal se ponen declaraciones los nombres ahiacute definidos son accesibles en todas las acciones sintaacutecticas intercaladas en las partes derechas de las reglas de ese siacutembolo la justificacioacuten es sencilla el coacutedigo del bloque asociado al siacutembolo se incluye al principio de un meacutetodo y el coacutedigo de las acciones pertenece a ese mismo meacutetodo

∆ Ejemplo 1

Se considera de nuevo el ejemplo de presentacioacuten ahora se pretende completar el anaacutelisis leacutexico-sintaacutec-tico con la realizacioacuten de las siguientes tareas

- cada vez que se analiza una expresioacuten un teacutermino o un factor se graba el nombre del siacutembolo no terminal correspondiente y

16

- cuando se analiza un teacutermino en el que hay operadores multiplicativos se cuenta su nuacutemero y se graba el resultado (noacutetese que se cuentan los operadores de cada teacutermino pero no se acumula la cuenta de todos los operadores de la expresioacuten)

Para la realizacioacuten de estas tareas se antildeaden bloques de coacutedigo Java En la especificacioacuten que se describe a continuacioacuten puede apreciarse- para los siacutembolos no terminales expresion termino y factor se ha incluido en los blo-ques correspondientes la operacioacuten de grabacioacuten de su nombre que se ejecutaraacute cada vez que se efec-tuacutee una llamada al meacutetodo- en el bloque asociado a termino se tiene la declaracioacuten del contador de operadores (que seraacute una variable local del meacutetodo)- en la parte derecha de la regla que define la estructura de un teacutermino se han incluido dos acciones sintaacutecticas una para contar la cantidad de operadores (situada detraacutes de la pieza sintaacutectica del opera-dor) y otra para grabar la cantidad de operadores del teacutermino (situada al final de la parte derecha)- se ha definido el meacutetodo grabar para facilitar la escritura del coacutedigo Java

options Ignore_Case = true

PARSER_BEGIN (ExprMin) public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

private static void grabar(String nombre) Systemoutprintln ( -gt + nombre + n)

PARSER_END (ExprMin)

void unaExpresion()

expresion() ltEOFgt

void expresion() grabar(Expresion)

termino() ( + termino() )

void termino() int nAst = 0 grabar(Termino)

17

factor() ( nAst++ factor() ) if (nAst gt 0) Systemoutprintln (n Asteriscos + nAst + n)

void factor() grabar(Factor)

ltconstantegt | ltvariablegt | ( expresion() )

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP | t | n | r

Cuando se incluyen acciones sintaacutecticas la legibilidad del aspecto sintaacutectico de la especificacioacuten queda oscurecida para paliar ese inconveniente puede antildeadirse un comentario que muestre la regla sintaacutectica escueta sin las acciones por ejemplo podriacutea escribirse

void termino() int nAst = 0 grabar(Termino)

factor() ( factor() )

factor()

( nAst++ factor() )

if (nAst gt 0) Systemoutprintln (n Asteriscos + nAst + n)

loz Valor comunicado por un meacutetodoEn todo lo visto hasta ahora los meacutetodos de anaacutelisis sintaacutectico generados por JavaCC no devuelven valor alguno al terminar su tarea de anaacutelisis ya que en la especificacioacuten siempre se ha declarado el nombre de un meacutetodo precedido de void (son meacutetodos de tipo void) Pero en general un meacutetodo de anaacutelisis sintaacutec-tico puede ser de cualquier tipo (primitivo o no) por ello un meacutetodo puede devolver un valor de cualquier tipo (de cualquier clase) al meacutetodo desde el que se efectuoacute la llamada

Esta posibilidad de devolucioacuten de valores tiene una utilidad fundamental en la implementacioacuten de analiza-dores de lenguajes permite la comunicacioacuten de datos (atributos de las entidades analizadas) entre los dife -rentes meacutetodos que colaboran en el anaacutelisis del programa completo

En una especificacioacuten JavaCC la llamada a un meacutetodo ocurre cuando en la parte derecha de una regla estaacute el nombre del siacutembolo no terminal asociado al meacutetodo Cuando un meacutetodo devuelve un valor (tiene un tipo distinto de void) es habitual la recepcioacuten del valor devuelto en una variable en la propia especificacioacuten

18

se puede indicar el nombre de la variable a la que se asignaraacute el valor devuelto tras la llamada para ello se emplea una notacioacuten ideacutentica a la forma que tienen las sentencias de asignacioacuten en el lenguaje Java

dato = nombreSimb()

donde dato es el nombre de la variable a la que se asigna el valor devuelto por el meacutetodo llamado esa va -riable ha de estar oportunamente declarada del mismo tipo que dicho meacutetodo

∆ Ejemplo 2

En el mismo ejemplo de presentacioacuten se desea ademaacutes del anaacutelisis leacutexico-sintaacutectico de la entrada contar la cantidad de operadores que hay en la expresioacuten A continuacioacuten se expone una especificacioacuten JavaCC que es una posible solucioacuten soacutelo se muestran las reglas sintaacutecticas (el resto de la especificacioacuten es igual que en el ejemplo de presentacioacuten)

En la especificacioacuten que se propone puede apreciarse- los siacutembolos no terminales expresion termino y factor se han declarado de tipo int los meacutetodos correspondientes devuelven un valor de tipo entero- el valor devuelto por los meacutetodos sirve para propagar a traveacutes de la estructura sintaacutectica la cuenta de los operadores que hay en el texto analizado- por tratarse de meacutetodos que devuelven un valor es preciso incluir las correspondientes sentencias de devolucioacuten return

void unaExpresion() int nOper nOper = expresion() ltEOFgt Systemoutprintln (Cantidad de operadores = + nOper)

int expresion() int n i n = termino() ( + n++ i = termino() n = n + i )

return n

int termino() int n i n = factor() ( n++ i = factor() n = n + i )

return n

int factor() int n ltconstantegt return 0 | ltvariablegt return 0 | ( n = expresion() ) return n

La especificacioacuten de un factor tambieacuten podriacutea escribirse de esta otra manera

19

int factor() int n = 0

( ltconstantegt | ltvariablegt | ( n = expresion() ) )

return n

A continuacioacuten se expone un nuevo ejemplo para ilustrar la utilidad que proporciona el valor devuelto por un meacutetodo de anaacutelisis sintaacutectico para la propagacioacuten (ascendente) de atributos (propiedades de las entida-des analizadas)

∆ Ejemplo 3

Se pretende ampliar el analizador del ejemplo de presentacioacuten para que se emita un mensaje indicativo cuando la expresioacuten analizada sea una expresioacuten en la que soacutelo aparecen constantes en la solucioacuten que se muestra soacutelo se incluyen las reglas sintaacutecticas (en el resto de la especificacioacuten no hay modificacio-nes)

void unaExpresion() boolean esCte

expresion() ltEOFgt

esCte = expresion() ltEOFgt

if (esCte) Systemoutprintln(nExpresion constanten)

boolean expresion() boolean esCte esTambienCte

termino() ( + termino() )

esCte = termino() ( + esTambienCte = termino() esCte = esCte ampamp esTambienCte )

return esCte

boolean termino() boolean esCte esTambienCte

factor() ( factor() )

esCte = factor() ( esTambienCte = factor() esCte = esCte ampamp esTambienCte )

return esCte

20

boolean factor() boolean esCte

ltconstantegt return true | ltvariablegt return false | ( esCte = expresion() ) return esCte

bull Reconsideracioacuten sobre las producciones vaciacuteasAnteriormente se ha comentado que para representar una produccioacuten vaciacutea en una especificacioacuten JavaCC hay que hacer uso de los metasiacutembolos de opcionalidad para ilustrar esta idea se ha incluido como ejem-plo la especificacioacuten de una lista de paraacutemetros compuesta por cero o maacutes expresiones separadas entre siacute por el caraacutecter coma

void parametros() [ expresion() ( expresion() ) ]

Pero si se aprovecha la posibilidad de incorporar acciones sintaacutecticas se tiene una manera de especificar directamente producciones vaciacuteas para conseguirlo basta considerar una produccioacuten vaciacutea (no tiene nada en su parte derecha) acompantildeada de una accioacuten sintaacutectica cuyo bloque de coacutedigo esteacute vaciacuteo Seguacuten esta posibilidad la especificacioacuten anterior podriacutea tambieacuten escribirse asiacute

void parametros() expresion() ( expresion() ) |

En general la accioacuten sintaacutectica asociada a una produccioacuten vaciacutea no tiene por queacute ser un bloque vaciacuteo de coacutedigo por ejemplo si se quiere que el meacutetodo que analiza los paraacutemetros de una llamada devuelva la cantidad de paraacutemetros encontrados se podriacutea escribir la siguiente especificacioacuten

int parametros()

int cantidad

( expresion() cantidad = 1

( expresion() cantidad++ )

| cantidad = 0 )

return cantidad

y si no quisiera ponerse expliacutecitamente la alternativa de la produccioacuten vaciacutea podriacutea escribirse

int parametros() int cantidad = 0

21

[ expresion() cantidad++ ( expresion() cantidad++ ) ]

return cantidad

Comunicacioacuten entre los analizadores leacutexico y sintaacutecticoCada vez que el analizador sintaacutectico realiza una llamada al analizador lexicograacutefico recibe como resultado de la llamada una representacioacuten de la pieza sintaacutectica encontrada en el texto analizado En los analizadores generados por JavaCC la comunicacioacuten de la pieza se efectuacutea mediante un valor de clase Token Esta cla-se estaacute definida en el fichero Tokenjava que se obtiene en todo caso siempre con el mismo contenido cualquiera que sea la especificacioacuten proporcionada como entrada al generador

Entre los campos de la clase Token se encuentran los siguientes

public int kind

nuacutemero entero que sirve de representacioacuten interna de la pieza sintaacutectica estos nuacutemeros asociados a las piezas son asignados automaacuteticamente por JavaCC los valores considerados pueden consultar-se en el fichero generado de nombre Constantsjava

public String image

cadena que contiene de la secuencia de caracteres (lexema) que constituyen la pieza sintaacutectica co-municada

public int beginLine beginColumn endLine endColumn

posiciones ocupadas (nuacutemeros de liacutenea y columna en el fichero de entrada) por el comienzo y el fi-nal del lexema de la pieza sintaacutectica comunicada

La clase Token tiene otros campos y meacutetodos en el fichero Tokenjava se tienen comentarios des-criptivos sobre todos los campos y meacutetodos que forman parte de la clase todos los campos de la clase se declaran como public

Ya se ha comentado anteriormente que cuando un meacutetodo de anaacutelisis sintaacutectico (asociado a un siacutembolo no terminal) tiene tipo el valor devuelto tras una llamada se puede almacenar en una variable por si se desea utilizar en alguna accioacuten sintaacutectica para ello se escribe

valor = nombreSimbolo()

(siendo valor el nombre de la variable y nombreSimbolo el nombre del siacutembolo no terminal) De manera anaacuteloga el objeto de la clase Token representativo de la pieza sintaacutectica encontrada en la entrada por el analizador lexicograacutefico puede asignarse a una variable del mismo tipo para tener accesibles las ca -racteriacutesticas de la pieza por si se precisaran en alguna accioacuten sintaacutectica para ello en la parte de la especifi -cacioacuten sintaacutectica donde aparezca la pieza sintaacutectica se escribe

dato = lt nombrePieza gt

donde nombrePieza es el nombre de la pieza sintaacutectica y dato es el nombre de una variable de tipo Token en la que se anota el valor indicativo de las propiedades de la pieza sintaacutectica comunicada

Tambieacuten existe otra manera de acceder a las caracteriacutesticas de la pieza sintaacutectica que el analizador lexico -graacutefico comunica al analizador sintaacutectico en la clase del analizador sintaacutectico se tiene declarado el campo

[ static ] public Token token

22

cada vez que el analizador sintaacutectico recibe una pieza sintaacutectica comunicada por el analizador lexicograacutefico se deja anotada en el campo token y ese contenido no se altera hasta que sea proporcionada la siguiente pieza de la entrada

∆ Ejemplo 4

Se considera una ampliacioacuten del ejemplo de presentacioacuten ademaacutes del anaacutelisis de la entrada se pretende obtener una relacioacuten numerada de las variables y las constantes indicando el nuacutemero de liacutenea y el nuacuteme-ro de columna donde estaacuten situadas por ejemplo si en la entrada se tiene la expresioacuten (grabada en dos liacuteneas)

x (y + 11) + 22

en la salida se tendraacute

1- x linea 1 columna 1

2- y linea 1 columna 6

3- 11 linea 1 columna 10

4- 22 linea 2 columna 5

En la especificacioacuten que se propone como solucioacuten puede apreciarse- en la clase del analizador sintaacutectico se han incluido meacutetodos auxiliares en los que se usan los nom-bres image beginLine beginColumn que son campos de un objeto de clase Token

- en la especificacioacuten sintaacutectica de factor se emplea la variable pieza de tipo Token para reco-ger las caracteriacutesticas de las piezas sintaacutecticas detectadas- la variable numero hace de contador para numerar las constantes y variables

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin

static int numero = 0

private static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

private static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

private static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

23

PARSER_END (ExprMin)

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() Token pieza pieza = ltconstantegt numero++ grabarDatosPieza(numero pieza) | pieza = ltvariablegt numero++ grabarDatosPieza(numero pieza) | ( expresion() )

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP | n | t | r

Si se agrupan las dos acciones sintaacutecticas comunes la especificacioacuten de factor tambieacuten puede escri-birse de esta forma

void factor() Token pieza ( pieza = ltconstantegt | pieza = ltvariablegt

) numero++ grabarDatosPieza(numero pieza)

| ( expresion() )

24

Si se accede al campo token de la clase del analizador sintaacutectico se puede resolver este ejemplo pres-cindiendo de la variable pieza

void factor() ( ltconstantegt | ltvariablegt

) numero++ grabarDatosPieza(numero token)

| ( expresion() )

Tareas asociadas a las piezas sintaacutecticasYa se ha visto que es posible asociar bloques de coacutedigo Java a puntos de la estructura sintaacutectica para que se realicen ciertas operaciones cuando el analizador sintaacutectico pase por esa zona de la estructura Tambieacuten es posible asociar coacutedigo Java a las piezas sintaacutecticas para que el analizador lexicograacutefico efectuacutee determina-das tareas cuando detecte en la entrada analizada piezas que tengan asociado coacutedigo

loz Accioacuten ligada a una pieza sintaacutecticaSi se asocia un bloque de coacutedigo a una pieza sintaacutectica se consigue que cada vez que se detecta en la en-trada analizada la presencia de esa pieza se ejecute en ese preciso momento el coacutedigo del bloque asociado a la pieza Estas acciones se denominan ldquoacciones lexicograacuteficasrdquo (lexical actions) el coacutedigo del bloque se traslada literalmente al coacutedigo del analizador lexicograacutefico generado para que se ejecute oportunamente cuando se detecte la pieza ligada al bloque de coacutedigo La accioacuten lexicograacutefica se ejecuta inmediatamente antes que la pieza sea comunicada al analizador sintaacutectico

Se puede asociar una accioacuten lexicograacutefica a una pieza sintaacutectica declarada con TOKEN y tambieacuten a una se-cuencia de caracteres declarada con SKIP ademaacutes de eacutestas hay otras dos formas de declaraciones lexico-graacuteficas (no mencionadas en esta introduccioacuten) que tambieacuten pueden llevar una accioacuten asociada

En una especificacioacuten JavaCC la forma de una pieza sintaacutectica (o bien de una secuencia de caracteres) se define poniendo su descripcioacuten delimitada entre llaves detraacutes de la indicacioacuten TOKEN (o bien SKIP) si se quiere asociar una accioacuten lexicograacutefica a la pieza (o a la secuencia) hay que colocar el bloque de coacutedigo de la accioacuten a continuacioacuten de la descripcioacuten de la forma de la pieza (o de la secuencia) el bloque (delimi-tado por llaves) queda situado entre la descripcioacuten y la llave que cierra la especificacioacuten

La pieza sintaacutectica especial que representa el final del fichero (EOF) tambieacuten puede llevar asociada una ac-cioacuten lexicograacutefica para ello hay que poner

ltgt TOKEN lt EOF gt coacutedigo de la accioacuten asociada

para justificar la presencia de los caracteres ltgt delante de la palabra TOKEN hay que basarse en el con-cepto de ldquoestado lexicograacuteficordquo (cuya explicacioacuten queda fuera del alcance de esta introduccioacuten)

∆ Ejemplo 5

Se considera nuevamente el ejemplo de presentacioacuten como resultado del anaacutelisis de una expresioacuten se pretende obtener una relacioacuten de los pareacutentesis contenidos en la expresioacuten en el mismo orden en que se encuentran ademaacutes por cada salto de liacutenea del texto de entrada se grabaraacute en la salida el caraacutecter y

25

se pasaraacute a una nueva liacutenea tambieacuten se anotaraacute la frase Final del trabajo cuando se alcance el final del fichero de entrada Asiacute por ejemplo si la entrada analizada es

(a + b) (( x )) (y + 7)

los resultados obtenidos seraacuten

() (())()

Final del trabajo

En la especificacioacuten JavaCC que se propone como solucioacuten soacutelo se muestran las reglas sintaacutecticas y le -xicograacuteficas (en el resto no hay cambios) en esta solucioacuten puede apreciarse- las piezas sintaacutecticas correspondientes a los pareacutentesis se han definido de manera nominal para po-der asociarles una accioacuten lexicograacutefica- los caracteres incluidos en la pieza de tipo SKIP se han descompuesto en dos grupos ya que la ac-cioacuten lexicograacutefica incorporada soacutelo afecta a uno de ellos

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ltpAbrirgt expresion() ltpCerrargt

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

TOKEN

26

lt pAbrir ( gt Systemoutprint(()

TOKEN lt pCerrar ) gt Systemoutprint())

ltgtTOKEN lt EOF gt Systemoutprintln ( Final del trabajo )

SKIP n Systemoutprint( n)

SKIP | t | r

loz Bloque de declaraciones lexicograacuteficasCada definicioacuten de una pieza sintaacutectica puede llevar asociada una accioacuten lexicograacutefica que se ejecuta in-mediatamente despueacutes de detectar en la entrada un lexema que se ajusta a la definicioacuten de la pieza cada accioacuten lexicograacutefica es un bloque de coacutedigo Java que puede contener sus propias declaraciones Pero a ve-ces interesa tener declaraciones que sean compartidas por todas las acciones lexicograacuteficas con indepen-dencia de la pieza a la que esteacute asociada cada una de ellas

Para incluir declaraciones que puedan usarse desde el coacutedigo asociado a cualquiera de las piezas sintaacutecti -cas se dispone de una construccioacuten con el siguiente formato

TOKEN_MGR_DECLS

que consta de una palabra reservada seguida del siacutembolo dos puntos seguido de un bloque de coacutedigo Java (delimitado por llaves) esta declaracioacuten se puede colocar en cualquier parte de una especificacioacuten JavaCC si se incluye soacutelo puede ponerse una vez

Todas las declaraciones de variables o de meacutetodos puestas en el bloque de declaraciones lexicograacuteficas compartidas son accesibles desde el coacutedigo de cualquier accioacuten lexicograacutefica la justificacioacuten es sencilla el bloque de declaraciones se traslada al principio del coacutedigo del analizador lexicograacutefico generado y las ac-ciones lexicograacuteficas tambieacuten se trasladan a ese analizador

loz Declaraciones lexicograacuteficas predefinidasDe lo expuesto hasta ahora se aprecia que en una accioacuten lexicograacutefica asociada a una pieza sintaacutectica re-sultan accesibles

- las declaraciones de su propio bloque (si tiene declaraciones)- los componentes del bloque de declaraciones compartidas (si estaacute incluido en la especificacioacuten)

Ademaacutes de esto se tienen otras declaraciones pertenecientes a la clase TokenManager que constituye el analizador lexicograacutefico que tambieacuten son utilizables desde cualquiera de las acciones lexico-graacuteficas estas declaraciones pueden considerarse como ldquopredefinidasrdquo ya que son incorporadas automaacuteti-camente por el generador Entre estas declaraciones accesibles se encuentran las siguientes

[ static ] StringBuffer imagecontiene la cadena de caracteres representativa de la pieza sintaacutectica actual que se acaba de recono-cer en le entrada se trata de un campo de la clase y se declara como static cuando el analizador

27

se ha generado con la opcioacuten (por defecto) Static = true si se generase con la opcioacuten Sta-tic = false no quedariacutea declarada como static

Token matchedToken

variable de clase Token que representa la pieza sintaacutectica actual la que se acaba de encontrar en la entrada se trata de una variable local del meacutetodo al que se ha trasladado el coacutedigo de la accioacuten (y por ello accesible desde ese coacutedigo)

El campo image puede usarse tanto si la accioacuten lexicograacutefica estaacute asociada a una pieza declarada con TOKEN como si lo estaacute a una secuencia declarada con SKIP Sin embargo la variable matchedToken no puede usarse en una accioacuten asociada a un patroacuten especificado como SKIP el motivo es que lo declara-do como SKIP no constituye propiamente una pieza sintaacutectica ya que no se va a comunicar al analizador sintaacutectico

∆ Ejemplo 6

Se propone aquiacute una nueva solucioacuten para el ejemplo nordm 4 expuesto anteriormente

Esta nueva solucioacuten tal y como se expone aquiacute admite algunas mejoras ya que su pretensioacuten soacutelo es ser-vir de ilustracioacuten para algunos aspectos que se acaban de citar (maacutes adelante se propone otra solucioacuten al-ternativa)

En la especificacioacuten de la solucioacuten es posible apreciar esto- las acciones lexicograacuteficas pueden llevar sus propias declaraciones las variables nLineaVar nColumnaVar nLineaCte y nColumnaCte se declaran dentro de las acciones

- la variable matchedToken a la que se hace referencia en el coacutedigo de la accioacuten lexicograacutefica aso-ciada a una variable contiene la informacioacuten sobre la pieza sintaacutectica que se acaba de detectar una va-riable de la expresioacuten analizada anaacutelogamente con la referencia relativa a una constante- el nombre image que aparece en las acciones lexicograacuteficas se refiere al campo de la clase Expr-MinTokenManager (analizador lexicograacutefico) noacutetese que en efecto se comunica como un paraacuteme-tro de clase StringBuffer

- la variable numero se emplea para numerar las constantes y variables encontradas

- los nombres beginLine y beginColumn son campos (puacuteblicos) de la clase Token por ello se pueden aplicar a un objeto de esa clase

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

void unaExpresion()

28

expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n StringBuffer lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

TOKEN lt variable [a-z] gt

int nLineaVar nColumnaVar numero++ nLineaVar = matchedTokenbeginLine nColumnaVar = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaVar nColumnaVar)

TOKEN lt constante ( [0-9] ) + gt

int nLineaCte nColumnaCte

29

numero++ nLineaCte = matchedTokenbeginLine nColumnaCte = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaCte nColumnaCte)

SKIP | n | t | r

La especificacioacuten precedente se puede escribir de otra manera maacutes escueta como se indica a continua-cioacuten (soacutelo se muestran las modificaciones) sobre esta nueva especificacioacuten puede mencionarse- se han eliminado por innecesarias las variables declaradas dentro de las acciones lexicograacuteficas- el nombre image que aparece en el bloque de las declaraciones compartidas se refiere al campo de la clase Token noacutetese que en efecto se aplica a un objeto de esa clase y se comunica como un paraacute -metro de tipo String

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

TOKEN lt variable [a-z] gt

numero++ grabarDatosPieza(numero matchedToken)

TOKEN lt constante ( [0-9] ) + gt

numero++ grabarDatosPieza(numero matchedToken)

loz Accioacuten lexicograacutefica comuacutenSi hubiera que realizar la misma tarea tras la deteccioacuten de todas y cada una de las piezas sintaacutecticas una posibilidad incoacutemoda seriacutea repetir el mismo coacutedigo en todas las acciones lexicograacuteficas para evitar tanta reiteracioacuten se tiene la opcioacuten Common_Token_Action su valor por defecto es false pero si se le asigna el valor true es posible definir una ldquoaccioacuten lexicograacutefica comuacutenrdquo que se escribe una uacutenica vez y se aplica a todas las piezas sintaacutecticas

30

Cuando la opcioacuten Common_Token_Action se establece con el valor true es obligado incluir el blo-que de declaraciones lexicograacuteficas compartidas (TOKEN_MGR_DECLS) Ademaacutes en ese bloque se preci-sa la definicioacuten de un meacutetodo cuyo encabezamiento sea

[ static ] void CommonTokenAction(Token t)

el coacutedigo de este meacutetodo constituye la accioacuten lexicograacutefica comuacuten que se ejecutaraacute despueacutes de la deteccioacuten de cualquier pieza sintaacutectica el meacutetodo tiene un paraacutemetro de tipo Token mediante el que se puede co-municar la informacioacuten correspondiente a la pieza que se acaba de encontrar en la entrada analizada

Si una pieza sintaacutectica tiene asociada una accioacuten lexicograacutefica propia la accioacuten lexicograacutefica comuacuten (si es-taacute definida) se ejecutaraacute inmediatamente despueacutes de realizar la accioacuten propia

La accioacuten lexicograacutefica comuacuten se aplica a todas las piezas sintaacutecticas definidas como TOKEN bien sea no-minalmente (en las reglas lexicograacuteficas) o bien anoacutenimamente (incorporadas dentro de las reglas sintaacutecti-cas) sin embargo no se aplica cuando la secuencia acoplada se corresponde con una expresioacuten regular es-pecificada como SKIP (noacutetese que por el contrario una especificacioacuten SKIP siacute puede tener asociada una accioacuten lexicograacutefica propia)

El coacutedigo del meacutetodo CommonTokenAction se traslada como un componente maacutes del bloque de decla-raciones compartidas (TOKEN_MGR_DECLS) al principio de la clase del analizador lexicograacutefico

∆ Ejemplo 7

Se pretende ahora ampliar el ejemplo de presentacioacuten con la obtencioacuten de una relacioacuten numerada de los lexemas de las piezas sintaacutecticas encontradas en la entrada analizada en el caso de las variables y de las constantes antes del lexema se mostraraacute un indicativo (Var Const) por ejemplo si la expresioacuten de la entrada es

( a + 987 ) b

se grabaraacute la salida

( 1Var a 2+ 3Const 987 4) 5 6Var b 7 8

En la solucioacuten que se propone a continuacioacuten se muestran la definicioacuten de las opciones y las reglas sin-taacutecticas y lexicograacuteficas (en el resto no hay modificaciones) puede resaltarse lo siguiente- las llamadas al meacutetodo CommonTokenAction se incorporan automaacuteticamente no hay que incluir-las en la especificacioacuten como paraacutemetro de llamada a este meacutetodo siempre se emplea el objeto que re -presenta la pieza que se acaba de detectar- la accioacuten lexicograacutefica comuacuten realiza la actualizacioacuten del contador de piezas y la grabacioacuten del lexe-ma de la pieza detectada con su nuacutemero de orden- la accioacuten lexicograacutefica comuacuten tambieacuten se aplica a las piezas sintaacutecticas anoacutenimas (pareacutentesis y ope-radores) y a la pieza especial que representa el final del fichero (EOF)

- las piezas sintaacutecticas ltconstantegt y ltvariablegt tienen asociada una accioacuten lexicograacutefica propia que se ejecuta antes de la accioacuten lexicograacutefica comuacuten- los caracteres que se saltan (SKIP) no estaacuten afectados por la accioacuten lexicograacutefica comuacuten

options Ignore_Case = true Common_Token_Action = true

31

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarIndicativo(String indicativo) Systemoutprint(indicativo + )

static void grabarLexema (int num String lexema) Systemoutprint(lexema + + num + n)

static void CommonTokenAction(Token pieza) numero++ grabarLexema(numero piezaimage)

TOKEN lt variable [a-z] gt

grabarIndicativo(Var)

32

TOKEN lt constante ( [0-9] ) + gt

grabarIndicativo(Const)

SKIP | n | t | r

A modo de ejemplo de aplicacioacuten de la accioacuten lexicograacutefica comuacuten en lo que sigue se ofrece una solucioacuten para la grabacioacuten (en el fichero predefinido de salida) del texto analizado con las liacuteneas numeradas

∆ Ejemplo 8 [ Listado del texto analizado ]Se desea completar el ejemplo de presentacioacuten con la grabacioacuten de un listado con las liacuteneas numeradas del texto analizado (noacutetese que una expresioacuten puede escribirse ocupando varias liacuteneas) No se muestra la especificacioacuten sintaacutectica debido a que no hay ninguna modificacioacuten en ella Los aspectos resentildeables de la solucioacuten que se propone son- se usa la variable numLin como contador de liacuteneas

- en la variable linea se van yuxtaponiendo los lexemas de las piezas sintaacutecticas de una liacutenea esta yuxtaposicioacuten se realiza mediante la accioacuten lexicograacutefica comuacuten- las secuencias reconocidas con los patrones especificados como SKIP no implican la ejecucioacuten de la accioacuten lexicograacutefica comuacuten por ello su lexema (almacenado en la variable image) ha de yuxtaponer-se expliacutecitamente- hay que separar el patroacuten correspondiente al fin de liacutenea para detectar el momento en que ha de gra-barse la liacutenea completa cuyo final se ha alcanzado

options Ignore_Case = true Common_Token_Action = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(nnAnalisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

TOKEN_MGR_DECLS static int numLin = 0 static String linea =

33

static void CommonTokenAction(Token pieza) linea = linea + piezaimage

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP lt | t | r gt linea = linea + image

SKIP n numLin++ Systemoutprintln(numLin + + linea) linea =

A modo de recapitulacioacutenↄ Accioacuten lexicograacutefica Accioacuten sintaacutectica

bull accioacuten lexicograacutefica bloque de coacutedigo Java asociado a una pieza sintaacutectica nominal (TOKEN) o a una secuencia de caracteres (SKIP) se ejecuta cuando se detecta en la entrada la pieza o la secuencia corres-pondiente no se puede aplicar a piezas sintaacutecticas anoacutenimas (autodefinidas con comillas)bull accioacuten sintaacutectica bloque de coacutedigo Java asociado a un punto de la estructura sintaacutectica (el punto indica-do por el sitio de la produccioacuten donde se inserta el bloque) se ejecuta cuando el anaacutelisis de la entrada pa-sa por ese punto de la estructura

ↄ Valor de una pieza sintaacutectica comunicadaEl analizador lexicograacutefico comunica al analizador sintaacutectico una pieza a traveacutes de un valor de la clase Token (fichero Tokenjava) un objeto de la clase Token representa las caracteriacutesticas de la pieza comunicada En la clase Token entre otros se tienen los campos

String imagelexema de la pieza

int beginLine beginColumn endLine endColumnposicioacuten del lexema en el fichero analizado (indicada por los nuacutemeros de fila y de columna de su comienzo y de su terminacioacuten)

ↄ Valor asociado a un siacutembolo

34

En las producciones que especifican la sintaxis se pueden incluir asignaciones que sirven para dejar ano-tado el valor asociado a un siacutembolo

bull siacutembolo no terminal valor = nombreSimbolo()el meacutetodo de anaacutelisis asociado a nombreSimbolo se ha declarado de un cierto tipo (distinto del tipo void) valor ha de ser una variable del mismo tipo que el meacutetodo

bull siacutembolo terminal dato = lt nombrePieza gtel valor asignado es el valor de la clase Token representativo de la pieza sintaacutectica (nominal) dato ha de ser una variable de tipo Token

ↄ Miradas sobre la pieza sintaacutectica comunicadaLa pieza sintaacutectica comunicada puede mirarse desde cada uno de los dos analizadores

bull analizador sintaacutecticola pieza comunicada (desde el analizador lexicograacutefico) se tiene anotada en un campo de la clase del analizador sintaacutectico declarado como

(static) public Token token

este valor es accesible desde el coacutedigo de todos los meacutetodos de anaacutelisis sintaacutectico el contenido del campo variacutea cuando se recibe una nueva pieza sintaacutectica

bull analizador lexicograacuteficola pieza dispuesta para ser comunicada (al analizador sintaacutectico) se tiene anotada en una variable local declarada como

Token matchedToken

este valor es accesible para el coacutedigo de todas las acciones lexicograacuteficas

ↄ El lexema de la pieza comunicadaEl lexema de la pieza sintaacutectica comunicada se encuentra disponible en dos sitios distintos que compar-ten el mismo nombre pero que son de distinto tipo y se usan de distinta manera

bull String image

es un campo de la clase TOKEN puede consultarse en relacioacuten con un objeto de esa clase que esteacute disponible

bull StringBuffer image

es un campo de la clase del analizador lexicograacutefico ( ∙ ∙ ∙ ∙ TokenManagerjava) es accesible desde el coacutedigo de la clase y por lo tanto desde todas las acciones lexicograacuteficas (bloques de coacute-digo que estaacuten incorporados al analizador lexicograacutefico)

se trata de un valor disponible tanto para las piezas sintaacutecticas nominales (TOKEN) como para las secuencias que se saltan (SKIP)

ↄ Resumen relativo a la lexicografiacutea

TOKEN pieza sintaacutectica nominal∙ ∙ ∙ pieza sintaacutectica anoacutenimaSKIP secuencia de caracteres que no forman pieza

35

Precisiones sobre el analizador sintaacutectico generadoPuede decirse que en su funcionamiento maacutes sencillo cuando no se emplean las posibilidades de examen por adelantado (ldquolookaheadrdquo) JavaCC comprueba que las reglas sintaacutecticas cumplen la condicioacuten LL(1) y generan un analizador descendente-predictivo-recursivo Pero esta apreciacioacuten hay que matizarla ya que el analizador sintaacutectico generado no se ajusta en sentido estricto al modelo de analizador LL(1)

En el modelo de anaacutelisis LL(1) el orden del examen de las distintas alternativas de expansioacuten de un siacutembo-lo no terminal es indiferente ya que se conocen los siacutembolos directores de cada una de ellas (que resultan ser conjuntos disjuntos) en la implementacioacuten del subprograma de anaacutelisis asociado al siacutembolo no terminal ltNombreSimbgt cuyas producciones son

ltNombreSimbgt = α1

| α2

∙ ∙ ∙

| αn

se podriacutea preguntar en cualquier orden si la pieza actual pertenece al conjunto de siacutembolos directores de las distintas reglas Sin embargo en el analizador sintaacutectico generado por JavaCC la implementacioacuten del meacute-todo asociado al siacutembolo no terminal ltNombreSimbgt no sigue esa pauta

Si las anteriores reglas sintaacutecticas se transcriben en JavaCC de la forma

void nombreSimb () α1-jcc

| α2-jcc

∙ ∙ ∙

| αn-jcc

1

accioacutenlexicograacutefica

comuacuten

Commom -TokenAction2

accioacutenlexicograacutefica

propia

(accioacuten ligadaa una pieza)3

bloque dedeclaracioneslexicograacuteficas

TOKEN_MGR_DECLS4

declaracioneslexicograacuteficaspredefinidas

(disponibilidad

de uso)TOKENsisisisi∙ ∙ ∙sino--SKIPnosisisi

Las indicaciones si de las columnas 3 y 4 son consecuencia de las indicaciones de la columna 2

La accioacuten lexicograacutefica comuacuten se ejecuta despueacutes de la accioacuten lexicograacutefica propia

36

el meacutetodo de anaacutelisis generado procede de la siguiente manera

se van considerando las alternativas sucesivamente en el mismo orden en el que estaacuten especifica-das primero la de α1 (α1-jcc) despueacutes la de α2 (α2-jcc) etc

para cada alternativa seleccionada se comprueba si la pieza por adelantado coincide con alguno de los siacutembolos iniciales de la parte derecha y si hay coincidencia se prosigue el anaacutelisis con ella si no hay coincidencia se pasa a la alternativa siguiente

siguiendo con este orden si se llega al final de las alternativas sin haberse encontrado coincidencia alguna se produce un error sintaacutectico

En el analizador generado por JavaCC hay una peculiaridad que no se tiene en los analizadores LL(1) es-trictamente considerados la palabra vaciacutea siacute se considera como posible siacutembolo inicial de la parte derecha una regla (esto ocurre siempre que la parte derecha es anulable) De esta peculiaridad se deriva una conse-cuencia que ha de tenerse en cuenta al escribir la especificacioacuten dado que siempre es posible considerar la palabra vaciacutea como la siguiente pieza por adelantado si se llega a examinar la alternativa correspondiente a una produccioacuten anulable el analizador generado siempre seleccionaraacute esa alternativa con independencia de la siguiente pieza que esteacute presente en la entrada que queda por analizar

Asiacute pues si alguna de las producciones que definen un siacutembolo no terminal es anulable es importante el or-den en que se escriben las alternativas en una especificacioacuten sintaacutectica JavaCC el funcionamiento del ana-lizador generado siacute depende de la colocacioacuten elegida para las reglas

Para ilustrar estas peculiaridades del analizador sintaacutectico generado por JavaCC a continuacioacuten se expone un ejemplo

∆ Ejemplo 9

Se quiere obtener un analizador leacutexico-sintaacutectico de textos que se ajustan al siguiente formato una se-cuencia de uno o maacutes nombres seguida del siacutembolo igual seguido de un nuacutemero entero si la secuencia tiene maacutes de un nombre han de estar separados bien por una coma en todos los casos o bien por el siacutem-bolo dos puntos en todos los casos esto es son correctas las secuencias

uno dos tres = 123exclusivo = 0Abajo Arriba = 57

y son incorrectas las secuenciasuno dos tres = 123

exclusivo inclusive = 0

fuera dentro = 56

Una gramaacutetica que define las secuencias con este formato es

ltListagt = nombre ltOtrosNombresgt = numero

ltOtrosNombresgt = nombre nombre | nombre nombre | ε

se trata de una gramaacutetica que cumple la condicioacuten LL(1)- el siacutembolo director para la alternativa vaciacutea es el siacutembolo igual es evidente pues que las tres reglas de ltOtrosNombresgt tienen siacutembolos directores distintos

37

- la decisioacuten de abandonar la repeticioacuten del anaacutelisis de un nuevo nombre (en las dos primeras alterna-tivas de ltOtrosNombresgt) se puede tomar ante la presencia del siacutembolo igual

En la implementacioacuten de un analizador sintaacutectico LL(1) en sentido estricto la seleccioacuten de la alternativa vaciacutea para expandir el siacutembolo ltOtrosNombresgt se hariacutea tras comprobar expliacutecitamente que la pie-za sintaacutectica por adelantado es el siacutembolo igual

El funcionamiento del analizador generado por JavaCC depende del orden de colocacioacuten de las reglas A continuacioacuten se exponen varias posibilidades de especificacioacuten y se comenta el analizador generado en cada caso

bull Especificacioacuten primeraSe transcriben literalmente las reglas tal y como se han escrito antes en la notacioacuten BNF noacutetese que para poder codificar la parte derecha de la regla vaciacutea ha de incluirse una accioacuten sintaacutectica vaciacutea

PARSER_BEGIN (Igualdad)

public class Igualdad

public static void main (String[] argum) throws ParseException Igualdad analisis = new Igualdad(Systemin) analisissecuencia() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (Igualdad)

void secuencia() lista() ltEOFgt

void lista() lt nombre gt otrosNombres() = lt numero gt

void otrosNombres() lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt ) |

TOKEN lt nombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN lt numero ( [ 0 - 9 ] )+ gt

SKIP | r | n

38

El analizador generado funciona correctamente puesto que en el meacutetodo otrosNombres se selec-ciona la alternativa vaciacutea despueacutes de haber comprobado que la pieza por adelantado no es siacutembolo ini-cial de ninguna de las dos alternativas precedentes (no es una coma ni un siacutembolo dos puntos)

bull Especificacioacuten segundaSe considera la misma especificacioacuten anterior pero cambiando el orden de colocacioacuten de las reglas ahora la produccioacuten vaciacutea se coloca como primera alternativa (soacutelo se muestran las partes modifica-das)

void otrosNombres() | lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt )

Con la entrada de esta especificacioacuten el generador JavaCC produce un mensaje en el que se avisa de que las dos alternativas que estaacuten colocadas detraacutes de la alternativa vaciacutea nunca podraacuten seleccionarse aun asiacute se genera un analizador Pero es un analizador que funciona incorrectamente asiacute por ejemplo la entrada

mxa nyz = 57

se considera incorrecta despueacutes de visto el primer nombre se intenta aplicar la produccioacuten vaciacutea (se espera encontrar el siacutembolo igual) pero habriacutea que aplicar la regla cuyo siacutembolo director es la coma

bull Especificacioacuten terceraSe utilizan las posibilidades de la notacioacuten BNF-Ampliada para prescindir de la especificacioacuten expliacuteci-ta de la palabra vaciacutea se aprovecha que JavaCC admite el operador + para indicar repeticiones de una o maacutes veces la alternativa vaciacutea estaacute impliacutecitamente contemplada en la posibilidad de cero repeti-ciones que contempla el operador (soacutelo se muestran las partes modificadas en la especificacioacuten)

void otrosNombres() ( lt nombre gt )+ | ( lt nombre gt )

El meacutetodo otrosNombres() generado hace las comprobaciones en el siguiente orden

- se comprueba si la pieza por adelantado es una coma (siacutembolo inicial de la primera alternativa) y en este caso se selecciona la primera regla- si no es una coma se comprueba si es un siacutembolo dos puntos (siacutembolo inicial de la segunda alter-nativa) y en este caso se selecciona la segunda regla- si no es un siacutembolo dos puntos se considera la palabra vaciacutea como siacutembolo inicial (siempre posi-ble impliacutecitamente) para la segunda alternativa en este caso para que no haya error la pieza por adelantado ha de ser un siacutembolo igual

Se trata pues de un analizador que funciona correctamente

bull Especificacioacuten cuarta

39

Se pone la misma especificacioacuten anterior pero cambiando el orden de consideracioacuten de la regla vaciacutea ahora se incluye impliacutecitamente en la primera alternativa (soacutelo se muestran las partes modificadas)

void otrosNombres() ( lt nombre gt ) | ( lt nombre gt )+

Para esta especificacioacuten el generador JavaCC emite un aviso en el que se indica que la segunda alter -nativa nunca se seleccionaraacute no obstante se genera un analizador que no funciona como se supone que deberiacutea hacerlo por ejemplo el anaacutelisis de la entrada

nyz mxa = 1

produce un error despueacutes de tratado el primer nombre se intenta aplicar la produccioacuten vaciacutea (corres-pondiente a la repeticioacuten de cero veces contemplada en la primera regla) por lo que no se encuentra el esperado siacutembolo igual

40

Page 6: JavaCC

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP | t | n | r

bull Obtencioacuten del analizadorSi la especificacioacuten precedente se tiene grabada en un fichero de nombre Ejemplojj para obtener el analizador

- se ejecuta el generador javacc Ejemplojj

- se compila el analizador generado javac ExprMinjava

bull Ejecucioacuten del analizadorSi se quiere analizar una expresioacuten grabada en un fichero de nombre PruebaExptxt

- se ejecuta el analizador obtenido java ExprMin lt PruebaExptxt

loz Analizadores generadosEn su funcionamiento maacutes sencillo y habitual JavaCC genera un analizador sintaacutectico complementado con un analizador lexicograacutefico para que conjuntamente se pueda realizar un anaacutelisis leacutexico-sintaacutectico de un texto de entrada

El analizador sintaacutectico obtenido es en general LL(k) descendente y determinista con la consulta de k siacutembolos por adelantado si la gramaacutetica proporcionada cumple la condicioacuten LL(1) se genera un analiza -dor sintaacutectico descendente-predictivo-recursivo Maacutes adelante se hacen algunas precisiones sobre esta afir-macioacuten

Si la especificacioacuten leacutexico-sintaacutectica de un lenguaje codificada en JavaCC tiene dado (como indicativo que acompantildea a las palabras reservadas PARSER_BEGIN y PARSER_END) el nombre EspLexSin y se tiene grabada en un fichero de nombre Lenguajejj cuando se ejecuta el generador tomando como entrada ese fichero

javacc Lenguajejj

se obtienen los siguientes ficheros (clases) con coacutedigo Java

Tokenjavadescripciones para la comunicacioacuten entre los analizadores leacutexico y sintaacutectico

5

TokenMgrErrorjavatratamiento de errores para el anaacutelisis lexicograacutefico

ParseExceptionjavatratamiento de errores para el anaacutelisis sintaacutectico

SimpleCharStreamjavacomponentes para la realizacioacuten de las tareas de entradasalida del analizador

EspLexSinConstantsjavadefinicioacuten de la representacioacuten interna de las piezas sintaacutecticas

EspLexSinTokenManagerjavaanalizador lexicograacutefico

EspLexSinjavaanalizador sintaacutectico

Puede apreciarse que hay dos categoriacuteas de nombres de ficheros generados los cuatro primeros nombres citados no dependen del nombre de la especificacioacuten considerada los otros nombres de ficheros se forman a partir del nombre dado a la especificacioacuten

Forma de una especificacioacuten JavaCC (versioacuten simplificada)La forma que se describe en lo que sigue es una versioacuten simplificada el generador JavaCC admite especificaciones con otras muchas posi-bilidades no mencionadas en esta introduccioacuten

Una especificacioacuten para el generador JavaCC puede considerarse dividida en cuatro secciones

loz Seccioacuten de opcionesEn esta seccioacuten cuya presencia es optativa se pueden asignar valores a diversos paraacutemetros (llamados op-ciones) que sirven para configurar ciertas caracteriacutesticas del funcionamiento del generador o del analizador generado Cada paraacutemetro (opcioacuten) tiene un valor por defecto que es el que toma cuando no se le asigna expliacutecitamente un valor Los valores de las opciones tambieacuten se pueden fijar en la liacutenea de comandos

Seccioacuten de opciones

Seccioacuten de ejecucioacuten

Seccioacuten de sintaxis

Seccioacuten de lexicografiacutea

6

cuando se ejecuta el generador (lo indicado en la liacutenea de comandos tiene prioridad sobre lo especificado en esta seccioacuten de opciones)

Algunas de las opciones para las que se puede fijar un valor son

Ignore_Case (valor por defecto false)

indica si en el texto analizado ha de distinguirse o no entre letras minuacutesculas y mayuacutesculas

Build_Parser (valor por defecto true)

indica si se genera el analizador sintaacutectico o no

Build_Token_Manager (valor por defecto true)

indica si se genera el analizador lexicograacutefico o no

Sanity_Check (valor por defecto true)

indica si se realizan comprobaciones sobre la gramaacutetica sintaacutectica

Debug_Parser (valor por defecto false)

indica si se genera una traza para el anaacutelisis leacutexico-sintaacutectico

Error_Reporting (valor por defecto true)

indica si los mensajes de error emitidos son maacutes o menos explicativos

Static (valor por defecto true)

con el valor por defecto los meacutetodos de los analizadores leacutexico y sintaacutectico se generan con el des-criptor estaacutetico (static)

Los nombres de las opciones pueden escribirse con letras minuacutesculas o mayuacutesculas los valores de las op-ciones son en la mayoriacutea de los casos un valor entero o un valor loacutegico Si se incluye la seccioacuten se empie-za poniendo la palabra reservada options en este caso al menos ha de ponerse una opcioacuten

La forma de esta seccioacuten es

options nombreOpcion1 = valorOpcion1

nombreOpcion2 = valorOpcion2

∙ ∙ ∙ ∙

nombreOpcionk = valorOpcionk

loz Seccioacuten de ejecucioacutenEn esta seccioacuten se pone el coacutedigo Java que contiene la llamada al analizador generado para que se realice el anaacutelisis de un determinado texto de entrada Tambieacuten se establece aquiacute el nombre de la especificacioacuten que es el nombre que se toma para formar los nombres de parte de los ficheros (clases) generados

La seccioacuten estaacute delimitada por dos palabras reservadas ambas acompantildeadas por un mismo nombre (puesto entre pareacutentesis) ese nombre es el que se da a la especificacioacuten Entre esas dos palabras ha de ponerse una clase directora para el proceso de anaacutelisis el nombre de esa clase directora ha de coincidir con el nombre dado a la especificacioacuten

PARSER_BEGIN (NombreDeLaEspecif)

7

∙ ∙ ∙ ∙

public class NombreDeLaEspecif

∙ ∙ ∙ ∙

∙ ∙ ∙ ∙

PARSER_END (NombreDeLaEspecif)

Una posible versioacuten sencilla de la clase directora es

public class NombreDeLaEspecif public static void main (String[] argum) throws ParseException NombreDeLaEspecif anLeSi = new NombreDeLaEspecif (Systemin) anLeSisimboloPrograma()

se crea el objeto anLeSi para poder aplicar los meacutetodos de anaacutelisis se asocia la entrada standard ( Systemin ) a la entrada para el analizador generado se aplica al objeto creado el meacutetodo de anaacutelisis correspondiente al siacutembolo inicial de la gramaacutetica

loz Seccioacuten de sintaxisEn esta seccioacuten se describe la sintaxis del lenguaje para el que se desea generar el analizador usaacutendose pa-ra ello una notacioacuten parecida a la BNF En lo que sigue se expone la forma de las producciones tal y como se escriben en JavaCC ponieacutendolas en comparacioacuten con las producciones de la notacioacuten BNF-Ampliada

bull Reglas sintaacutecticasEl conjunto de reglas que definen un siacutembolo no terminal

ltNombreSimbgt = α1

| α2

∙ ∙ ∙

| αn

se codifica de la siguiente manera

void nombreSimb () α1-jcc

| α2-jcc

∙ ∙ ∙

| αn-jcc

8

- el nombre del siacutembolo no terminal (parte izquierda de las reglas) se escribe como el encabeza-miento de un meacutetodo en Java sin argumentos y sin valor de devolucioacuten (meacutetodo de tipo void) se sigue como en Java la costumbre de escribir los nombres de los meacutetodos empezando con letra mi-nuacutescula

- el siacutembolo dos puntos representa la separacioacuten entre las partes izquierda y derechas de las reglas (en vez del siacutembolo = de la notacioacuten BNF)

- el siacutembolo barra vertical representa la separacioacuten entre distintas partes derechas con la misma parte izquierda (igual que en la notacioacuten BNF)

- despueacutes del siacutembolo dos puntos se pone un bloque de coacutedigo Java este coacutedigo se suele dedicar a la realizacioacuten de tareas semaacutenticas si se pretende hacer soacutelo un anaacutelisis sintaacutectico el bloque que-daraacute vaciacuteo (aunque siempre es obligada su presencia)

- el conjunto de las alternativas (partes derechas de las reglas) estaacute delimitado mediante llaves como si constituyese un bloque de coacutedigo Java

- αi-jcc representa la codificacioacuten de una alternativa (parte derecha) empleaacutendose una nota-cioacuten que se describe a continuacioacuten

bull Siacutembolos no terminales en las partes derechasEn la notacioacuten BNF se pone ltNombreSimbgt en la notacioacuten JavaCC se escribe

nombreSimb()

esto es se pone como si fuese una llamada en Java a un meacutetodo sin argumentos

bull Siacutembolos terminales en las partes derechasSe considera una clasificacioacuten en las piezas sintaacutecticas (siacutembolos terminales de una gramaacutetica sintaacutectica) distinguieacutendose entre piezas sintaacutecticas nominales y anoacutenimas

son nominales las piezas sintaacutecticas a las que se asocia un nombre en otra parte de la especificacioacuten (maacutes adelante se describe la manera de indicar esta asociacioacuten) ese nombre asociado a la pieza es el que se usa al escribir las partes derechas de las producciones En la notacioacuten JavaCC un siacutembolo terminal no-minal se escribe poniendo el nombre delimitado por los caracteres lt y gt

ltnombreTerminalgt

son anoacutenimas las piezas sintaacutecticas que no tienen nombre asociado no se precisa porque la pieza se re -presenta por su propia secuencia de caracteres (lexema) En la notacioacuten JavaCC un siacutembolo terminal anoacutenimo se escribe poniendo su lexema delimitado por comillas por ejemplo

= lt lt= END

bull Metasiacutembolos de opcionalidad y de repeticioacutenEn la notacioacuten BNF se emplean los corchetes para indicar opcionalidad en la notacioacuten JavaCC se utilizan tambieacuten esos mismos siacutembolos

[ α ] en ambas notaciones

En la notacioacuten BNF se emplean las llaves para indicar una repeticioacuten de cero o maacutes veces en la notacioacuten JavaCC se utilizan los pareacutentesis y un asterisco detraacutes del pareacutentesis de cerrar

α en notacioacuten BNF-Ampliada

( α ) en notacioacuten JavaCC

bull Otros metasiacutembolos de la notacioacuten JavaCC

9

La opcionalidad tambieacuten se puede indicar empleando el metasiacutembolo de interrogacioacuten

( α )

Para indicar repeticioacuten de una o maacutes veces se utiliza el metasiacutembolo operador aditivo suma

( α )+

bull Control del final de fichero de entradaPara controlar el final del fichero en el texto analizado ha de antildeadirse una regla sintaacutectica especial que tenga (seguacuten la notacioacuten BNF) la forma

ltProgramaCompletogt = ltProgramagt FF

donde

ltProgramaCompletogt es un nuevo siacutembolo (no terminal) auxiliar antildeadido

ltProgramagt es el siacutembolo inicial de la gramaacutetica sintaacutectica

FF es una representacioacuten del final del fichero

En JavaCC el final de fichero se escribe con la notacioacuten ltEOFgt siendo EOF una palabra reservada la regla sintaacutectica especial antildeadida a la especificacioacuten quedariacutea codificada asiacute

void programaCompleto() programa() ltEOFgt

bull Representacioacuten de la produccioacuten vaciacuteaLas producciones vaciacuteas (reglas sintaacutecticas cuya parte derecha es la palabra vaciacutea ε ) no se pueden repre-sentar expliacutecitamente en una especificacioacuten leacutexico-sintaacutectica escrita en JavaCC Para indicar la presencia de una produccioacuten vaciacutea han de aplicarse las posibilidades de la notacioacuten BNF-Ampliada seguacuten esta nota-cioacuten cuando se indica la opcionalidad de un componente se estaacute especificando impliacutecitamente la palabra vaciacutea (en el caso de que el componente no esteacute presente)

Asiacute pues si se quisiera escribir en JavaCC la construccioacuten sintaacutectica expresada por las reglas

ltComponentegt = α

| ε

deberiacutean de considerarse con la notacioacuten equivalente

ltComponentegt = [ α ]

y si hubiera varias alternativas no vaciacuteas

ltComponentegt = α | β

| ε

habriacutea que considerarlas como

ltComponentegt = [ α | β ]

o tambieacuten podriacutea verse como

ltComponentegt = α

10

| [ β ]

Como ejemplo sea la especificacioacuten sintaacutectica de una llamada a una funcioacuten que puede tener cero o maacutes paraacutemetros separados por comas aunque no haya paraacutemetros los pareacutentesis estaacuten presentes Las siguien-tes reglas definen esta estructura

ltLlamadagt = id ( ltParametrosgt )

ltParametrosgt = ltExpresiongt ltExpresiongt | ε

En JavaCC la definicioacuten del siacutembolo ltParametrosgt se puede escribir como sigue (noacutetese que aquiacute los pareacutentesis empleados para aplicar el operador son metasiacutembolos)

void parametros() [ expresion() ( expresion() ) ]

loz Seccioacuten de lexicografiacuteaEn esta seccioacuten se indica la lexicografiacutea del lenguaje para el que se va a generar el analizador la notacioacuten de JavaCC que representa la forma de cada una de las piezas sintaacutecticas es una variante de las bien conoci-das expresiones regulares

bull El nombre de las piezas sintaacutecticas nominalesPara asociar un nombre a una pieza sintaacutectica y para al mismo tiempo definir la forma de la pieza se em-plea la notacioacuten

TOKEN lt nombreTerminal Expresioacuten-Regular gt

- TOKEN es una palabra reservada detraacutes de ella se pone el separador dos puntos

- nombreTerminal representa el nombre que se quiere asociar a la pieza sintaacutectica

- Expresioacuten-Regular indica una expresioacuten regular que especifica la forma de la pieza

- el nombre y la expresioacuten se separan mediante el caraacutecter dos puntos y conjuntamente se deli -mitan mediante los caracteres lt y gt a su vez toda la asociacioacuten se delimita con llaves

bull Expresiones regulares en JavaCCLas expresiones regulares en una especificacioacuten JavaCC se escriben de manera parecida a la forma utiliza-da en otras conocidas notaciones aunque con ciertas peculiaridades A continuacioacuten se mencionan los as-pectos maacutes significativos de las expresiones regulares JavaCC y algunos ejemplos

Los operadores de las expresiones son

| unioacuten

repeticioacuten cero o maacutes veces

+ repeticioacuten una o maacutes veces

opcionalidad

11

Los pareacutentesis permiten fijar el orden de aplicacioacuten de los operadores Una peculiaridad de las expresiones regulares JavaCC es que los operadores de repeticioacuten y de opcionalidad requieren que el componente al que se aplican esteacute en todo caso delimitado por pareacutentesis

Se pueden citar las siguientes caracteriacutesticas baacutesicas de las expresiones regulares en JavaCC

Se tiene la posibilidad de definir conjuntos de caracteres poniendo entre corchetes la relacioacuten de carac-teres separados por comas y cada caraacutecter delimitado por comillas En un conjunto se puede fijar un ran-go de caracteres poniendo el primero y el uacuteltimo separados por el siacutembolo guioacuten (-) A veces resulta maacutes coacutemodo indicar los caracteres que no pertenecen al conjunto para ello se puede definir el comple -mentario de un conjunto poniendo el siacutembolo ~ delante del corchete inicial

Para indicar que una secuencia de caracteres colocados consecutivamente constituye por siacute misma una expresioacuten regular (o un componente de una expresioacuten) se escribe la secuencia delimitada por comillas El siacutembolo se emplea como metacaraacutecter para indicar que el siacutembolo que estaacute inmediatamente a su derecha se considere como caraacutecter del alfabeto no como posible metacaraacutecter (operador delimitador)

En una expresioacuten regular se pueden incluir como si fueran caracteres las secuencias de escape con la misma notacioacuten y el mismo significado que en Java o en C

Noacutetese que en una produccioacuten sintaacutectica la opcionalidad se puede especificar de dos formas con corchetes o con el siacutembolo de interrogacioacuten sin embargo en una expresioacuten regular la opcionalidad soacutelo se puede es-pecificar mediante el siacutembolo de interrogacioacuten (los corchetes en las expresiones regulares son para definir conjuntos de caracteres)

A continuacioacuten se muestran ejemplos de expresiones regulares escritas seguacuten la notacioacuten JavaCC

ENDsecuencia de las tres letras mayuacutesculas que conforman la palabra END

whilesecuencia de las cinco letras minuacutesculas que conforman la palabra while

ncaraacutecter representativo del fin de liacutenea (secuencia de escape)

rcaraacutecter representativo del retorno de carro (secuencia de escape)

caraacutecter dos puntos

=secuencia de dos caracteres (siacutembolo de asignacioacuten en Pascal)

gtcaraacutecter representativo del operador de relacioacuten mayor

(i | I)(f | F)palabra ldquoifrdquo escrita con letras minuacutesculas o mayuacutesculas (cuatro combinaciones)

if | IFpalabra ldquoifrdquo escrita soacutelo con minuacutesculas o soacutelo con mayuacutesculas (dos combinaciones)

12

u | o | i | e | auna cualquiera de las vocales minuacutesculas (pero soacutelo una de ellas)

[ A E I O U ]una cualquiera de las vocales mayuacutesculas (pero soacutelo una de ellas)

[ a - z ntilde ]una cualquiera de las letras minuacutesculas incluyendo la letra ntilde

[ a - z A - Z 0 - 9 ]una cualquiera de las letras o de las cifras decimales

([ 0 - 9 ])+constante entera (nuacutemero de una o maacutes cifras)

noacutetese que es incorrecto poner [ 0 - 9 ]+

( + | - )([ 0 - 9 ])+constante entera precedida opcionalmente por un signo

noacutetese que la primera cifra ha de seguir inmediatamente al signo (si existe)

([+ -])([ 0 - 9 ])+representa lo mismo que la expresioacuten anterior

[ a - z ] ([ a - z 0 - 9 ])una letra minuacutescula seguida de cero o maacutes letras minuacutesculas o cifras decimales

~[a - z ntilde A - Z Ntilde]cualquier caraacutecter que no sea una letra

noacutetese que la expresioacuten representa un uacutenico caraacutecter

~[n r]cualquier caraacutecter (un uacutenico caraacutecter) excepto el fin de liacutenea y el retorno de carro

~[ ]cualquier caraacutecter del alfabeto (un uacutenico caraacutecter)

([ 0 - 9 ])+ ( ([ 0 - 9 ])+)constante aritmeacutetica con parte decimal opcional

Los modernossecuencia de catorce caracteres Los modernos

noacutetese el uso de para imponer la condicioacuten de caraacutecter del siacutembolo comillas

nsecuencia de cuatro caracteres n

noacutetese que aquiacute no se representa la secuencia de escape

bull Piezas sintaacutecticas internas (ldquoprivaterdquo)

13

Es posible definir piezas sintaacutecticas auxiliares llamadas ldquointernasrdquo porque no se comunican al analizador sintaacutectico sino que soacutelo sirven para facilitar la escritura de la forma de las piezas sintaacutecticas que siacute se co-munican con el uso de las piezas internas es posible evitar repeticiones de escritura y hacer maacutes legible la especificacioacuten de las caracteriacutesticas lexicograacuteficas Las piezas internas permiten dar nombre a subexpre-siones regulares que a su vez pueden emplearse como componentes para escribir expresiones maacutes com-plejas que incorporan esos nombres Para indicar que una pieza sintaacutectica se considere como interna se pone el siacutembolo delante del nombre asociado a la pieza

A continuacioacuten se muestra un ejemplo tiacutepico de utilizacioacuten de las piezas internas en una especificacioacuten le-xicograacutefica en JavaCC Se quieren definir tres piezas sintaacutecticas que representan un nombre una constante entera y una constante decimal una posible especificacioacuten es

TOKEN ltnombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN ltcteEntera ( [ 0 - 9 ] )+ gt

TOKEN ltcteDecimal ( [ 0 - 9 ] )+ ( [ 0 - 9 ] )+ gt

Con la inclusioacuten de tres piezas sintaacutecticas internas auxiliares esta misma especificacioacuten podriacutea escribirse de la siguiente manera

TOKEN lt letra [ a - z A - Z ntilde Ntilde ] gt

TOKEN lt cifra [ 0 - 9 ] gt

TOKEN lt numero ( ltcifragt )+ gt

TOKEN lt nombre ( ltletragt )+ gt

TOKEN lt cteEntera ltnumerogt gt

TOKEN lt cteDecimal ltnumerogt ltnumerogt gt

Los nombres letra cifra y numero son exclusivos (internos) del analizador lexicograacutefico y no se pueden emplear como nombres de piezas en la especificacioacuten de las reglas sintaacutecticas cuando esos nom-bres se usan en la definicioacuten de otras piezas han de escribirse delimitados por los siacutembolos lt y gt

bull Distincioacuten restringida entre minuacutesculas y mayuacutesculasSe tiene la opcioacuten Ignore_Case para determinar si en el anaacutelisis del texto de entrada se distingue entre las letras minuacutesculas y las mayuacutesculas el valor por defecto false indica que siacute se diferencian si se asig-na el valor true a esta opcioacuten entonces no se diferencian en ninguacuten caso con independencia de que en los patrones que se definan para las piezas sintaacutecticas se pongan minuacutesculas o mayuacutesculas

Pero a veces conviene que la diferenciacioacuten entre minuacutesculas y mayuacutesculas no se aplique de manera ge-neral a toda la entrada sino en particular a las secuencias de caracteres que se acoplen a ciertos patrones Para este fin existe la posibilidad de establecer que en la expresioacuten regular que define el patroacuten de una determinada pieza sintaacutectica no se diferencie entre minuacutesculas y mayuacutesculas ello se consigue poniendo

14

[ IGNORE_CASE ]

inmediatamente detraacutes de la palabra TOKEN con la que empieza la especificacioacuten de la pieza sintaacutectica en la que se pretende la no distincioacuten entre minuacutesculas y mayuacutesculas

Noacutetese que ignore_case tiene dos significados

- es el nombre de un paraacutemetro de la seccioacuten de opciones (puede ponerse en minuacutesculas o mayuacutesculas)- es una palabra reservada que puede aplicarse (escribieacutendola entre corchetes y en mayuacutesculas) a la de -finicioacuten de una pieza sintaacutectica

Por ejemplo sea un lenguaje que en general distingue entre minuacutesculas y mayuacutesculas pero en la codifi -cacioacuten del exponente de una constante real exponencial puede ponerse la letra e en minuacutescula o en ma-yuacutescula En la especificacioacuten JavaCC para ese lenguaje la opcioacuten Ignore_Case ha de tomar el valor false en la definicioacuten de una constante exponencial son posibles las siguientes formas equivalentes

1) sin hacer uso de IGNORE_CASE

TOKEN lt cifras ( [ 0 - 9 ] )+ gt

TOKEN lt signo [+ -] gt

TOKEN lt constante

ltcifrasgt ( ltcifrasgt) [e E] (ltsignogt) ltcifrasgt gt

2) aplicando IGNORE_CASE

TOKEN lt cifras ( [ 0 - 9 ] )+ gt

TOKEN lt signo [+ -] gt

TOKEN [IGNORE_CASE] lt constante

ltcifrasgt ( ltcifrasgt) e (ltsignogt) ltcifrasgt gt

bull Secuencias de caracteres sin efecto en la estructura sintaacutecticaEn los lenguajes de programacioacuten suele haber secuencias de caracteres que no forman parte de la estructu-ra sintaacutectica de un programa esas secuencias son detectadas por el analizador lexicograacutefico pero no son comunicadas al sintaacutectico no han de considerarse como piezas sino que han de saltarse En JavaCC se pueden definir estas secuencias mediante la palabra reservada SKIP la notacioacuten de una especificacioacuten con la palabra SKIP de una secuencia que ha de saltarse es la misma que la notacioacuten usada para las espe-cificaciones con la palabra TOKEN de las piezas que han de comunicarse

Los dos casos maacutes habituales de secuencias que no se comunican al analizador sintaacutectico son

a) los caracteres separadores que no tienen significado en el texto analizadoen los lenguajes de codificacioacuten en formato libre hay caracteres del texto fuente que han de ignorarse en la mayoriacutea de los casos estos caracteres son el espacio en blanco el tabulador el retorno de carro y el fin de liacutenea esta caracteriacutestica se especifica

SKIP |t | r |n

b) los comentarios

15

en el anaacutelisis sintaacutectico de un programa los comentarios han de ignorarse una manera de especificar esta circunstancia es poniendo en una definicioacuten SKIP la expresioacuten regular que denota la forma de los comentarios del lenguaje por ejemplo para los comentarios de una liacutenea de Java se pondriacutea

SKIP

lt (~ [n r]) gt

Tareas asociadas a la estructura sintaacutecticaJavaCC genera un analizador sintaacutectico descendente recursivo constituido por un conjunto de meacutetodos ca-da siacutembolo no terminal de la gramaacutetica sintaacutectica tiene asociado un meacutetodo de anaacutelisis (el meacutetodo tiene el mismo nombre que el siacutembolo correspondiente) Un meacutetodo de anaacutelisis ademaacutes de la realizacioacuten de la parte del anaacutelisis sintaacutectico que le corresponde puede llevar a cabo otras tareas complementarias que sirvan para efectuar un anaacutelisis maacutes complejo del texto de entrada

loz Bloque para un siacutemboloAl describir la forma de las reglas sintaacutecticas de un siacutembolo no terminal en una especificacioacuten JavaCC ya se ha visto que detraacutes de la definicioacuten del nombre del siacutembolo y antes de empezar con las diferentes alter-nativas que tiene asociadas se incluye un bloque de coacutedigo Java (en el ejemplo de presentacioacuten este bloque estaacute vaciacuteo en todos los siacutembolos no terminales de la gramaacutetica) Cada bloque situado en esa posicioacuten puede considerarse ligado al siacutembolo no terminal en cuya descripcioacuten estaacute incluido el coacutedigo Java del bloque se traslada literalmente al principio del meacutetodo generado correspondiente al siacutembolo no terminal y por lo tan-to es lo primero que se considera (se ejecuta) cuando en el transcurso del proceso de anaacutelisis se realiza una llamada al meacutetodo Asiacute pues cada siacutembolo no terminal siempre tiene asociado un uacutenico bloque de coacute-digo Java ndashbloque de presencia obligada aunque pueda estar vaciacuteo- en este bloque se pueden poner de-claraciones y sentencias

loz Acciones sintaacutecticasAdemaacutes del bloque asociado a cada uno de los siacutembolos no terminales de la gramaacutetica se pueden incluir otros bloques de coacutedigo Java intercalados en cualquiera de las partes derechas de las reglas sintaacutecticas Los bloques incluidos en las partes derechas se denominan ldquoacciones sintaacutecticasrdquo (parser actions) ya que son trozos de coacutedigo que pueden considerarse asociados a los puntos de la estructura sintaacutectica donde estaacuten co-locados Una accioacuten sintaacutectica se puede poner en cualquier posicioacuten al principio al final o en el medio de la parte derecha de una regla precedida o seguida de siacutembolos terminales o no terminales el coacutedigo Java de una accioacuten sintaacutectica se traslada literalmente al meacutetodo asociado al siacutembolo no terminal de la parte izquierda de la regla de manera que se ejecuta cada vez que el analizador transcurre por el punto de la estructura sintaacutectica donde se ha incorporado la accioacuten (esto es las acciones sintaacutecticas se incorporan al coacutedigo del analizador sintaacutectico) No obstante su nombre en los bloques de las acciones sintaacutecticas suele ponerse coacutedigo dedicado a la realizacioacuten de tareas semaacutenticas

Si en el bloque asociado a un siacutembolo no terminal se ponen declaraciones los nombres ahiacute definidos son accesibles en todas las acciones sintaacutecticas intercaladas en las partes derechas de las reglas de ese siacutembolo la justificacioacuten es sencilla el coacutedigo del bloque asociado al siacutembolo se incluye al principio de un meacutetodo y el coacutedigo de las acciones pertenece a ese mismo meacutetodo

∆ Ejemplo 1

Se considera de nuevo el ejemplo de presentacioacuten ahora se pretende completar el anaacutelisis leacutexico-sintaacutec-tico con la realizacioacuten de las siguientes tareas

- cada vez que se analiza una expresioacuten un teacutermino o un factor se graba el nombre del siacutembolo no terminal correspondiente y

16

- cuando se analiza un teacutermino en el que hay operadores multiplicativos se cuenta su nuacutemero y se graba el resultado (noacutetese que se cuentan los operadores de cada teacutermino pero no se acumula la cuenta de todos los operadores de la expresioacuten)

Para la realizacioacuten de estas tareas se antildeaden bloques de coacutedigo Java En la especificacioacuten que se describe a continuacioacuten puede apreciarse- para los siacutembolos no terminales expresion termino y factor se ha incluido en los blo-ques correspondientes la operacioacuten de grabacioacuten de su nombre que se ejecutaraacute cada vez que se efec-tuacutee una llamada al meacutetodo- en el bloque asociado a termino se tiene la declaracioacuten del contador de operadores (que seraacute una variable local del meacutetodo)- en la parte derecha de la regla que define la estructura de un teacutermino se han incluido dos acciones sintaacutecticas una para contar la cantidad de operadores (situada detraacutes de la pieza sintaacutectica del opera-dor) y otra para grabar la cantidad de operadores del teacutermino (situada al final de la parte derecha)- se ha definido el meacutetodo grabar para facilitar la escritura del coacutedigo Java

options Ignore_Case = true

PARSER_BEGIN (ExprMin) public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

private static void grabar(String nombre) Systemoutprintln ( -gt + nombre + n)

PARSER_END (ExprMin)

void unaExpresion()

expresion() ltEOFgt

void expresion() grabar(Expresion)

termino() ( + termino() )

void termino() int nAst = 0 grabar(Termino)

17

factor() ( nAst++ factor() ) if (nAst gt 0) Systemoutprintln (n Asteriscos + nAst + n)

void factor() grabar(Factor)

ltconstantegt | ltvariablegt | ( expresion() )

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP | t | n | r

Cuando se incluyen acciones sintaacutecticas la legibilidad del aspecto sintaacutectico de la especificacioacuten queda oscurecida para paliar ese inconveniente puede antildeadirse un comentario que muestre la regla sintaacutectica escueta sin las acciones por ejemplo podriacutea escribirse

void termino() int nAst = 0 grabar(Termino)

factor() ( factor() )

factor()

( nAst++ factor() )

if (nAst gt 0) Systemoutprintln (n Asteriscos + nAst + n)

loz Valor comunicado por un meacutetodoEn todo lo visto hasta ahora los meacutetodos de anaacutelisis sintaacutectico generados por JavaCC no devuelven valor alguno al terminar su tarea de anaacutelisis ya que en la especificacioacuten siempre se ha declarado el nombre de un meacutetodo precedido de void (son meacutetodos de tipo void) Pero en general un meacutetodo de anaacutelisis sintaacutec-tico puede ser de cualquier tipo (primitivo o no) por ello un meacutetodo puede devolver un valor de cualquier tipo (de cualquier clase) al meacutetodo desde el que se efectuoacute la llamada

Esta posibilidad de devolucioacuten de valores tiene una utilidad fundamental en la implementacioacuten de analiza-dores de lenguajes permite la comunicacioacuten de datos (atributos de las entidades analizadas) entre los dife -rentes meacutetodos que colaboran en el anaacutelisis del programa completo

En una especificacioacuten JavaCC la llamada a un meacutetodo ocurre cuando en la parte derecha de una regla estaacute el nombre del siacutembolo no terminal asociado al meacutetodo Cuando un meacutetodo devuelve un valor (tiene un tipo distinto de void) es habitual la recepcioacuten del valor devuelto en una variable en la propia especificacioacuten

18

se puede indicar el nombre de la variable a la que se asignaraacute el valor devuelto tras la llamada para ello se emplea una notacioacuten ideacutentica a la forma que tienen las sentencias de asignacioacuten en el lenguaje Java

dato = nombreSimb()

donde dato es el nombre de la variable a la que se asigna el valor devuelto por el meacutetodo llamado esa va -riable ha de estar oportunamente declarada del mismo tipo que dicho meacutetodo

∆ Ejemplo 2

En el mismo ejemplo de presentacioacuten se desea ademaacutes del anaacutelisis leacutexico-sintaacutectico de la entrada contar la cantidad de operadores que hay en la expresioacuten A continuacioacuten se expone una especificacioacuten JavaCC que es una posible solucioacuten soacutelo se muestran las reglas sintaacutecticas (el resto de la especificacioacuten es igual que en el ejemplo de presentacioacuten)

En la especificacioacuten que se propone puede apreciarse- los siacutembolos no terminales expresion termino y factor se han declarado de tipo int los meacutetodos correspondientes devuelven un valor de tipo entero- el valor devuelto por los meacutetodos sirve para propagar a traveacutes de la estructura sintaacutectica la cuenta de los operadores que hay en el texto analizado- por tratarse de meacutetodos que devuelven un valor es preciso incluir las correspondientes sentencias de devolucioacuten return

void unaExpresion() int nOper nOper = expresion() ltEOFgt Systemoutprintln (Cantidad de operadores = + nOper)

int expresion() int n i n = termino() ( + n++ i = termino() n = n + i )

return n

int termino() int n i n = factor() ( n++ i = factor() n = n + i )

return n

int factor() int n ltconstantegt return 0 | ltvariablegt return 0 | ( n = expresion() ) return n

La especificacioacuten de un factor tambieacuten podriacutea escribirse de esta otra manera

19

int factor() int n = 0

( ltconstantegt | ltvariablegt | ( n = expresion() ) )

return n

A continuacioacuten se expone un nuevo ejemplo para ilustrar la utilidad que proporciona el valor devuelto por un meacutetodo de anaacutelisis sintaacutectico para la propagacioacuten (ascendente) de atributos (propiedades de las entida-des analizadas)

∆ Ejemplo 3

Se pretende ampliar el analizador del ejemplo de presentacioacuten para que se emita un mensaje indicativo cuando la expresioacuten analizada sea una expresioacuten en la que soacutelo aparecen constantes en la solucioacuten que se muestra soacutelo se incluyen las reglas sintaacutecticas (en el resto de la especificacioacuten no hay modificacio-nes)

void unaExpresion() boolean esCte

expresion() ltEOFgt

esCte = expresion() ltEOFgt

if (esCte) Systemoutprintln(nExpresion constanten)

boolean expresion() boolean esCte esTambienCte

termino() ( + termino() )

esCte = termino() ( + esTambienCte = termino() esCte = esCte ampamp esTambienCte )

return esCte

boolean termino() boolean esCte esTambienCte

factor() ( factor() )

esCte = factor() ( esTambienCte = factor() esCte = esCte ampamp esTambienCte )

return esCte

20

boolean factor() boolean esCte

ltconstantegt return true | ltvariablegt return false | ( esCte = expresion() ) return esCte

bull Reconsideracioacuten sobre las producciones vaciacuteasAnteriormente se ha comentado que para representar una produccioacuten vaciacutea en una especificacioacuten JavaCC hay que hacer uso de los metasiacutembolos de opcionalidad para ilustrar esta idea se ha incluido como ejem-plo la especificacioacuten de una lista de paraacutemetros compuesta por cero o maacutes expresiones separadas entre siacute por el caraacutecter coma

void parametros() [ expresion() ( expresion() ) ]

Pero si se aprovecha la posibilidad de incorporar acciones sintaacutecticas se tiene una manera de especificar directamente producciones vaciacuteas para conseguirlo basta considerar una produccioacuten vaciacutea (no tiene nada en su parte derecha) acompantildeada de una accioacuten sintaacutectica cuyo bloque de coacutedigo esteacute vaciacuteo Seguacuten esta posibilidad la especificacioacuten anterior podriacutea tambieacuten escribirse asiacute

void parametros() expresion() ( expresion() ) |

En general la accioacuten sintaacutectica asociada a una produccioacuten vaciacutea no tiene por queacute ser un bloque vaciacuteo de coacutedigo por ejemplo si se quiere que el meacutetodo que analiza los paraacutemetros de una llamada devuelva la cantidad de paraacutemetros encontrados se podriacutea escribir la siguiente especificacioacuten

int parametros()

int cantidad

( expresion() cantidad = 1

( expresion() cantidad++ )

| cantidad = 0 )

return cantidad

y si no quisiera ponerse expliacutecitamente la alternativa de la produccioacuten vaciacutea podriacutea escribirse

int parametros() int cantidad = 0

21

[ expresion() cantidad++ ( expresion() cantidad++ ) ]

return cantidad

Comunicacioacuten entre los analizadores leacutexico y sintaacutecticoCada vez que el analizador sintaacutectico realiza una llamada al analizador lexicograacutefico recibe como resultado de la llamada una representacioacuten de la pieza sintaacutectica encontrada en el texto analizado En los analizadores generados por JavaCC la comunicacioacuten de la pieza se efectuacutea mediante un valor de clase Token Esta cla-se estaacute definida en el fichero Tokenjava que se obtiene en todo caso siempre con el mismo contenido cualquiera que sea la especificacioacuten proporcionada como entrada al generador

Entre los campos de la clase Token se encuentran los siguientes

public int kind

nuacutemero entero que sirve de representacioacuten interna de la pieza sintaacutectica estos nuacutemeros asociados a las piezas son asignados automaacuteticamente por JavaCC los valores considerados pueden consultar-se en el fichero generado de nombre Constantsjava

public String image

cadena que contiene de la secuencia de caracteres (lexema) que constituyen la pieza sintaacutectica co-municada

public int beginLine beginColumn endLine endColumn

posiciones ocupadas (nuacutemeros de liacutenea y columna en el fichero de entrada) por el comienzo y el fi-nal del lexema de la pieza sintaacutectica comunicada

La clase Token tiene otros campos y meacutetodos en el fichero Tokenjava se tienen comentarios des-criptivos sobre todos los campos y meacutetodos que forman parte de la clase todos los campos de la clase se declaran como public

Ya se ha comentado anteriormente que cuando un meacutetodo de anaacutelisis sintaacutectico (asociado a un siacutembolo no terminal) tiene tipo el valor devuelto tras una llamada se puede almacenar en una variable por si se desea utilizar en alguna accioacuten sintaacutectica para ello se escribe

valor = nombreSimbolo()

(siendo valor el nombre de la variable y nombreSimbolo el nombre del siacutembolo no terminal) De manera anaacuteloga el objeto de la clase Token representativo de la pieza sintaacutectica encontrada en la entrada por el analizador lexicograacutefico puede asignarse a una variable del mismo tipo para tener accesibles las ca -racteriacutesticas de la pieza por si se precisaran en alguna accioacuten sintaacutectica para ello en la parte de la especifi -cacioacuten sintaacutectica donde aparezca la pieza sintaacutectica se escribe

dato = lt nombrePieza gt

donde nombrePieza es el nombre de la pieza sintaacutectica y dato es el nombre de una variable de tipo Token en la que se anota el valor indicativo de las propiedades de la pieza sintaacutectica comunicada

Tambieacuten existe otra manera de acceder a las caracteriacutesticas de la pieza sintaacutectica que el analizador lexico -graacutefico comunica al analizador sintaacutectico en la clase del analizador sintaacutectico se tiene declarado el campo

[ static ] public Token token

22

cada vez que el analizador sintaacutectico recibe una pieza sintaacutectica comunicada por el analizador lexicograacutefico se deja anotada en el campo token y ese contenido no se altera hasta que sea proporcionada la siguiente pieza de la entrada

∆ Ejemplo 4

Se considera una ampliacioacuten del ejemplo de presentacioacuten ademaacutes del anaacutelisis de la entrada se pretende obtener una relacioacuten numerada de las variables y las constantes indicando el nuacutemero de liacutenea y el nuacuteme-ro de columna donde estaacuten situadas por ejemplo si en la entrada se tiene la expresioacuten (grabada en dos liacuteneas)

x (y + 11) + 22

en la salida se tendraacute

1- x linea 1 columna 1

2- y linea 1 columna 6

3- 11 linea 1 columna 10

4- 22 linea 2 columna 5

En la especificacioacuten que se propone como solucioacuten puede apreciarse- en la clase del analizador sintaacutectico se han incluido meacutetodos auxiliares en los que se usan los nom-bres image beginLine beginColumn que son campos de un objeto de clase Token

- en la especificacioacuten sintaacutectica de factor se emplea la variable pieza de tipo Token para reco-ger las caracteriacutesticas de las piezas sintaacutecticas detectadas- la variable numero hace de contador para numerar las constantes y variables

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin

static int numero = 0

private static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

private static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

private static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

23

PARSER_END (ExprMin)

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() Token pieza pieza = ltconstantegt numero++ grabarDatosPieza(numero pieza) | pieza = ltvariablegt numero++ grabarDatosPieza(numero pieza) | ( expresion() )

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP | n | t | r

Si se agrupan las dos acciones sintaacutecticas comunes la especificacioacuten de factor tambieacuten puede escri-birse de esta forma

void factor() Token pieza ( pieza = ltconstantegt | pieza = ltvariablegt

) numero++ grabarDatosPieza(numero pieza)

| ( expresion() )

24

Si se accede al campo token de la clase del analizador sintaacutectico se puede resolver este ejemplo pres-cindiendo de la variable pieza

void factor() ( ltconstantegt | ltvariablegt

) numero++ grabarDatosPieza(numero token)

| ( expresion() )

Tareas asociadas a las piezas sintaacutecticasYa se ha visto que es posible asociar bloques de coacutedigo Java a puntos de la estructura sintaacutectica para que se realicen ciertas operaciones cuando el analizador sintaacutectico pase por esa zona de la estructura Tambieacuten es posible asociar coacutedigo Java a las piezas sintaacutecticas para que el analizador lexicograacutefico efectuacutee determina-das tareas cuando detecte en la entrada analizada piezas que tengan asociado coacutedigo

loz Accioacuten ligada a una pieza sintaacutecticaSi se asocia un bloque de coacutedigo a una pieza sintaacutectica se consigue que cada vez que se detecta en la en-trada analizada la presencia de esa pieza se ejecute en ese preciso momento el coacutedigo del bloque asociado a la pieza Estas acciones se denominan ldquoacciones lexicograacuteficasrdquo (lexical actions) el coacutedigo del bloque se traslada literalmente al coacutedigo del analizador lexicograacutefico generado para que se ejecute oportunamente cuando se detecte la pieza ligada al bloque de coacutedigo La accioacuten lexicograacutefica se ejecuta inmediatamente antes que la pieza sea comunicada al analizador sintaacutectico

Se puede asociar una accioacuten lexicograacutefica a una pieza sintaacutectica declarada con TOKEN y tambieacuten a una se-cuencia de caracteres declarada con SKIP ademaacutes de eacutestas hay otras dos formas de declaraciones lexico-graacuteficas (no mencionadas en esta introduccioacuten) que tambieacuten pueden llevar una accioacuten asociada

En una especificacioacuten JavaCC la forma de una pieza sintaacutectica (o bien de una secuencia de caracteres) se define poniendo su descripcioacuten delimitada entre llaves detraacutes de la indicacioacuten TOKEN (o bien SKIP) si se quiere asociar una accioacuten lexicograacutefica a la pieza (o a la secuencia) hay que colocar el bloque de coacutedigo de la accioacuten a continuacioacuten de la descripcioacuten de la forma de la pieza (o de la secuencia) el bloque (delimi-tado por llaves) queda situado entre la descripcioacuten y la llave que cierra la especificacioacuten

La pieza sintaacutectica especial que representa el final del fichero (EOF) tambieacuten puede llevar asociada una ac-cioacuten lexicograacutefica para ello hay que poner

ltgt TOKEN lt EOF gt coacutedigo de la accioacuten asociada

para justificar la presencia de los caracteres ltgt delante de la palabra TOKEN hay que basarse en el con-cepto de ldquoestado lexicograacuteficordquo (cuya explicacioacuten queda fuera del alcance de esta introduccioacuten)

∆ Ejemplo 5

Se considera nuevamente el ejemplo de presentacioacuten como resultado del anaacutelisis de una expresioacuten se pretende obtener una relacioacuten de los pareacutentesis contenidos en la expresioacuten en el mismo orden en que se encuentran ademaacutes por cada salto de liacutenea del texto de entrada se grabaraacute en la salida el caraacutecter y

25

se pasaraacute a una nueva liacutenea tambieacuten se anotaraacute la frase Final del trabajo cuando se alcance el final del fichero de entrada Asiacute por ejemplo si la entrada analizada es

(a + b) (( x )) (y + 7)

los resultados obtenidos seraacuten

() (())()

Final del trabajo

En la especificacioacuten JavaCC que se propone como solucioacuten soacutelo se muestran las reglas sintaacutecticas y le -xicograacuteficas (en el resto no hay cambios) en esta solucioacuten puede apreciarse- las piezas sintaacutecticas correspondientes a los pareacutentesis se han definido de manera nominal para po-der asociarles una accioacuten lexicograacutefica- los caracteres incluidos en la pieza de tipo SKIP se han descompuesto en dos grupos ya que la ac-cioacuten lexicograacutefica incorporada soacutelo afecta a uno de ellos

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ltpAbrirgt expresion() ltpCerrargt

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

TOKEN

26

lt pAbrir ( gt Systemoutprint(()

TOKEN lt pCerrar ) gt Systemoutprint())

ltgtTOKEN lt EOF gt Systemoutprintln ( Final del trabajo )

SKIP n Systemoutprint( n)

SKIP | t | r

loz Bloque de declaraciones lexicograacuteficasCada definicioacuten de una pieza sintaacutectica puede llevar asociada una accioacuten lexicograacutefica que se ejecuta in-mediatamente despueacutes de detectar en la entrada un lexema que se ajusta a la definicioacuten de la pieza cada accioacuten lexicograacutefica es un bloque de coacutedigo Java que puede contener sus propias declaraciones Pero a ve-ces interesa tener declaraciones que sean compartidas por todas las acciones lexicograacuteficas con indepen-dencia de la pieza a la que esteacute asociada cada una de ellas

Para incluir declaraciones que puedan usarse desde el coacutedigo asociado a cualquiera de las piezas sintaacutecti -cas se dispone de una construccioacuten con el siguiente formato

TOKEN_MGR_DECLS

que consta de una palabra reservada seguida del siacutembolo dos puntos seguido de un bloque de coacutedigo Java (delimitado por llaves) esta declaracioacuten se puede colocar en cualquier parte de una especificacioacuten JavaCC si se incluye soacutelo puede ponerse una vez

Todas las declaraciones de variables o de meacutetodos puestas en el bloque de declaraciones lexicograacuteficas compartidas son accesibles desde el coacutedigo de cualquier accioacuten lexicograacutefica la justificacioacuten es sencilla el bloque de declaraciones se traslada al principio del coacutedigo del analizador lexicograacutefico generado y las ac-ciones lexicograacuteficas tambieacuten se trasladan a ese analizador

loz Declaraciones lexicograacuteficas predefinidasDe lo expuesto hasta ahora se aprecia que en una accioacuten lexicograacutefica asociada a una pieza sintaacutectica re-sultan accesibles

- las declaraciones de su propio bloque (si tiene declaraciones)- los componentes del bloque de declaraciones compartidas (si estaacute incluido en la especificacioacuten)

Ademaacutes de esto se tienen otras declaraciones pertenecientes a la clase TokenManager que constituye el analizador lexicograacutefico que tambieacuten son utilizables desde cualquiera de las acciones lexico-graacuteficas estas declaraciones pueden considerarse como ldquopredefinidasrdquo ya que son incorporadas automaacuteti-camente por el generador Entre estas declaraciones accesibles se encuentran las siguientes

[ static ] StringBuffer imagecontiene la cadena de caracteres representativa de la pieza sintaacutectica actual que se acaba de recono-cer en le entrada se trata de un campo de la clase y se declara como static cuando el analizador

27

se ha generado con la opcioacuten (por defecto) Static = true si se generase con la opcioacuten Sta-tic = false no quedariacutea declarada como static

Token matchedToken

variable de clase Token que representa la pieza sintaacutectica actual la que se acaba de encontrar en la entrada se trata de una variable local del meacutetodo al que se ha trasladado el coacutedigo de la accioacuten (y por ello accesible desde ese coacutedigo)

El campo image puede usarse tanto si la accioacuten lexicograacutefica estaacute asociada a una pieza declarada con TOKEN como si lo estaacute a una secuencia declarada con SKIP Sin embargo la variable matchedToken no puede usarse en una accioacuten asociada a un patroacuten especificado como SKIP el motivo es que lo declara-do como SKIP no constituye propiamente una pieza sintaacutectica ya que no se va a comunicar al analizador sintaacutectico

∆ Ejemplo 6

Se propone aquiacute una nueva solucioacuten para el ejemplo nordm 4 expuesto anteriormente

Esta nueva solucioacuten tal y como se expone aquiacute admite algunas mejoras ya que su pretensioacuten soacutelo es ser-vir de ilustracioacuten para algunos aspectos que se acaban de citar (maacutes adelante se propone otra solucioacuten al-ternativa)

En la especificacioacuten de la solucioacuten es posible apreciar esto- las acciones lexicograacuteficas pueden llevar sus propias declaraciones las variables nLineaVar nColumnaVar nLineaCte y nColumnaCte se declaran dentro de las acciones

- la variable matchedToken a la que se hace referencia en el coacutedigo de la accioacuten lexicograacutefica aso-ciada a una variable contiene la informacioacuten sobre la pieza sintaacutectica que se acaba de detectar una va-riable de la expresioacuten analizada anaacutelogamente con la referencia relativa a una constante- el nombre image que aparece en las acciones lexicograacuteficas se refiere al campo de la clase Expr-MinTokenManager (analizador lexicograacutefico) noacutetese que en efecto se comunica como un paraacuteme-tro de clase StringBuffer

- la variable numero se emplea para numerar las constantes y variables encontradas

- los nombres beginLine y beginColumn son campos (puacuteblicos) de la clase Token por ello se pueden aplicar a un objeto de esa clase

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

void unaExpresion()

28

expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n StringBuffer lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

TOKEN lt variable [a-z] gt

int nLineaVar nColumnaVar numero++ nLineaVar = matchedTokenbeginLine nColumnaVar = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaVar nColumnaVar)

TOKEN lt constante ( [0-9] ) + gt

int nLineaCte nColumnaCte

29

numero++ nLineaCte = matchedTokenbeginLine nColumnaCte = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaCte nColumnaCte)

SKIP | n | t | r

La especificacioacuten precedente se puede escribir de otra manera maacutes escueta como se indica a continua-cioacuten (soacutelo se muestran las modificaciones) sobre esta nueva especificacioacuten puede mencionarse- se han eliminado por innecesarias las variables declaradas dentro de las acciones lexicograacuteficas- el nombre image que aparece en el bloque de las declaraciones compartidas se refiere al campo de la clase Token noacutetese que en efecto se aplica a un objeto de esa clase y se comunica como un paraacute -metro de tipo String

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

TOKEN lt variable [a-z] gt

numero++ grabarDatosPieza(numero matchedToken)

TOKEN lt constante ( [0-9] ) + gt

numero++ grabarDatosPieza(numero matchedToken)

loz Accioacuten lexicograacutefica comuacutenSi hubiera que realizar la misma tarea tras la deteccioacuten de todas y cada una de las piezas sintaacutecticas una posibilidad incoacutemoda seriacutea repetir el mismo coacutedigo en todas las acciones lexicograacuteficas para evitar tanta reiteracioacuten se tiene la opcioacuten Common_Token_Action su valor por defecto es false pero si se le asigna el valor true es posible definir una ldquoaccioacuten lexicograacutefica comuacutenrdquo que se escribe una uacutenica vez y se aplica a todas las piezas sintaacutecticas

30

Cuando la opcioacuten Common_Token_Action se establece con el valor true es obligado incluir el blo-que de declaraciones lexicograacuteficas compartidas (TOKEN_MGR_DECLS) Ademaacutes en ese bloque se preci-sa la definicioacuten de un meacutetodo cuyo encabezamiento sea

[ static ] void CommonTokenAction(Token t)

el coacutedigo de este meacutetodo constituye la accioacuten lexicograacutefica comuacuten que se ejecutaraacute despueacutes de la deteccioacuten de cualquier pieza sintaacutectica el meacutetodo tiene un paraacutemetro de tipo Token mediante el que se puede co-municar la informacioacuten correspondiente a la pieza que se acaba de encontrar en la entrada analizada

Si una pieza sintaacutectica tiene asociada una accioacuten lexicograacutefica propia la accioacuten lexicograacutefica comuacuten (si es-taacute definida) se ejecutaraacute inmediatamente despueacutes de realizar la accioacuten propia

La accioacuten lexicograacutefica comuacuten se aplica a todas las piezas sintaacutecticas definidas como TOKEN bien sea no-minalmente (en las reglas lexicograacuteficas) o bien anoacutenimamente (incorporadas dentro de las reglas sintaacutecti-cas) sin embargo no se aplica cuando la secuencia acoplada se corresponde con una expresioacuten regular es-pecificada como SKIP (noacutetese que por el contrario una especificacioacuten SKIP siacute puede tener asociada una accioacuten lexicograacutefica propia)

El coacutedigo del meacutetodo CommonTokenAction se traslada como un componente maacutes del bloque de decla-raciones compartidas (TOKEN_MGR_DECLS) al principio de la clase del analizador lexicograacutefico

∆ Ejemplo 7

Se pretende ahora ampliar el ejemplo de presentacioacuten con la obtencioacuten de una relacioacuten numerada de los lexemas de las piezas sintaacutecticas encontradas en la entrada analizada en el caso de las variables y de las constantes antes del lexema se mostraraacute un indicativo (Var Const) por ejemplo si la expresioacuten de la entrada es

( a + 987 ) b

se grabaraacute la salida

( 1Var a 2+ 3Const 987 4) 5 6Var b 7 8

En la solucioacuten que se propone a continuacioacuten se muestran la definicioacuten de las opciones y las reglas sin-taacutecticas y lexicograacuteficas (en el resto no hay modificaciones) puede resaltarse lo siguiente- las llamadas al meacutetodo CommonTokenAction se incorporan automaacuteticamente no hay que incluir-las en la especificacioacuten como paraacutemetro de llamada a este meacutetodo siempre se emplea el objeto que re -presenta la pieza que se acaba de detectar- la accioacuten lexicograacutefica comuacuten realiza la actualizacioacuten del contador de piezas y la grabacioacuten del lexe-ma de la pieza detectada con su nuacutemero de orden- la accioacuten lexicograacutefica comuacuten tambieacuten se aplica a las piezas sintaacutecticas anoacutenimas (pareacutentesis y ope-radores) y a la pieza especial que representa el final del fichero (EOF)

- las piezas sintaacutecticas ltconstantegt y ltvariablegt tienen asociada una accioacuten lexicograacutefica propia que se ejecuta antes de la accioacuten lexicograacutefica comuacuten- los caracteres que se saltan (SKIP) no estaacuten afectados por la accioacuten lexicograacutefica comuacuten

options Ignore_Case = true Common_Token_Action = true

31

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarIndicativo(String indicativo) Systemoutprint(indicativo + )

static void grabarLexema (int num String lexema) Systemoutprint(lexema + + num + n)

static void CommonTokenAction(Token pieza) numero++ grabarLexema(numero piezaimage)

TOKEN lt variable [a-z] gt

grabarIndicativo(Var)

32

TOKEN lt constante ( [0-9] ) + gt

grabarIndicativo(Const)

SKIP | n | t | r

A modo de ejemplo de aplicacioacuten de la accioacuten lexicograacutefica comuacuten en lo que sigue se ofrece una solucioacuten para la grabacioacuten (en el fichero predefinido de salida) del texto analizado con las liacuteneas numeradas

∆ Ejemplo 8 [ Listado del texto analizado ]Se desea completar el ejemplo de presentacioacuten con la grabacioacuten de un listado con las liacuteneas numeradas del texto analizado (noacutetese que una expresioacuten puede escribirse ocupando varias liacuteneas) No se muestra la especificacioacuten sintaacutectica debido a que no hay ninguna modificacioacuten en ella Los aspectos resentildeables de la solucioacuten que se propone son- se usa la variable numLin como contador de liacuteneas

- en la variable linea se van yuxtaponiendo los lexemas de las piezas sintaacutecticas de una liacutenea esta yuxtaposicioacuten se realiza mediante la accioacuten lexicograacutefica comuacuten- las secuencias reconocidas con los patrones especificados como SKIP no implican la ejecucioacuten de la accioacuten lexicograacutefica comuacuten por ello su lexema (almacenado en la variable image) ha de yuxtaponer-se expliacutecitamente- hay que separar el patroacuten correspondiente al fin de liacutenea para detectar el momento en que ha de gra-barse la liacutenea completa cuyo final se ha alcanzado

options Ignore_Case = true Common_Token_Action = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(nnAnalisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

TOKEN_MGR_DECLS static int numLin = 0 static String linea =

33

static void CommonTokenAction(Token pieza) linea = linea + piezaimage

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP lt | t | r gt linea = linea + image

SKIP n numLin++ Systemoutprintln(numLin + + linea) linea =

A modo de recapitulacioacutenↄ Accioacuten lexicograacutefica Accioacuten sintaacutectica

bull accioacuten lexicograacutefica bloque de coacutedigo Java asociado a una pieza sintaacutectica nominal (TOKEN) o a una secuencia de caracteres (SKIP) se ejecuta cuando se detecta en la entrada la pieza o la secuencia corres-pondiente no se puede aplicar a piezas sintaacutecticas anoacutenimas (autodefinidas con comillas)bull accioacuten sintaacutectica bloque de coacutedigo Java asociado a un punto de la estructura sintaacutectica (el punto indica-do por el sitio de la produccioacuten donde se inserta el bloque) se ejecuta cuando el anaacutelisis de la entrada pa-sa por ese punto de la estructura

ↄ Valor de una pieza sintaacutectica comunicadaEl analizador lexicograacutefico comunica al analizador sintaacutectico una pieza a traveacutes de un valor de la clase Token (fichero Tokenjava) un objeto de la clase Token representa las caracteriacutesticas de la pieza comunicada En la clase Token entre otros se tienen los campos

String imagelexema de la pieza

int beginLine beginColumn endLine endColumnposicioacuten del lexema en el fichero analizado (indicada por los nuacutemeros de fila y de columna de su comienzo y de su terminacioacuten)

ↄ Valor asociado a un siacutembolo

34

En las producciones que especifican la sintaxis se pueden incluir asignaciones que sirven para dejar ano-tado el valor asociado a un siacutembolo

bull siacutembolo no terminal valor = nombreSimbolo()el meacutetodo de anaacutelisis asociado a nombreSimbolo se ha declarado de un cierto tipo (distinto del tipo void) valor ha de ser una variable del mismo tipo que el meacutetodo

bull siacutembolo terminal dato = lt nombrePieza gtel valor asignado es el valor de la clase Token representativo de la pieza sintaacutectica (nominal) dato ha de ser una variable de tipo Token

ↄ Miradas sobre la pieza sintaacutectica comunicadaLa pieza sintaacutectica comunicada puede mirarse desde cada uno de los dos analizadores

bull analizador sintaacutecticola pieza comunicada (desde el analizador lexicograacutefico) se tiene anotada en un campo de la clase del analizador sintaacutectico declarado como

(static) public Token token

este valor es accesible desde el coacutedigo de todos los meacutetodos de anaacutelisis sintaacutectico el contenido del campo variacutea cuando se recibe una nueva pieza sintaacutectica

bull analizador lexicograacuteficola pieza dispuesta para ser comunicada (al analizador sintaacutectico) se tiene anotada en una variable local declarada como

Token matchedToken

este valor es accesible para el coacutedigo de todas las acciones lexicograacuteficas

ↄ El lexema de la pieza comunicadaEl lexema de la pieza sintaacutectica comunicada se encuentra disponible en dos sitios distintos que compar-ten el mismo nombre pero que son de distinto tipo y se usan de distinta manera

bull String image

es un campo de la clase TOKEN puede consultarse en relacioacuten con un objeto de esa clase que esteacute disponible

bull StringBuffer image

es un campo de la clase del analizador lexicograacutefico ( ∙ ∙ ∙ ∙ TokenManagerjava) es accesible desde el coacutedigo de la clase y por lo tanto desde todas las acciones lexicograacuteficas (bloques de coacute-digo que estaacuten incorporados al analizador lexicograacutefico)

se trata de un valor disponible tanto para las piezas sintaacutecticas nominales (TOKEN) como para las secuencias que se saltan (SKIP)

ↄ Resumen relativo a la lexicografiacutea

TOKEN pieza sintaacutectica nominal∙ ∙ ∙ pieza sintaacutectica anoacutenimaSKIP secuencia de caracteres que no forman pieza

35

Precisiones sobre el analizador sintaacutectico generadoPuede decirse que en su funcionamiento maacutes sencillo cuando no se emplean las posibilidades de examen por adelantado (ldquolookaheadrdquo) JavaCC comprueba que las reglas sintaacutecticas cumplen la condicioacuten LL(1) y generan un analizador descendente-predictivo-recursivo Pero esta apreciacioacuten hay que matizarla ya que el analizador sintaacutectico generado no se ajusta en sentido estricto al modelo de analizador LL(1)

En el modelo de anaacutelisis LL(1) el orden del examen de las distintas alternativas de expansioacuten de un siacutembo-lo no terminal es indiferente ya que se conocen los siacutembolos directores de cada una de ellas (que resultan ser conjuntos disjuntos) en la implementacioacuten del subprograma de anaacutelisis asociado al siacutembolo no terminal ltNombreSimbgt cuyas producciones son

ltNombreSimbgt = α1

| α2

∙ ∙ ∙

| αn

se podriacutea preguntar en cualquier orden si la pieza actual pertenece al conjunto de siacutembolos directores de las distintas reglas Sin embargo en el analizador sintaacutectico generado por JavaCC la implementacioacuten del meacute-todo asociado al siacutembolo no terminal ltNombreSimbgt no sigue esa pauta

Si las anteriores reglas sintaacutecticas se transcriben en JavaCC de la forma

void nombreSimb () α1-jcc

| α2-jcc

∙ ∙ ∙

| αn-jcc

1

accioacutenlexicograacutefica

comuacuten

Commom -TokenAction2

accioacutenlexicograacutefica

propia

(accioacuten ligadaa una pieza)3

bloque dedeclaracioneslexicograacuteficas

TOKEN_MGR_DECLS4

declaracioneslexicograacuteficaspredefinidas

(disponibilidad

de uso)TOKENsisisisi∙ ∙ ∙sino--SKIPnosisisi

Las indicaciones si de las columnas 3 y 4 son consecuencia de las indicaciones de la columna 2

La accioacuten lexicograacutefica comuacuten se ejecuta despueacutes de la accioacuten lexicograacutefica propia

36

el meacutetodo de anaacutelisis generado procede de la siguiente manera

se van considerando las alternativas sucesivamente en el mismo orden en el que estaacuten especifica-das primero la de α1 (α1-jcc) despueacutes la de α2 (α2-jcc) etc

para cada alternativa seleccionada se comprueba si la pieza por adelantado coincide con alguno de los siacutembolos iniciales de la parte derecha y si hay coincidencia se prosigue el anaacutelisis con ella si no hay coincidencia se pasa a la alternativa siguiente

siguiendo con este orden si se llega al final de las alternativas sin haberse encontrado coincidencia alguna se produce un error sintaacutectico

En el analizador generado por JavaCC hay una peculiaridad que no se tiene en los analizadores LL(1) es-trictamente considerados la palabra vaciacutea siacute se considera como posible siacutembolo inicial de la parte derecha una regla (esto ocurre siempre que la parte derecha es anulable) De esta peculiaridad se deriva una conse-cuencia que ha de tenerse en cuenta al escribir la especificacioacuten dado que siempre es posible considerar la palabra vaciacutea como la siguiente pieza por adelantado si se llega a examinar la alternativa correspondiente a una produccioacuten anulable el analizador generado siempre seleccionaraacute esa alternativa con independencia de la siguiente pieza que esteacute presente en la entrada que queda por analizar

Asiacute pues si alguna de las producciones que definen un siacutembolo no terminal es anulable es importante el or-den en que se escriben las alternativas en una especificacioacuten sintaacutectica JavaCC el funcionamiento del ana-lizador generado siacute depende de la colocacioacuten elegida para las reglas

Para ilustrar estas peculiaridades del analizador sintaacutectico generado por JavaCC a continuacioacuten se expone un ejemplo

∆ Ejemplo 9

Se quiere obtener un analizador leacutexico-sintaacutectico de textos que se ajustan al siguiente formato una se-cuencia de uno o maacutes nombres seguida del siacutembolo igual seguido de un nuacutemero entero si la secuencia tiene maacutes de un nombre han de estar separados bien por una coma en todos los casos o bien por el siacutem-bolo dos puntos en todos los casos esto es son correctas las secuencias

uno dos tres = 123exclusivo = 0Abajo Arriba = 57

y son incorrectas las secuenciasuno dos tres = 123

exclusivo inclusive = 0

fuera dentro = 56

Una gramaacutetica que define las secuencias con este formato es

ltListagt = nombre ltOtrosNombresgt = numero

ltOtrosNombresgt = nombre nombre | nombre nombre | ε

se trata de una gramaacutetica que cumple la condicioacuten LL(1)- el siacutembolo director para la alternativa vaciacutea es el siacutembolo igual es evidente pues que las tres reglas de ltOtrosNombresgt tienen siacutembolos directores distintos

37

- la decisioacuten de abandonar la repeticioacuten del anaacutelisis de un nuevo nombre (en las dos primeras alterna-tivas de ltOtrosNombresgt) se puede tomar ante la presencia del siacutembolo igual

En la implementacioacuten de un analizador sintaacutectico LL(1) en sentido estricto la seleccioacuten de la alternativa vaciacutea para expandir el siacutembolo ltOtrosNombresgt se hariacutea tras comprobar expliacutecitamente que la pie-za sintaacutectica por adelantado es el siacutembolo igual

El funcionamiento del analizador generado por JavaCC depende del orden de colocacioacuten de las reglas A continuacioacuten se exponen varias posibilidades de especificacioacuten y se comenta el analizador generado en cada caso

bull Especificacioacuten primeraSe transcriben literalmente las reglas tal y como se han escrito antes en la notacioacuten BNF noacutetese que para poder codificar la parte derecha de la regla vaciacutea ha de incluirse una accioacuten sintaacutectica vaciacutea

PARSER_BEGIN (Igualdad)

public class Igualdad

public static void main (String[] argum) throws ParseException Igualdad analisis = new Igualdad(Systemin) analisissecuencia() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (Igualdad)

void secuencia() lista() ltEOFgt

void lista() lt nombre gt otrosNombres() = lt numero gt

void otrosNombres() lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt ) |

TOKEN lt nombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN lt numero ( [ 0 - 9 ] )+ gt

SKIP | r | n

38

El analizador generado funciona correctamente puesto que en el meacutetodo otrosNombres se selec-ciona la alternativa vaciacutea despueacutes de haber comprobado que la pieza por adelantado no es siacutembolo ini-cial de ninguna de las dos alternativas precedentes (no es una coma ni un siacutembolo dos puntos)

bull Especificacioacuten segundaSe considera la misma especificacioacuten anterior pero cambiando el orden de colocacioacuten de las reglas ahora la produccioacuten vaciacutea se coloca como primera alternativa (soacutelo se muestran las partes modifica-das)

void otrosNombres() | lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt )

Con la entrada de esta especificacioacuten el generador JavaCC produce un mensaje en el que se avisa de que las dos alternativas que estaacuten colocadas detraacutes de la alternativa vaciacutea nunca podraacuten seleccionarse aun asiacute se genera un analizador Pero es un analizador que funciona incorrectamente asiacute por ejemplo la entrada

mxa nyz = 57

se considera incorrecta despueacutes de visto el primer nombre se intenta aplicar la produccioacuten vaciacutea (se espera encontrar el siacutembolo igual) pero habriacutea que aplicar la regla cuyo siacutembolo director es la coma

bull Especificacioacuten terceraSe utilizan las posibilidades de la notacioacuten BNF-Ampliada para prescindir de la especificacioacuten expliacuteci-ta de la palabra vaciacutea se aprovecha que JavaCC admite el operador + para indicar repeticiones de una o maacutes veces la alternativa vaciacutea estaacute impliacutecitamente contemplada en la posibilidad de cero repeti-ciones que contempla el operador (soacutelo se muestran las partes modificadas en la especificacioacuten)

void otrosNombres() ( lt nombre gt )+ | ( lt nombre gt )

El meacutetodo otrosNombres() generado hace las comprobaciones en el siguiente orden

- se comprueba si la pieza por adelantado es una coma (siacutembolo inicial de la primera alternativa) y en este caso se selecciona la primera regla- si no es una coma se comprueba si es un siacutembolo dos puntos (siacutembolo inicial de la segunda alter-nativa) y en este caso se selecciona la segunda regla- si no es un siacutembolo dos puntos se considera la palabra vaciacutea como siacutembolo inicial (siempre posi-ble impliacutecitamente) para la segunda alternativa en este caso para que no haya error la pieza por adelantado ha de ser un siacutembolo igual

Se trata pues de un analizador que funciona correctamente

bull Especificacioacuten cuarta

39

Se pone la misma especificacioacuten anterior pero cambiando el orden de consideracioacuten de la regla vaciacutea ahora se incluye impliacutecitamente en la primera alternativa (soacutelo se muestran las partes modificadas)

void otrosNombres() ( lt nombre gt ) | ( lt nombre gt )+

Para esta especificacioacuten el generador JavaCC emite un aviso en el que se indica que la segunda alter -nativa nunca se seleccionaraacute no obstante se genera un analizador que no funciona como se supone que deberiacutea hacerlo por ejemplo el anaacutelisis de la entrada

nyz mxa = 1

produce un error despueacutes de tratado el primer nombre se intenta aplicar la produccioacuten vaciacutea (corres-pondiente a la repeticioacuten de cero veces contemplada en la primera regla) por lo que no se encuentra el esperado siacutembolo igual

40

Page 7: JavaCC

TokenMgrErrorjavatratamiento de errores para el anaacutelisis lexicograacutefico

ParseExceptionjavatratamiento de errores para el anaacutelisis sintaacutectico

SimpleCharStreamjavacomponentes para la realizacioacuten de las tareas de entradasalida del analizador

EspLexSinConstantsjavadefinicioacuten de la representacioacuten interna de las piezas sintaacutecticas

EspLexSinTokenManagerjavaanalizador lexicograacutefico

EspLexSinjavaanalizador sintaacutectico

Puede apreciarse que hay dos categoriacuteas de nombres de ficheros generados los cuatro primeros nombres citados no dependen del nombre de la especificacioacuten considerada los otros nombres de ficheros se forman a partir del nombre dado a la especificacioacuten

Forma de una especificacioacuten JavaCC (versioacuten simplificada)La forma que se describe en lo que sigue es una versioacuten simplificada el generador JavaCC admite especificaciones con otras muchas posi-bilidades no mencionadas en esta introduccioacuten

Una especificacioacuten para el generador JavaCC puede considerarse dividida en cuatro secciones

loz Seccioacuten de opcionesEn esta seccioacuten cuya presencia es optativa se pueden asignar valores a diversos paraacutemetros (llamados op-ciones) que sirven para configurar ciertas caracteriacutesticas del funcionamiento del generador o del analizador generado Cada paraacutemetro (opcioacuten) tiene un valor por defecto que es el que toma cuando no se le asigna expliacutecitamente un valor Los valores de las opciones tambieacuten se pueden fijar en la liacutenea de comandos

Seccioacuten de opciones

Seccioacuten de ejecucioacuten

Seccioacuten de sintaxis

Seccioacuten de lexicografiacutea

6

cuando se ejecuta el generador (lo indicado en la liacutenea de comandos tiene prioridad sobre lo especificado en esta seccioacuten de opciones)

Algunas de las opciones para las que se puede fijar un valor son

Ignore_Case (valor por defecto false)

indica si en el texto analizado ha de distinguirse o no entre letras minuacutesculas y mayuacutesculas

Build_Parser (valor por defecto true)

indica si se genera el analizador sintaacutectico o no

Build_Token_Manager (valor por defecto true)

indica si se genera el analizador lexicograacutefico o no

Sanity_Check (valor por defecto true)

indica si se realizan comprobaciones sobre la gramaacutetica sintaacutectica

Debug_Parser (valor por defecto false)

indica si se genera una traza para el anaacutelisis leacutexico-sintaacutectico

Error_Reporting (valor por defecto true)

indica si los mensajes de error emitidos son maacutes o menos explicativos

Static (valor por defecto true)

con el valor por defecto los meacutetodos de los analizadores leacutexico y sintaacutectico se generan con el des-criptor estaacutetico (static)

Los nombres de las opciones pueden escribirse con letras minuacutesculas o mayuacutesculas los valores de las op-ciones son en la mayoriacutea de los casos un valor entero o un valor loacutegico Si se incluye la seccioacuten se empie-za poniendo la palabra reservada options en este caso al menos ha de ponerse una opcioacuten

La forma de esta seccioacuten es

options nombreOpcion1 = valorOpcion1

nombreOpcion2 = valorOpcion2

∙ ∙ ∙ ∙

nombreOpcionk = valorOpcionk

loz Seccioacuten de ejecucioacutenEn esta seccioacuten se pone el coacutedigo Java que contiene la llamada al analizador generado para que se realice el anaacutelisis de un determinado texto de entrada Tambieacuten se establece aquiacute el nombre de la especificacioacuten que es el nombre que se toma para formar los nombres de parte de los ficheros (clases) generados

La seccioacuten estaacute delimitada por dos palabras reservadas ambas acompantildeadas por un mismo nombre (puesto entre pareacutentesis) ese nombre es el que se da a la especificacioacuten Entre esas dos palabras ha de ponerse una clase directora para el proceso de anaacutelisis el nombre de esa clase directora ha de coincidir con el nombre dado a la especificacioacuten

PARSER_BEGIN (NombreDeLaEspecif)

7

∙ ∙ ∙ ∙

public class NombreDeLaEspecif

∙ ∙ ∙ ∙

∙ ∙ ∙ ∙

PARSER_END (NombreDeLaEspecif)

Una posible versioacuten sencilla de la clase directora es

public class NombreDeLaEspecif public static void main (String[] argum) throws ParseException NombreDeLaEspecif anLeSi = new NombreDeLaEspecif (Systemin) anLeSisimboloPrograma()

se crea el objeto anLeSi para poder aplicar los meacutetodos de anaacutelisis se asocia la entrada standard ( Systemin ) a la entrada para el analizador generado se aplica al objeto creado el meacutetodo de anaacutelisis correspondiente al siacutembolo inicial de la gramaacutetica

loz Seccioacuten de sintaxisEn esta seccioacuten se describe la sintaxis del lenguaje para el que se desea generar el analizador usaacutendose pa-ra ello una notacioacuten parecida a la BNF En lo que sigue se expone la forma de las producciones tal y como se escriben en JavaCC ponieacutendolas en comparacioacuten con las producciones de la notacioacuten BNF-Ampliada

bull Reglas sintaacutecticasEl conjunto de reglas que definen un siacutembolo no terminal

ltNombreSimbgt = α1

| α2

∙ ∙ ∙

| αn

se codifica de la siguiente manera

void nombreSimb () α1-jcc

| α2-jcc

∙ ∙ ∙

| αn-jcc

8

- el nombre del siacutembolo no terminal (parte izquierda de las reglas) se escribe como el encabeza-miento de un meacutetodo en Java sin argumentos y sin valor de devolucioacuten (meacutetodo de tipo void) se sigue como en Java la costumbre de escribir los nombres de los meacutetodos empezando con letra mi-nuacutescula

- el siacutembolo dos puntos representa la separacioacuten entre las partes izquierda y derechas de las reglas (en vez del siacutembolo = de la notacioacuten BNF)

- el siacutembolo barra vertical representa la separacioacuten entre distintas partes derechas con la misma parte izquierda (igual que en la notacioacuten BNF)

- despueacutes del siacutembolo dos puntos se pone un bloque de coacutedigo Java este coacutedigo se suele dedicar a la realizacioacuten de tareas semaacutenticas si se pretende hacer soacutelo un anaacutelisis sintaacutectico el bloque que-daraacute vaciacuteo (aunque siempre es obligada su presencia)

- el conjunto de las alternativas (partes derechas de las reglas) estaacute delimitado mediante llaves como si constituyese un bloque de coacutedigo Java

- αi-jcc representa la codificacioacuten de una alternativa (parte derecha) empleaacutendose una nota-cioacuten que se describe a continuacioacuten

bull Siacutembolos no terminales en las partes derechasEn la notacioacuten BNF se pone ltNombreSimbgt en la notacioacuten JavaCC se escribe

nombreSimb()

esto es se pone como si fuese una llamada en Java a un meacutetodo sin argumentos

bull Siacutembolos terminales en las partes derechasSe considera una clasificacioacuten en las piezas sintaacutecticas (siacutembolos terminales de una gramaacutetica sintaacutectica) distinguieacutendose entre piezas sintaacutecticas nominales y anoacutenimas

son nominales las piezas sintaacutecticas a las que se asocia un nombre en otra parte de la especificacioacuten (maacutes adelante se describe la manera de indicar esta asociacioacuten) ese nombre asociado a la pieza es el que se usa al escribir las partes derechas de las producciones En la notacioacuten JavaCC un siacutembolo terminal no-minal se escribe poniendo el nombre delimitado por los caracteres lt y gt

ltnombreTerminalgt

son anoacutenimas las piezas sintaacutecticas que no tienen nombre asociado no se precisa porque la pieza se re -presenta por su propia secuencia de caracteres (lexema) En la notacioacuten JavaCC un siacutembolo terminal anoacutenimo se escribe poniendo su lexema delimitado por comillas por ejemplo

= lt lt= END

bull Metasiacutembolos de opcionalidad y de repeticioacutenEn la notacioacuten BNF se emplean los corchetes para indicar opcionalidad en la notacioacuten JavaCC se utilizan tambieacuten esos mismos siacutembolos

[ α ] en ambas notaciones

En la notacioacuten BNF se emplean las llaves para indicar una repeticioacuten de cero o maacutes veces en la notacioacuten JavaCC se utilizan los pareacutentesis y un asterisco detraacutes del pareacutentesis de cerrar

α en notacioacuten BNF-Ampliada

( α ) en notacioacuten JavaCC

bull Otros metasiacutembolos de la notacioacuten JavaCC

9

La opcionalidad tambieacuten se puede indicar empleando el metasiacutembolo de interrogacioacuten

( α )

Para indicar repeticioacuten de una o maacutes veces se utiliza el metasiacutembolo operador aditivo suma

( α )+

bull Control del final de fichero de entradaPara controlar el final del fichero en el texto analizado ha de antildeadirse una regla sintaacutectica especial que tenga (seguacuten la notacioacuten BNF) la forma

ltProgramaCompletogt = ltProgramagt FF

donde

ltProgramaCompletogt es un nuevo siacutembolo (no terminal) auxiliar antildeadido

ltProgramagt es el siacutembolo inicial de la gramaacutetica sintaacutectica

FF es una representacioacuten del final del fichero

En JavaCC el final de fichero se escribe con la notacioacuten ltEOFgt siendo EOF una palabra reservada la regla sintaacutectica especial antildeadida a la especificacioacuten quedariacutea codificada asiacute

void programaCompleto() programa() ltEOFgt

bull Representacioacuten de la produccioacuten vaciacuteaLas producciones vaciacuteas (reglas sintaacutecticas cuya parte derecha es la palabra vaciacutea ε ) no se pueden repre-sentar expliacutecitamente en una especificacioacuten leacutexico-sintaacutectica escrita en JavaCC Para indicar la presencia de una produccioacuten vaciacutea han de aplicarse las posibilidades de la notacioacuten BNF-Ampliada seguacuten esta nota-cioacuten cuando se indica la opcionalidad de un componente se estaacute especificando impliacutecitamente la palabra vaciacutea (en el caso de que el componente no esteacute presente)

Asiacute pues si se quisiera escribir en JavaCC la construccioacuten sintaacutectica expresada por las reglas

ltComponentegt = α

| ε

deberiacutean de considerarse con la notacioacuten equivalente

ltComponentegt = [ α ]

y si hubiera varias alternativas no vaciacuteas

ltComponentegt = α | β

| ε

habriacutea que considerarlas como

ltComponentegt = [ α | β ]

o tambieacuten podriacutea verse como

ltComponentegt = α

10

| [ β ]

Como ejemplo sea la especificacioacuten sintaacutectica de una llamada a una funcioacuten que puede tener cero o maacutes paraacutemetros separados por comas aunque no haya paraacutemetros los pareacutentesis estaacuten presentes Las siguien-tes reglas definen esta estructura

ltLlamadagt = id ( ltParametrosgt )

ltParametrosgt = ltExpresiongt ltExpresiongt | ε

En JavaCC la definicioacuten del siacutembolo ltParametrosgt se puede escribir como sigue (noacutetese que aquiacute los pareacutentesis empleados para aplicar el operador son metasiacutembolos)

void parametros() [ expresion() ( expresion() ) ]

loz Seccioacuten de lexicografiacuteaEn esta seccioacuten se indica la lexicografiacutea del lenguaje para el que se va a generar el analizador la notacioacuten de JavaCC que representa la forma de cada una de las piezas sintaacutecticas es una variante de las bien conoci-das expresiones regulares

bull El nombre de las piezas sintaacutecticas nominalesPara asociar un nombre a una pieza sintaacutectica y para al mismo tiempo definir la forma de la pieza se em-plea la notacioacuten

TOKEN lt nombreTerminal Expresioacuten-Regular gt

- TOKEN es una palabra reservada detraacutes de ella se pone el separador dos puntos

- nombreTerminal representa el nombre que se quiere asociar a la pieza sintaacutectica

- Expresioacuten-Regular indica una expresioacuten regular que especifica la forma de la pieza

- el nombre y la expresioacuten se separan mediante el caraacutecter dos puntos y conjuntamente se deli -mitan mediante los caracteres lt y gt a su vez toda la asociacioacuten se delimita con llaves

bull Expresiones regulares en JavaCCLas expresiones regulares en una especificacioacuten JavaCC se escriben de manera parecida a la forma utiliza-da en otras conocidas notaciones aunque con ciertas peculiaridades A continuacioacuten se mencionan los as-pectos maacutes significativos de las expresiones regulares JavaCC y algunos ejemplos

Los operadores de las expresiones son

| unioacuten

repeticioacuten cero o maacutes veces

+ repeticioacuten una o maacutes veces

opcionalidad

11

Los pareacutentesis permiten fijar el orden de aplicacioacuten de los operadores Una peculiaridad de las expresiones regulares JavaCC es que los operadores de repeticioacuten y de opcionalidad requieren que el componente al que se aplican esteacute en todo caso delimitado por pareacutentesis

Se pueden citar las siguientes caracteriacutesticas baacutesicas de las expresiones regulares en JavaCC

Se tiene la posibilidad de definir conjuntos de caracteres poniendo entre corchetes la relacioacuten de carac-teres separados por comas y cada caraacutecter delimitado por comillas En un conjunto se puede fijar un ran-go de caracteres poniendo el primero y el uacuteltimo separados por el siacutembolo guioacuten (-) A veces resulta maacutes coacutemodo indicar los caracteres que no pertenecen al conjunto para ello se puede definir el comple -mentario de un conjunto poniendo el siacutembolo ~ delante del corchete inicial

Para indicar que una secuencia de caracteres colocados consecutivamente constituye por siacute misma una expresioacuten regular (o un componente de una expresioacuten) se escribe la secuencia delimitada por comillas El siacutembolo se emplea como metacaraacutecter para indicar que el siacutembolo que estaacute inmediatamente a su derecha se considere como caraacutecter del alfabeto no como posible metacaraacutecter (operador delimitador)

En una expresioacuten regular se pueden incluir como si fueran caracteres las secuencias de escape con la misma notacioacuten y el mismo significado que en Java o en C

Noacutetese que en una produccioacuten sintaacutectica la opcionalidad se puede especificar de dos formas con corchetes o con el siacutembolo de interrogacioacuten sin embargo en una expresioacuten regular la opcionalidad soacutelo se puede es-pecificar mediante el siacutembolo de interrogacioacuten (los corchetes en las expresiones regulares son para definir conjuntos de caracteres)

A continuacioacuten se muestran ejemplos de expresiones regulares escritas seguacuten la notacioacuten JavaCC

ENDsecuencia de las tres letras mayuacutesculas que conforman la palabra END

whilesecuencia de las cinco letras minuacutesculas que conforman la palabra while

ncaraacutecter representativo del fin de liacutenea (secuencia de escape)

rcaraacutecter representativo del retorno de carro (secuencia de escape)

caraacutecter dos puntos

=secuencia de dos caracteres (siacutembolo de asignacioacuten en Pascal)

gtcaraacutecter representativo del operador de relacioacuten mayor

(i | I)(f | F)palabra ldquoifrdquo escrita con letras minuacutesculas o mayuacutesculas (cuatro combinaciones)

if | IFpalabra ldquoifrdquo escrita soacutelo con minuacutesculas o soacutelo con mayuacutesculas (dos combinaciones)

12

u | o | i | e | auna cualquiera de las vocales minuacutesculas (pero soacutelo una de ellas)

[ A E I O U ]una cualquiera de las vocales mayuacutesculas (pero soacutelo una de ellas)

[ a - z ntilde ]una cualquiera de las letras minuacutesculas incluyendo la letra ntilde

[ a - z A - Z 0 - 9 ]una cualquiera de las letras o de las cifras decimales

([ 0 - 9 ])+constante entera (nuacutemero de una o maacutes cifras)

noacutetese que es incorrecto poner [ 0 - 9 ]+

( + | - )([ 0 - 9 ])+constante entera precedida opcionalmente por un signo

noacutetese que la primera cifra ha de seguir inmediatamente al signo (si existe)

([+ -])([ 0 - 9 ])+representa lo mismo que la expresioacuten anterior

[ a - z ] ([ a - z 0 - 9 ])una letra minuacutescula seguida de cero o maacutes letras minuacutesculas o cifras decimales

~[a - z ntilde A - Z Ntilde]cualquier caraacutecter que no sea una letra

noacutetese que la expresioacuten representa un uacutenico caraacutecter

~[n r]cualquier caraacutecter (un uacutenico caraacutecter) excepto el fin de liacutenea y el retorno de carro

~[ ]cualquier caraacutecter del alfabeto (un uacutenico caraacutecter)

([ 0 - 9 ])+ ( ([ 0 - 9 ])+)constante aritmeacutetica con parte decimal opcional

Los modernossecuencia de catorce caracteres Los modernos

noacutetese el uso de para imponer la condicioacuten de caraacutecter del siacutembolo comillas

nsecuencia de cuatro caracteres n

noacutetese que aquiacute no se representa la secuencia de escape

bull Piezas sintaacutecticas internas (ldquoprivaterdquo)

13

Es posible definir piezas sintaacutecticas auxiliares llamadas ldquointernasrdquo porque no se comunican al analizador sintaacutectico sino que soacutelo sirven para facilitar la escritura de la forma de las piezas sintaacutecticas que siacute se co-munican con el uso de las piezas internas es posible evitar repeticiones de escritura y hacer maacutes legible la especificacioacuten de las caracteriacutesticas lexicograacuteficas Las piezas internas permiten dar nombre a subexpre-siones regulares que a su vez pueden emplearse como componentes para escribir expresiones maacutes com-plejas que incorporan esos nombres Para indicar que una pieza sintaacutectica se considere como interna se pone el siacutembolo delante del nombre asociado a la pieza

A continuacioacuten se muestra un ejemplo tiacutepico de utilizacioacuten de las piezas internas en una especificacioacuten le-xicograacutefica en JavaCC Se quieren definir tres piezas sintaacutecticas que representan un nombre una constante entera y una constante decimal una posible especificacioacuten es

TOKEN ltnombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN ltcteEntera ( [ 0 - 9 ] )+ gt

TOKEN ltcteDecimal ( [ 0 - 9 ] )+ ( [ 0 - 9 ] )+ gt

Con la inclusioacuten de tres piezas sintaacutecticas internas auxiliares esta misma especificacioacuten podriacutea escribirse de la siguiente manera

TOKEN lt letra [ a - z A - Z ntilde Ntilde ] gt

TOKEN lt cifra [ 0 - 9 ] gt

TOKEN lt numero ( ltcifragt )+ gt

TOKEN lt nombre ( ltletragt )+ gt

TOKEN lt cteEntera ltnumerogt gt

TOKEN lt cteDecimal ltnumerogt ltnumerogt gt

Los nombres letra cifra y numero son exclusivos (internos) del analizador lexicograacutefico y no se pueden emplear como nombres de piezas en la especificacioacuten de las reglas sintaacutecticas cuando esos nom-bres se usan en la definicioacuten de otras piezas han de escribirse delimitados por los siacutembolos lt y gt

bull Distincioacuten restringida entre minuacutesculas y mayuacutesculasSe tiene la opcioacuten Ignore_Case para determinar si en el anaacutelisis del texto de entrada se distingue entre las letras minuacutesculas y las mayuacutesculas el valor por defecto false indica que siacute se diferencian si se asig-na el valor true a esta opcioacuten entonces no se diferencian en ninguacuten caso con independencia de que en los patrones que se definan para las piezas sintaacutecticas se pongan minuacutesculas o mayuacutesculas

Pero a veces conviene que la diferenciacioacuten entre minuacutesculas y mayuacutesculas no se aplique de manera ge-neral a toda la entrada sino en particular a las secuencias de caracteres que se acoplen a ciertos patrones Para este fin existe la posibilidad de establecer que en la expresioacuten regular que define el patroacuten de una determinada pieza sintaacutectica no se diferencie entre minuacutesculas y mayuacutesculas ello se consigue poniendo

14

[ IGNORE_CASE ]

inmediatamente detraacutes de la palabra TOKEN con la que empieza la especificacioacuten de la pieza sintaacutectica en la que se pretende la no distincioacuten entre minuacutesculas y mayuacutesculas

Noacutetese que ignore_case tiene dos significados

- es el nombre de un paraacutemetro de la seccioacuten de opciones (puede ponerse en minuacutesculas o mayuacutesculas)- es una palabra reservada que puede aplicarse (escribieacutendola entre corchetes y en mayuacutesculas) a la de -finicioacuten de una pieza sintaacutectica

Por ejemplo sea un lenguaje que en general distingue entre minuacutesculas y mayuacutesculas pero en la codifi -cacioacuten del exponente de una constante real exponencial puede ponerse la letra e en minuacutescula o en ma-yuacutescula En la especificacioacuten JavaCC para ese lenguaje la opcioacuten Ignore_Case ha de tomar el valor false en la definicioacuten de una constante exponencial son posibles las siguientes formas equivalentes

1) sin hacer uso de IGNORE_CASE

TOKEN lt cifras ( [ 0 - 9 ] )+ gt

TOKEN lt signo [+ -] gt

TOKEN lt constante

ltcifrasgt ( ltcifrasgt) [e E] (ltsignogt) ltcifrasgt gt

2) aplicando IGNORE_CASE

TOKEN lt cifras ( [ 0 - 9 ] )+ gt

TOKEN lt signo [+ -] gt

TOKEN [IGNORE_CASE] lt constante

ltcifrasgt ( ltcifrasgt) e (ltsignogt) ltcifrasgt gt

bull Secuencias de caracteres sin efecto en la estructura sintaacutecticaEn los lenguajes de programacioacuten suele haber secuencias de caracteres que no forman parte de la estructu-ra sintaacutectica de un programa esas secuencias son detectadas por el analizador lexicograacutefico pero no son comunicadas al sintaacutectico no han de considerarse como piezas sino que han de saltarse En JavaCC se pueden definir estas secuencias mediante la palabra reservada SKIP la notacioacuten de una especificacioacuten con la palabra SKIP de una secuencia que ha de saltarse es la misma que la notacioacuten usada para las espe-cificaciones con la palabra TOKEN de las piezas que han de comunicarse

Los dos casos maacutes habituales de secuencias que no se comunican al analizador sintaacutectico son

a) los caracteres separadores que no tienen significado en el texto analizadoen los lenguajes de codificacioacuten en formato libre hay caracteres del texto fuente que han de ignorarse en la mayoriacutea de los casos estos caracteres son el espacio en blanco el tabulador el retorno de carro y el fin de liacutenea esta caracteriacutestica se especifica

SKIP |t | r |n

b) los comentarios

15

en el anaacutelisis sintaacutectico de un programa los comentarios han de ignorarse una manera de especificar esta circunstancia es poniendo en una definicioacuten SKIP la expresioacuten regular que denota la forma de los comentarios del lenguaje por ejemplo para los comentarios de una liacutenea de Java se pondriacutea

SKIP

lt (~ [n r]) gt

Tareas asociadas a la estructura sintaacutecticaJavaCC genera un analizador sintaacutectico descendente recursivo constituido por un conjunto de meacutetodos ca-da siacutembolo no terminal de la gramaacutetica sintaacutectica tiene asociado un meacutetodo de anaacutelisis (el meacutetodo tiene el mismo nombre que el siacutembolo correspondiente) Un meacutetodo de anaacutelisis ademaacutes de la realizacioacuten de la parte del anaacutelisis sintaacutectico que le corresponde puede llevar a cabo otras tareas complementarias que sirvan para efectuar un anaacutelisis maacutes complejo del texto de entrada

loz Bloque para un siacutemboloAl describir la forma de las reglas sintaacutecticas de un siacutembolo no terminal en una especificacioacuten JavaCC ya se ha visto que detraacutes de la definicioacuten del nombre del siacutembolo y antes de empezar con las diferentes alter-nativas que tiene asociadas se incluye un bloque de coacutedigo Java (en el ejemplo de presentacioacuten este bloque estaacute vaciacuteo en todos los siacutembolos no terminales de la gramaacutetica) Cada bloque situado en esa posicioacuten puede considerarse ligado al siacutembolo no terminal en cuya descripcioacuten estaacute incluido el coacutedigo Java del bloque se traslada literalmente al principio del meacutetodo generado correspondiente al siacutembolo no terminal y por lo tan-to es lo primero que se considera (se ejecuta) cuando en el transcurso del proceso de anaacutelisis se realiza una llamada al meacutetodo Asiacute pues cada siacutembolo no terminal siempre tiene asociado un uacutenico bloque de coacute-digo Java ndashbloque de presencia obligada aunque pueda estar vaciacuteo- en este bloque se pueden poner de-claraciones y sentencias

loz Acciones sintaacutecticasAdemaacutes del bloque asociado a cada uno de los siacutembolos no terminales de la gramaacutetica se pueden incluir otros bloques de coacutedigo Java intercalados en cualquiera de las partes derechas de las reglas sintaacutecticas Los bloques incluidos en las partes derechas se denominan ldquoacciones sintaacutecticasrdquo (parser actions) ya que son trozos de coacutedigo que pueden considerarse asociados a los puntos de la estructura sintaacutectica donde estaacuten co-locados Una accioacuten sintaacutectica se puede poner en cualquier posicioacuten al principio al final o en el medio de la parte derecha de una regla precedida o seguida de siacutembolos terminales o no terminales el coacutedigo Java de una accioacuten sintaacutectica se traslada literalmente al meacutetodo asociado al siacutembolo no terminal de la parte izquierda de la regla de manera que se ejecuta cada vez que el analizador transcurre por el punto de la estructura sintaacutectica donde se ha incorporado la accioacuten (esto es las acciones sintaacutecticas se incorporan al coacutedigo del analizador sintaacutectico) No obstante su nombre en los bloques de las acciones sintaacutecticas suele ponerse coacutedigo dedicado a la realizacioacuten de tareas semaacutenticas

Si en el bloque asociado a un siacutembolo no terminal se ponen declaraciones los nombres ahiacute definidos son accesibles en todas las acciones sintaacutecticas intercaladas en las partes derechas de las reglas de ese siacutembolo la justificacioacuten es sencilla el coacutedigo del bloque asociado al siacutembolo se incluye al principio de un meacutetodo y el coacutedigo de las acciones pertenece a ese mismo meacutetodo

∆ Ejemplo 1

Se considera de nuevo el ejemplo de presentacioacuten ahora se pretende completar el anaacutelisis leacutexico-sintaacutec-tico con la realizacioacuten de las siguientes tareas

- cada vez que se analiza una expresioacuten un teacutermino o un factor se graba el nombre del siacutembolo no terminal correspondiente y

16

- cuando se analiza un teacutermino en el que hay operadores multiplicativos se cuenta su nuacutemero y se graba el resultado (noacutetese que se cuentan los operadores de cada teacutermino pero no se acumula la cuenta de todos los operadores de la expresioacuten)

Para la realizacioacuten de estas tareas se antildeaden bloques de coacutedigo Java En la especificacioacuten que se describe a continuacioacuten puede apreciarse- para los siacutembolos no terminales expresion termino y factor se ha incluido en los blo-ques correspondientes la operacioacuten de grabacioacuten de su nombre que se ejecutaraacute cada vez que se efec-tuacutee una llamada al meacutetodo- en el bloque asociado a termino se tiene la declaracioacuten del contador de operadores (que seraacute una variable local del meacutetodo)- en la parte derecha de la regla que define la estructura de un teacutermino se han incluido dos acciones sintaacutecticas una para contar la cantidad de operadores (situada detraacutes de la pieza sintaacutectica del opera-dor) y otra para grabar la cantidad de operadores del teacutermino (situada al final de la parte derecha)- se ha definido el meacutetodo grabar para facilitar la escritura del coacutedigo Java

options Ignore_Case = true

PARSER_BEGIN (ExprMin) public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

private static void grabar(String nombre) Systemoutprintln ( -gt + nombre + n)

PARSER_END (ExprMin)

void unaExpresion()

expresion() ltEOFgt

void expresion() grabar(Expresion)

termino() ( + termino() )

void termino() int nAst = 0 grabar(Termino)

17

factor() ( nAst++ factor() ) if (nAst gt 0) Systemoutprintln (n Asteriscos + nAst + n)

void factor() grabar(Factor)

ltconstantegt | ltvariablegt | ( expresion() )

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP | t | n | r

Cuando se incluyen acciones sintaacutecticas la legibilidad del aspecto sintaacutectico de la especificacioacuten queda oscurecida para paliar ese inconveniente puede antildeadirse un comentario que muestre la regla sintaacutectica escueta sin las acciones por ejemplo podriacutea escribirse

void termino() int nAst = 0 grabar(Termino)

factor() ( factor() )

factor()

( nAst++ factor() )

if (nAst gt 0) Systemoutprintln (n Asteriscos + nAst + n)

loz Valor comunicado por un meacutetodoEn todo lo visto hasta ahora los meacutetodos de anaacutelisis sintaacutectico generados por JavaCC no devuelven valor alguno al terminar su tarea de anaacutelisis ya que en la especificacioacuten siempre se ha declarado el nombre de un meacutetodo precedido de void (son meacutetodos de tipo void) Pero en general un meacutetodo de anaacutelisis sintaacutec-tico puede ser de cualquier tipo (primitivo o no) por ello un meacutetodo puede devolver un valor de cualquier tipo (de cualquier clase) al meacutetodo desde el que se efectuoacute la llamada

Esta posibilidad de devolucioacuten de valores tiene una utilidad fundamental en la implementacioacuten de analiza-dores de lenguajes permite la comunicacioacuten de datos (atributos de las entidades analizadas) entre los dife -rentes meacutetodos que colaboran en el anaacutelisis del programa completo

En una especificacioacuten JavaCC la llamada a un meacutetodo ocurre cuando en la parte derecha de una regla estaacute el nombre del siacutembolo no terminal asociado al meacutetodo Cuando un meacutetodo devuelve un valor (tiene un tipo distinto de void) es habitual la recepcioacuten del valor devuelto en una variable en la propia especificacioacuten

18

se puede indicar el nombre de la variable a la que se asignaraacute el valor devuelto tras la llamada para ello se emplea una notacioacuten ideacutentica a la forma que tienen las sentencias de asignacioacuten en el lenguaje Java

dato = nombreSimb()

donde dato es el nombre de la variable a la que se asigna el valor devuelto por el meacutetodo llamado esa va -riable ha de estar oportunamente declarada del mismo tipo que dicho meacutetodo

∆ Ejemplo 2

En el mismo ejemplo de presentacioacuten se desea ademaacutes del anaacutelisis leacutexico-sintaacutectico de la entrada contar la cantidad de operadores que hay en la expresioacuten A continuacioacuten se expone una especificacioacuten JavaCC que es una posible solucioacuten soacutelo se muestran las reglas sintaacutecticas (el resto de la especificacioacuten es igual que en el ejemplo de presentacioacuten)

En la especificacioacuten que se propone puede apreciarse- los siacutembolos no terminales expresion termino y factor se han declarado de tipo int los meacutetodos correspondientes devuelven un valor de tipo entero- el valor devuelto por los meacutetodos sirve para propagar a traveacutes de la estructura sintaacutectica la cuenta de los operadores que hay en el texto analizado- por tratarse de meacutetodos que devuelven un valor es preciso incluir las correspondientes sentencias de devolucioacuten return

void unaExpresion() int nOper nOper = expresion() ltEOFgt Systemoutprintln (Cantidad de operadores = + nOper)

int expresion() int n i n = termino() ( + n++ i = termino() n = n + i )

return n

int termino() int n i n = factor() ( n++ i = factor() n = n + i )

return n

int factor() int n ltconstantegt return 0 | ltvariablegt return 0 | ( n = expresion() ) return n

La especificacioacuten de un factor tambieacuten podriacutea escribirse de esta otra manera

19

int factor() int n = 0

( ltconstantegt | ltvariablegt | ( n = expresion() ) )

return n

A continuacioacuten se expone un nuevo ejemplo para ilustrar la utilidad que proporciona el valor devuelto por un meacutetodo de anaacutelisis sintaacutectico para la propagacioacuten (ascendente) de atributos (propiedades de las entida-des analizadas)

∆ Ejemplo 3

Se pretende ampliar el analizador del ejemplo de presentacioacuten para que se emita un mensaje indicativo cuando la expresioacuten analizada sea una expresioacuten en la que soacutelo aparecen constantes en la solucioacuten que se muestra soacutelo se incluyen las reglas sintaacutecticas (en el resto de la especificacioacuten no hay modificacio-nes)

void unaExpresion() boolean esCte

expresion() ltEOFgt

esCte = expresion() ltEOFgt

if (esCte) Systemoutprintln(nExpresion constanten)

boolean expresion() boolean esCte esTambienCte

termino() ( + termino() )

esCte = termino() ( + esTambienCte = termino() esCte = esCte ampamp esTambienCte )

return esCte

boolean termino() boolean esCte esTambienCte

factor() ( factor() )

esCte = factor() ( esTambienCte = factor() esCte = esCte ampamp esTambienCte )

return esCte

20

boolean factor() boolean esCte

ltconstantegt return true | ltvariablegt return false | ( esCte = expresion() ) return esCte

bull Reconsideracioacuten sobre las producciones vaciacuteasAnteriormente se ha comentado que para representar una produccioacuten vaciacutea en una especificacioacuten JavaCC hay que hacer uso de los metasiacutembolos de opcionalidad para ilustrar esta idea se ha incluido como ejem-plo la especificacioacuten de una lista de paraacutemetros compuesta por cero o maacutes expresiones separadas entre siacute por el caraacutecter coma

void parametros() [ expresion() ( expresion() ) ]

Pero si se aprovecha la posibilidad de incorporar acciones sintaacutecticas se tiene una manera de especificar directamente producciones vaciacuteas para conseguirlo basta considerar una produccioacuten vaciacutea (no tiene nada en su parte derecha) acompantildeada de una accioacuten sintaacutectica cuyo bloque de coacutedigo esteacute vaciacuteo Seguacuten esta posibilidad la especificacioacuten anterior podriacutea tambieacuten escribirse asiacute

void parametros() expresion() ( expresion() ) |

En general la accioacuten sintaacutectica asociada a una produccioacuten vaciacutea no tiene por queacute ser un bloque vaciacuteo de coacutedigo por ejemplo si se quiere que el meacutetodo que analiza los paraacutemetros de una llamada devuelva la cantidad de paraacutemetros encontrados se podriacutea escribir la siguiente especificacioacuten

int parametros()

int cantidad

( expresion() cantidad = 1

( expresion() cantidad++ )

| cantidad = 0 )

return cantidad

y si no quisiera ponerse expliacutecitamente la alternativa de la produccioacuten vaciacutea podriacutea escribirse

int parametros() int cantidad = 0

21

[ expresion() cantidad++ ( expresion() cantidad++ ) ]

return cantidad

Comunicacioacuten entre los analizadores leacutexico y sintaacutecticoCada vez que el analizador sintaacutectico realiza una llamada al analizador lexicograacutefico recibe como resultado de la llamada una representacioacuten de la pieza sintaacutectica encontrada en el texto analizado En los analizadores generados por JavaCC la comunicacioacuten de la pieza se efectuacutea mediante un valor de clase Token Esta cla-se estaacute definida en el fichero Tokenjava que se obtiene en todo caso siempre con el mismo contenido cualquiera que sea la especificacioacuten proporcionada como entrada al generador

Entre los campos de la clase Token se encuentran los siguientes

public int kind

nuacutemero entero que sirve de representacioacuten interna de la pieza sintaacutectica estos nuacutemeros asociados a las piezas son asignados automaacuteticamente por JavaCC los valores considerados pueden consultar-se en el fichero generado de nombre Constantsjava

public String image

cadena que contiene de la secuencia de caracteres (lexema) que constituyen la pieza sintaacutectica co-municada

public int beginLine beginColumn endLine endColumn

posiciones ocupadas (nuacutemeros de liacutenea y columna en el fichero de entrada) por el comienzo y el fi-nal del lexema de la pieza sintaacutectica comunicada

La clase Token tiene otros campos y meacutetodos en el fichero Tokenjava se tienen comentarios des-criptivos sobre todos los campos y meacutetodos que forman parte de la clase todos los campos de la clase se declaran como public

Ya se ha comentado anteriormente que cuando un meacutetodo de anaacutelisis sintaacutectico (asociado a un siacutembolo no terminal) tiene tipo el valor devuelto tras una llamada se puede almacenar en una variable por si se desea utilizar en alguna accioacuten sintaacutectica para ello se escribe

valor = nombreSimbolo()

(siendo valor el nombre de la variable y nombreSimbolo el nombre del siacutembolo no terminal) De manera anaacuteloga el objeto de la clase Token representativo de la pieza sintaacutectica encontrada en la entrada por el analizador lexicograacutefico puede asignarse a una variable del mismo tipo para tener accesibles las ca -racteriacutesticas de la pieza por si se precisaran en alguna accioacuten sintaacutectica para ello en la parte de la especifi -cacioacuten sintaacutectica donde aparezca la pieza sintaacutectica se escribe

dato = lt nombrePieza gt

donde nombrePieza es el nombre de la pieza sintaacutectica y dato es el nombre de una variable de tipo Token en la que se anota el valor indicativo de las propiedades de la pieza sintaacutectica comunicada

Tambieacuten existe otra manera de acceder a las caracteriacutesticas de la pieza sintaacutectica que el analizador lexico -graacutefico comunica al analizador sintaacutectico en la clase del analizador sintaacutectico se tiene declarado el campo

[ static ] public Token token

22

cada vez que el analizador sintaacutectico recibe una pieza sintaacutectica comunicada por el analizador lexicograacutefico se deja anotada en el campo token y ese contenido no se altera hasta que sea proporcionada la siguiente pieza de la entrada

∆ Ejemplo 4

Se considera una ampliacioacuten del ejemplo de presentacioacuten ademaacutes del anaacutelisis de la entrada se pretende obtener una relacioacuten numerada de las variables y las constantes indicando el nuacutemero de liacutenea y el nuacuteme-ro de columna donde estaacuten situadas por ejemplo si en la entrada se tiene la expresioacuten (grabada en dos liacuteneas)

x (y + 11) + 22

en la salida se tendraacute

1- x linea 1 columna 1

2- y linea 1 columna 6

3- 11 linea 1 columna 10

4- 22 linea 2 columna 5

En la especificacioacuten que se propone como solucioacuten puede apreciarse- en la clase del analizador sintaacutectico se han incluido meacutetodos auxiliares en los que se usan los nom-bres image beginLine beginColumn que son campos de un objeto de clase Token

- en la especificacioacuten sintaacutectica de factor se emplea la variable pieza de tipo Token para reco-ger las caracteriacutesticas de las piezas sintaacutecticas detectadas- la variable numero hace de contador para numerar las constantes y variables

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin

static int numero = 0

private static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

private static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

private static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

23

PARSER_END (ExprMin)

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() Token pieza pieza = ltconstantegt numero++ grabarDatosPieza(numero pieza) | pieza = ltvariablegt numero++ grabarDatosPieza(numero pieza) | ( expresion() )

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP | n | t | r

Si se agrupan las dos acciones sintaacutecticas comunes la especificacioacuten de factor tambieacuten puede escri-birse de esta forma

void factor() Token pieza ( pieza = ltconstantegt | pieza = ltvariablegt

) numero++ grabarDatosPieza(numero pieza)

| ( expresion() )

24

Si se accede al campo token de la clase del analizador sintaacutectico se puede resolver este ejemplo pres-cindiendo de la variable pieza

void factor() ( ltconstantegt | ltvariablegt

) numero++ grabarDatosPieza(numero token)

| ( expresion() )

Tareas asociadas a las piezas sintaacutecticasYa se ha visto que es posible asociar bloques de coacutedigo Java a puntos de la estructura sintaacutectica para que se realicen ciertas operaciones cuando el analizador sintaacutectico pase por esa zona de la estructura Tambieacuten es posible asociar coacutedigo Java a las piezas sintaacutecticas para que el analizador lexicograacutefico efectuacutee determina-das tareas cuando detecte en la entrada analizada piezas que tengan asociado coacutedigo

loz Accioacuten ligada a una pieza sintaacutecticaSi se asocia un bloque de coacutedigo a una pieza sintaacutectica se consigue que cada vez que se detecta en la en-trada analizada la presencia de esa pieza se ejecute en ese preciso momento el coacutedigo del bloque asociado a la pieza Estas acciones se denominan ldquoacciones lexicograacuteficasrdquo (lexical actions) el coacutedigo del bloque se traslada literalmente al coacutedigo del analizador lexicograacutefico generado para que se ejecute oportunamente cuando se detecte la pieza ligada al bloque de coacutedigo La accioacuten lexicograacutefica se ejecuta inmediatamente antes que la pieza sea comunicada al analizador sintaacutectico

Se puede asociar una accioacuten lexicograacutefica a una pieza sintaacutectica declarada con TOKEN y tambieacuten a una se-cuencia de caracteres declarada con SKIP ademaacutes de eacutestas hay otras dos formas de declaraciones lexico-graacuteficas (no mencionadas en esta introduccioacuten) que tambieacuten pueden llevar una accioacuten asociada

En una especificacioacuten JavaCC la forma de una pieza sintaacutectica (o bien de una secuencia de caracteres) se define poniendo su descripcioacuten delimitada entre llaves detraacutes de la indicacioacuten TOKEN (o bien SKIP) si se quiere asociar una accioacuten lexicograacutefica a la pieza (o a la secuencia) hay que colocar el bloque de coacutedigo de la accioacuten a continuacioacuten de la descripcioacuten de la forma de la pieza (o de la secuencia) el bloque (delimi-tado por llaves) queda situado entre la descripcioacuten y la llave que cierra la especificacioacuten

La pieza sintaacutectica especial que representa el final del fichero (EOF) tambieacuten puede llevar asociada una ac-cioacuten lexicograacutefica para ello hay que poner

ltgt TOKEN lt EOF gt coacutedigo de la accioacuten asociada

para justificar la presencia de los caracteres ltgt delante de la palabra TOKEN hay que basarse en el con-cepto de ldquoestado lexicograacuteficordquo (cuya explicacioacuten queda fuera del alcance de esta introduccioacuten)

∆ Ejemplo 5

Se considera nuevamente el ejemplo de presentacioacuten como resultado del anaacutelisis de una expresioacuten se pretende obtener una relacioacuten de los pareacutentesis contenidos en la expresioacuten en el mismo orden en que se encuentran ademaacutes por cada salto de liacutenea del texto de entrada se grabaraacute en la salida el caraacutecter y

25

se pasaraacute a una nueva liacutenea tambieacuten se anotaraacute la frase Final del trabajo cuando se alcance el final del fichero de entrada Asiacute por ejemplo si la entrada analizada es

(a + b) (( x )) (y + 7)

los resultados obtenidos seraacuten

() (())()

Final del trabajo

En la especificacioacuten JavaCC que se propone como solucioacuten soacutelo se muestran las reglas sintaacutecticas y le -xicograacuteficas (en el resto no hay cambios) en esta solucioacuten puede apreciarse- las piezas sintaacutecticas correspondientes a los pareacutentesis se han definido de manera nominal para po-der asociarles una accioacuten lexicograacutefica- los caracteres incluidos en la pieza de tipo SKIP se han descompuesto en dos grupos ya que la ac-cioacuten lexicograacutefica incorporada soacutelo afecta a uno de ellos

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ltpAbrirgt expresion() ltpCerrargt

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

TOKEN

26

lt pAbrir ( gt Systemoutprint(()

TOKEN lt pCerrar ) gt Systemoutprint())

ltgtTOKEN lt EOF gt Systemoutprintln ( Final del trabajo )

SKIP n Systemoutprint( n)

SKIP | t | r

loz Bloque de declaraciones lexicograacuteficasCada definicioacuten de una pieza sintaacutectica puede llevar asociada una accioacuten lexicograacutefica que se ejecuta in-mediatamente despueacutes de detectar en la entrada un lexema que se ajusta a la definicioacuten de la pieza cada accioacuten lexicograacutefica es un bloque de coacutedigo Java que puede contener sus propias declaraciones Pero a ve-ces interesa tener declaraciones que sean compartidas por todas las acciones lexicograacuteficas con indepen-dencia de la pieza a la que esteacute asociada cada una de ellas

Para incluir declaraciones que puedan usarse desde el coacutedigo asociado a cualquiera de las piezas sintaacutecti -cas se dispone de una construccioacuten con el siguiente formato

TOKEN_MGR_DECLS

que consta de una palabra reservada seguida del siacutembolo dos puntos seguido de un bloque de coacutedigo Java (delimitado por llaves) esta declaracioacuten se puede colocar en cualquier parte de una especificacioacuten JavaCC si se incluye soacutelo puede ponerse una vez

Todas las declaraciones de variables o de meacutetodos puestas en el bloque de declaraciones lexicograacuteficas compartidas son accesibles desde el coacutedigo de cualquier accioacuten lexicograacutefica la justificacioacuten es sencilla el bloque de declaraciones se traslada al principio del coacutedigo del analizador lexicograacutefico generado y las ac-ciones lexicograacuteficas tambieacuten se trasladan a ese analizador

loz Declaraciones lexicograacuteficas predefinidasDe lo expuesto hasta ahora se aprecia que en una accioacuten lexicograacutefica asociada a una pieza sintaacutectica re-sultan accesibles

- las declaraciones de su propio bloque (si tiene declaraciones)- los componentes del bloque de declaraciones compartidas (si estaacute incluido en la especificacioacuten)

Ademaacutes de esto se tienen otras declaraciones pertenecientes a la clase TokenManager que constituye el analizador lexicograacutefico que tambieacuten son utilizables desde cualquiera de las acciones lexico-graacuteficas estas declaraciones pueden considerarse como ldquopredefinidasrdquo ya que son incorporadas automaacuteti-camente por el generador Entre estas declaraciones accesibles se encuentran las siguientes

[ static ] StringBuffer imagecontiene la cadena de caracteres representativa de la pieza sintaacutectica actual que se acaba de recono-cer en le entrada se trata de un campo de la clase y se declara como static cuando el analizador

27

se ha generado con la opcioacuten (por defecto) Static = true si se generase con la opcioacuten Sta-tic = false no quedariacutea declarada como static

Token matchedToken

variable de clase Token que representa la pieza sintaacutectica actual la que se acaba de encontrar en la entrada se trata de una variable local del meacutetodo al que se ha trasladado el coacutedigo de la accioacuten (y por ello accesible desde ese coacutedigo)

El campo image puede usarse tanto si la accioacuten lexicograacutefica estaacute asociada a una pieza declarada con TOKEN como si lo estaacute a una secuencia declarada con SKIP Sin embargo la variable matchedToken no puede usarse en una accioacuten asociada a un patroacuten especificado como SKIP el motivo es que lo declara-do como SKIP no constituye propiamente una pieza sintaacutectica ya que no se va a comunicar al analizador sintaacutectico

∆ Ejemplo 6

Se propone aquiacute una nueva solucioacuten para el ejemplo nordm 4 expuesto anteriormente

Esta nueva solucioacuten tal y como se expone aquiacute admite algunas mejoras ya que su pretensioacuten soacutelo es ser-vir de ilustracioacuten para algunos aspectos que se acaban de citar (maacutes adelante se propone otra solucioacuten al-ternativa)

En la especificacioacuten de la solucioacuten es posible apreciar esto- las acciones lexicograacuteficas pueden llevar sus propias declaraciones las variables nLineaVar nColumnaVar nLineaCte y nColumnaCte se declaran dentro de las acciones

- la variable matchedToken a la que se hace referencia en el coacutedigo de la accioacuten lexicograacutefica aso-ciada a una variable contiene la informacioacuten sobre la pieza sintaacutectica que se acaba de detectar una va-riable de la expresioacuten analizada anaacutelogamente con la referencia relativa a una constante- el nombre image que aparece en las acciones lexicograacuteficas se refiere al campo de la clase Expr-MinTokenManager (analizador lexicograacutefico) noacutetese que en efecto se comunica como un paraacuteme-tro de clase StringBuffer

- la variable numero se emplea para numerar las constantes y variables encontradas

- los nombres beginLine y beginColumn son campos (puacuteblicos) de la clase Token por ello se pueden aplicar a un objeto de esa clase

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

void unaExpresion()

28

expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n StringBuffer lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

TOKEN lt variable [a-z] gt

int nLineaVar nColumnaVar numero++ nLineaVar = matchedTokenbeginLine nColumnaVar = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaVar nColumnaVar)

TOKEN lt constante ( [0-9] ) + gt

int nLineaCte nColumnaCte

29

numero++ nLineaCte = matchedTokenbeginLine nColumnaCte = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaCte nColumnaCte)

SKIP | n | t | r

La especificacioacuten precedente se puede escribir de otra manera maacutes escueta como se indica a continua-cioacuten (soacutelo se muestran las modificaciones) sobre esta nueva especificacioacuten puede mencionarse- se han eliminado por innecesarias las variables declaradas dentro de las acciones lexicograacuteficas- el nombre image que aparece en el bloque de las declaraciones compartidas se refiere al campo de la clase Token noacutetese que en efecto se aplica a un objeto de esa clase y se comunica como un paraacute -metro de tipo String

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

TOKEN lt variable [a-z] gt

numero++ grabarDatosPieza(numero matchedToken)

TOKEN lt constante ( [0-9] ) + gt

numero++ grabarDatosPieza(numero matchedToken)

loz Accioacuten lexicograacutefica comuacutenSi hubiera que realizar la misma tarea tras la deteccioacuten de todas y cada una de las piezas sintaacutecticas una posibilidad incoacutemoda seriacutea repetir el mismo coacutedigo en todas las acciones lexicograacuteficas para evitar tanta reiteracioacuten se tiene la opcioacuten Common_Token_Action su valor por defecto es false pero si se le asigna el valor true es posible definir una ldquoaccioacuten lexicograacutefica comuacutenrdquo que se escribe una uacutenica vez y se aplica a todas las piezas sintaacutecticas

30

Cuando la opcioacuten Common_Token_Action se establece con el valor true es obligado incluir el blo-que de declaraciones lexicograacuteficas compartidas (TOKEN_MGR_DECLS) Ademaacutes en ese bloque se preci-sa la definicioacuten de un meacutetodo cuyo encabezamiento sea

[ static ] void CommonTokenAction(Token t)

el coacutedigo de este meacutetodo constituye la accioacuten lexicograacutefica comuacuten que se ejecutaraacute despueacutes de la deteccioacuten de cualquier pieza sintaacutectica el meacutetodo tiene un paraacutemetro de tipo Token mediante el que se puede co-municar la informacioacuten correspondiente a la pieza que se acaba de encontrar en la entrada analizada

Si una pieza sintaacutectica tiene asociada una accioacuten lexicograacutefica propia la accioacuten lexicograacutefica comuacuten (si es-taacute definida) se ejecutaraacute inmediatamente despueacutes de realizar la accioacuten propia

La accioacuten lexicograacutefica comuacuten se aplica a todas las piezas sintaacutecticas definidas como TOKEN bien sea no-minalmente (en las reglas lexicograacuteficas) o bien anoacutenimamente (incorporadas dentro de las reglas sintaacutecti-cas) sin embargo no se aplica cuando la secuencia acoplada se corresponde con una expresioacuten regular es-pecificada como SKIP (noacutetese que por el contrario una especificacioacuten SKIP siacute puede tener asociada una accioacuten lexicograacutefica propia)

El coacutedigo del meacutetodo CommonTokenAction se traslada como un componente maacutes del bloque de decla-raciones compartidas (TOKEN_MGR_DECLS) al principio de la clase del analizador lexicograacutefico

∆ Ejemplo 7

Se pretende ahora ampliar el ejemplo de presentacioacuten con la obtencioacuten de una relacioacuten numerada de los lexemas de las piezas sintaacutecticas encontradas en la entrada analizada en el caso de las variables y de las constantes antes del lexema se mostraraacute un indicativo (Var Const) por ejemplo si la expresioacuten de la entrada es

( a + 987 ) b

se grabaraacute la salida

( 1Var a 2+ 3Const 987 4) 5 6Var b 7 8

En la solucioacuten que se propone a continuacioacuten se muestran la definicioacuten de las opciones y las reglas sin-taacutecticas y lexicograacuteficas (en el resto no hay modificaciones) puede resaltarse lo siguiente- las llamadas al meacutetodo CommonTokenAction se incorporan automaacuteticamente no hay que incluir-las en la especificacioacuten como paraacutemetro de llamada a este meacutetodo siempre se emplea el objeto que re -presenta la pieza que se acaba de detectar- la accioacuten lexicograacutefica comuacuten realiza la actualizacioacuten del contador de piezas y la grabacioacuten del lexe-ma de la pieza detectada con su nuacutemero de orden- la accioacuten lexicograacutefica comuacuten tambieacuten se aplica a las piezas sintaacutecticas anoacutenimas (pareacutentesis y ope-radores) y a la pieza especial que representa el final del fichero (EOF)

- las piezas sintaacutecticas ltconstantegt y ltvariablegt tienen asociada una accioacuten lexicograacutefica propia que se ejecuta antes de la accioacuten lexicograacutefica comuacuten- los caracteres que se saltan (SKIP) no estaacuten afectados por la accioacuten lexicograacutefica comuacuten

options Ignore_Case = true Common_Token_Action = true

31

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarIndicativo(String indicativo) Systemoutprint(indicativo + )

static void grabarLexema (int num String lexema) Systemoutprint(lexema + + num + n)

static void CommonTokenAction(Token pieza) numero++ grabarLexema(numero piezaimage)

TOKEN lt variable [a-z] gt

grabarIndicativo(Var)

32

TOKEN lt constante ( [0-9] ) + gt

grabarIndicativo(Const)

SKIP | n | t | r

A modo de ejemplo de aplicacioacuten de la accioacuten lexicograacutefica comuacuten en lo que sigue se ofrece una solucioacuten para la grabacioacuten (en el fichero predefinido de salida) del texto analizado con las liacuteneas numeradas

∆ Ejemplo 8 [ Listado del texto analizado ]Se desea completar el ejemplo de presentacioacuten con la grabacioacuten de un listado con las liacuteneas numeradas del texto analizado (noacutetese que una expresioacuten puede escribirse ocupando varias liacuteneas) No se muestra la especificacioacuten sintaacutectica debido a que no hay ninguna modificacioacuten en ella Los aspectos resentildeables de la solucioacuten que se propone son- se usa la variable numLin como contador de liacuteneas

- en la variable linea se van yuxtaponiendo los lexemas de las piezas sintaacutecticas de una liacutenea esta yuxtaposicioacuten se realiza mediante la accioacuten lexicograacutefica comuacuten- las secuencias reconocidas con los patrones especificados como SKIP no implican la ejecucioacuten de la accioacuten lexicograacutefica comuacuten por ello su lexema (almacenado en la variable image) ha de yuxtaponer-se expliacutecitamente- hay que separar el patroacuten correspondiente al fin de liacutenea para detectar el momento en que ha de gra-barse la liacutenea completa cuyo final se ha alcanzado

options Ignore_Case = true Common_Token_Action = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(nnAnalisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

TOKEN_MGR_DECLS static int numLin = 0 static String linea =

33

static void CommonTokenAction(Token pieza) linea = linea + piezaimage

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP lt | t | r gt linea = linea + image

SKIP n numLin++ Systemoutprintln(numLin + + linea) linea =

A modo de recapitulacioacutenↄ Accioacuten lexicograacutefica Accioacuten sintaacutectica

bull accioacuten lexicograacutefica bloque de coacutedigo Java asociado a una pieza sintaacutectica nominal (TOKEN) o a una secuencia de caracteres (SKIP) se ejecuta cuando se detecta en la entrada la pieza o la secuencia corres-pondiente no se puede aplicar a piezas sintaacutecticas anoacutenimas (autodefinidas con comillas)bull accioacuten sintaacutectica bloque de coacutedigo Java asociado a un punto de la estructura sintaacutectica (el punto indica-do por el sitio de la produccioacuten donde se inserta el bloque) se ejecuta cuando el anaacutelisis de la entrada pa-sa por ese punto de la estructura

ↄ Valor de una pieza sintaacutectica comunicadaEl analizador lexicograacutefico comunica al analizador sintaacutectico una pieza a traveacutes de un valor de la clase Token (fichero Tokenjava) un objeto de la clase Token representa las caracteriacutesticas de la pieza comunicada En la clase Token entre otros se tienen los campos

String imagelexema de la pieza

int beginLine beginColumn endLine endColumnposicioacuten del lexema en el fichero analizado (indicada por los nuacutemeros de fila y de columna de su comienzo y de su terminacioacuten)

ↄ Valor asociado a un siacutembolo

34

En las producciones que especifican la sintaxis se pueden incluir asignaciones que sirven para dejar ano-tado el valor asociado a un siacutembolo

bull siacutembolo no terminal valor = nombreSimbolo()el meacutetodo de anaacutelisis asociado a nombreSimbolo se ha declarado de un cierto tipo (distinto del tipo void) valor ha de ser una variable del mismo tipo que el meacutetodo

bull siacutembolo terminal dato = lt nombrePieza gtel valor asignado es el valor de la clase Token representativo de la pieza sintaacutectica (nominal) dato ha de ser una variable de tipo Token

ↄ Miradas sobre la pieza sintaacutectica comunicadaLa pieza sintaacutectica comunicada puede mirarse desde cada uno de los dos analizadores

bull analizador sintaacutecticola pieza comunicada (desde el analizador lexicograacutefico) se tiene anotada en un campo de la clase del analizador sintaacutectico declarado como

(static) public Token token

este valor es accesible desde el coacutedigo de todos los meacutetodos de anaacutelisis sintaacutectico el contenido del campo variacutea cuando se recibe una nueva pieza sintaacutectica

bull analizador lexicograacuteficola pieza dispuesta para ser comunicada (al analizador sintaacutectico) se tiene anotada en una variable local declarada como

Token matchedToken

este valor es accesible para el coacutedigo de todas las acciones lexicograacuteficas

ↄ El lexema de la pieza comunicadaEl lexema de la pieza sintaacutectica comunicada se encuentra disponible en dos sitios distintos que compar-ten el mismo nombre pero que son de distinto tipo y se usan de distinta manera

bull String image

es un campo de la clase TOKEN puede consultarse en relacioacuten con un objeto de esa clase que esteacute disponible

bull StringBuffer image

es un campo de la clase del analizador lexicograacutefico ( ∙ ∙ ∙ ∙ TokenManagerjava) es accesible desde el coacutedigo de la clase y por lo tanto desde todas las acciones lexicograacuteficas (bloques de coacute-digo que estaacuten incorporados al analizador lexicograacutefico)

se trata de un valor disponible tanto para las piezas sintaacutecticas nominales (TOKEN) como para las secuencias que se saltan (SKIP)

ↄ Resumen relativo a la lexicografiacutea

TOKEN pieza sintaacutectica nominal∙ ∙ ∙ pieza sintaacutectica anoacutenimaSKIP secuencia de caracteres que no forman pieza

35

Precisiones sobre el analizador sintaacutectico generadoPuede decirse que en su funcionamiento maacutes sencillo cuando no se emplean las posibilidades de examen por adelantado (ldquolookaheadrdquo) JavaCC comprueba que las reglas sintaacutecticas cumplen la condicioacuten LL(1) y generan un analizador descendente-predictivo-recursivo Pero esta apreciacioacuten hay que matizarla ya que el analizador sintaacutectico generado no se ajusta en sentido estricto al modelo de analizador LL(1)

En el modelo de anaacutelisis LL(1) el orden del examen de las distintas alternativas de expansioacuten de un siacutembo-lo no terminal es indiferente ya que se conocen los siacutembolos directores de cada una de ellas (que resultan ser conjuntos disjuntos) en la implementacioacuten del subprograma de anaacutelisis asociado al siacutembolo no terminal ltNombreSimbgt cuyas producciones son

ltNombreSimbgt = α1

| α2

∙ ∙ ∙

| αn

se podriacutea preguntar en cualquier orden si la pieza actual pertenece al conjunto de siacutembolos directores de las distintas reglas Sin embargo en el analizador sintaacutectico generado por JavaCC la implementacioacuten del meacute-todo asociado al siacutembolo no terminal ltNombreSimbgt no sigue esa pauta

Si las anteriores reglas sintaacutecticas se transcriben en JavaCC de la forma

void nombreSimb () α1-jcc

| α2-jcc

∙ ∙ ∙

| αn-jcc

1

accioacutenlexicograacutefica

comuacuten

Commom -TokenAction2

accioacutenlexicograacutefica

propia

(accioacuten ligadaa una pieza)3

bloque dedeclaracioneslexicograacuteficas

TOKEN_MGR_DECLS4

declaracioneslexicograacuteficaspredefinidas

(disponibilidad

de uso)TOKENsisisisi∙ ∙ ∙sino--SKIPnosisisi

Las indicaciones si de las columnas 3 y 4 son consecuencia de las indicaciones de la columna 2

La accioacuten lexicograacutefica comuacuten se ejecuta despueacutes de la accioacuten lexicograacutefica propia

36

el meacutetodo de anaacutelisis generado procede de la siguiente manera

se van considerando las alternativas sucesivamente en el mismo orden en el que estaacuten especifica-das primero la de α1 (α1-jcc) despueacutes la de α2 (α2-jcc) etc

para cada alternativa seleccionada se comprueba si la pieza por adelantado coincide con alguno de los siacutembolos iniciales de la parte derecha y si hay coincidencia se prosigue el anaacutelisis con ella si no hay coincidencia se pasa a la alternativa siguiente

siguiendo con este orden si se llega al final de las alternativas sin haberse encontrado coincidencia alguna se produce un error sintaacutectico

En el analizador generado por JavaCC hay una peculiaridad que no se tiene en los analizadores LL(1) es-trictamente considerados la palabra vaciacutea siacute se considera como posible siacutembolo inicial de la parte derecha una regla (esto ocurre siempre que la parte derecha es anulable) De esta peculiaridad se deriva una conse-cuencia que ha de tenerse en cuenta al escribir la especificacioacuten dado que siempre es posible considerar la palabra vaciacutea como la siguiente pieza por adelantado si se llega a examinar la alternativa correspondiente a una produccioacuten anulable el analizador generado siempre seleccionaraacute esa alternativa con independencia de la siguiente pieza que esteacute presente en la entrada que queda por analizar

Asiacute pues si alguna de las producciones que definen un siacutembolo no terminal es anulable es importante el or-den en que se escriben las alternativas en una especificacioacuten sintaacutectica JavaCC el funcionamiento del ana-lizador generado siacute depende de la colocacioacuten elegida para las reglas

Para ilustrar estas peculiaridades del analizador sintaacutectico generado por JavaCC a continuacioacuten se expone un ejemplo

∆ Ejemplo 9

Se quiere obtener un analizador leacutexico-sintaacutectico de textos que se ajustan al siguiente formato una se-cuencia de uno o maacutes nombres seguida del siacutembolo igual seguido de un nuacutemero entero si la secuencia tiene maacutes de un nombre han de estar separados bien por una coma en todos los casos o bien por el siacutem-bolo dos puntos en todos los casos esto es son correctas las secuencias

uno dos tres = 123exclusivo = 0Abajo Arriba = 57

y son incorrectas las secuenciasuno dos tres = 123

exclusivo inclusive = 0

fuera dentro = 56

Una gramaacutetica que define las secuencias con este formato es

ltListagt = nombre ltOtrosNombresgt = numero

ltOtrosNombresgt = nombre nombre | nombre nombre | ε

se trata de una gramaacutetica que cumple la condicioacuten LL(1)- el siacutembolo director para la alternativa vaciacutea es el siacutembolo igual es evidente pues que las tres reglas de ltOtrosNombresgt tienen siacutembolos directores distintos

37

- la decisioacuten de abandonar la repeticioacuten del anaacutelisis de un nuevo nombre (en las dos primeras alterna-tivas de ltOtrosNombresgt) se puede tomar ante la presencia del siacutembolo igual

En la implementacioacuten de un analizador sintaacutectico LL(1) en sentido estricto la seleccioacuten de la alternativa vaciacutea para expandir el siacutembolo ltOtrosNombresgt se hariacutea tras comprobar expliacutecitamente que la pie-za sintaacutectica por adelantado es el siacutembolo igual

El funcionamiento del analizador generado por JavaCC depende del orden de colocacioacuten de las reglas A continuacioacuten se exponen varias posibilidades de especificacioacuten y se comenta el analizador generado en cada caso

bull Especificacioacuten primeraSe transcriben literalmente las reglas tal y como se han escrito antes en la notacioacuten BNF noacutetese que para poder codificar la parte derecha de la regla vaciacutea ha de incluirse una accioacuten sintaacutectica vaciacutea

PARSER_BEGIN (Igualdad)

public class Igualdad

public static void main (String[] argum) throws ParseException Igualdad analisis = new Igualdad(Systemin) analisissecuencia() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (Igualdad)

void secuencia() lista() ltEOFgt

void lista() lt nombre gt otrosNombres() = lt numero gt

void otrosNombres() lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt ) |

TOKEN lt nombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN lt numero ( [ 0 - 9 ] )+ gt

SKIP | r | n

38

El analizador generado funciona correctamente puesto que en el meacutetodo otrosNombres se selec-ciona la alternativa vaciacutea despueacutes de haber comprobado que la pieza por adelantado no es siacutembolo ini-cial de ninguna de las dos alternativas precedentes (no es una coma ni un siacutembolo dos puntos)

bull Especificacioacuten segundaSe considera la misma especificacioacuten anterior pero cambiando el orden de colocacioacuten de las reglas ahora la produccioacuten vaciacutea se coloca como primera alternativa (soacutelo se muestran las partes modifica-das)

void otrosNombres() | lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt )

Con la entrada de esta especificacioacuten el generador JavaCC produce un mensaje en el que se avisa de que las dos alternativas que estaacuten colocadas detraacutes de la alternativa vaciacutea nunca podraacuten seleccionarse aun asiacute se genera un analizador Pero es un analizador que funciona incorrectamente asiacute por ejemplo la entrada

mxa nyz = 57

se considera incorrecta despueacutes de visto el primer nombre se intenta aplicar la produccioacuten vaciacutea (se espera encontrar el siacutembolo igual) pero habriacutea que aplicar la regla cuyo siacutembolo director es la coma

bull Especificacioacuten terceraSe utilizan las posibilidades de la notacioacuten BNF-Ampliada para prescindir de la especificacioacuten expliacuteci-ta de la palabra vaciacutea se aprovecha que JavaCC admite el operador + para indicar repeticiones de una o maacutes veces la alternativa vaciacutea estaacute impliacutecitamente contemplada en la posibilidad de cero repeti-ciones que contempla el operador (soacutelo se muestran las partes modificadas en la especificacioacuten)

void otrosNombres() ( lt nombre gt )+ | ( lt nombre gt )

El meacutetodo otrosNombres() generado hace las comprobaciones en el siguiente orden

- se comprueba si la pieza por adelantado es una coma (siacutembolo inicial de la primera alternativa) y en este caso se selecciona la primera regla- si no es una coma se comprueba si es un siacutembolo dos puntos (siacutembolo inicial de la segunda alter-nativa) y en este caso se selecciona la segunda regla- si no es un siacutembolo dos puntos se considera la palabra vaciacutea como siacutembolo inicial (siempre posi-ble impliacutecitamente) para la segunda alternativa en este caso para que no haya error la pieza por adelantado ha de ser un siacutembolo igual

Se trata pues de un analizador que funciona correctamente

bull Especificacioacuten cuarta

39

Se pone la misma especificacioacuten anterior pero cambiando el orden de consideracioacuten de la regla vaciacutea ahora se incluye impliacutecitamente en la primera alternativa (soacutelo se muestran las partes modificadas)

void otrosNombres() ( lt nombre gt ) | ( lt nombre gt )+

Para esta especificacioacuten el generador JavaCC emite un aviso en el que se indica que la segunda alter -nativa nunca se seleccionaraacute no obstante se genera un analizador que no funciona como se supone que deberiacutea hacerlo por ejemplo el anaacutelisis de la entrada

nyz mxa = 1

produce un error despueacutes de tratado el primer nombre se intenta aplicar la produccioacuten vaciacutea (corres-pondiente a la repeticioacuten de cero veces contemplada en la primera regla) por lo que no se encuentra el esperado siacutembolo igual

40

Page 8: JavaCC

cuando se ejecuta el generador (lo indicado en la liacutenea de comandos tiene prioridad sobre lo especificado en esta seccioacuten de opciones)

Algunas de las opciones para las que se puede fijar un valor son

Ignore_Case (valor por defecto false)

indica si en el texto analizado ha de distinguirse o no entre letras minuacutesculas y mayuacutesculas

Build_Parser (valor por defecto true)

indica si se genera el analizador sintaacutectico o no

Build_Token_Manager (valor por defecto true)

indica si se genera el analizador lexicograacutefico o no

Sanity_Check (valor por defecto true)

indica si se realizan comprobaciones sobre la gramaacutetica sintaacutectica

Debug_Parser (valor por defecto false)

indica si se genera una traza para el anaacutelisis leacutexico-sintaacutectico

Error_Reporting (valor por defecto true)

indica si los mensajes de error emitidos son maacutes o menos explicativos

Static (valor por defecto true)

con el valor por defecto los meacutetodos de los analizadores leacutexico y sintaacutectico se generan con el des-criptor estaacutetico (static)

Los nombres de las opciones pueden escribirse con letras minuacutesculas o mayuacutesculas los valores de las op-ciones son en la mayoriacutea de los casos un valor entero o un valor loacutegico Si se incluye la seccioacuten se empie-za poniendo la palabra reservada options en este caso al menos ha de ponerse una opcioacuten

La forma de esta seccioacuten es

options nombreOpcion1 = valorOpcion1

nombreOpcion2 = valorOpcion2

∙ ∙ ∙ ∙

nombreOpcionk = valorOpcionk

loz Seccioacuten de ejecucioacutenEn esta seccioacuten se pone el coacutedigo Java que contiene la llamada al analizador generado para que se realice el anaacutelisis de un determinado texto de entrada Tambieacuten se establece aquiacute el nombre de la especificacioacuten que es el nombre que se toma para formar los nombres de parte de los ficheros (clases) generados

La seccioacuten estaacute delimitada por dos palabras reservadas ambas acompantildeadas por un mismo nombre (puesto entre pareacutentesis) ese nombre es el que se da a la especificacioacuten Entre esas dos palabras ha de ponerse una clase directora para el proceso de anaacutelisis el nombre de esa clase directora ha de coincidir con el nombre dado a la especificacioacuten

PARSER_BEGIN (NombreDeLaEspecif)

7

∙ ∙ ∙ ∙

public class NombreDeLaEspecif

∙ ∙ ∙ ∙

∙ ∙ ∙ ∙

PARSER_END (NombreDeLaEspecif)

Una posible versioacuten sencilla de la clase directora es

public class NombreDeLaEspecif public static void main (String[] argum) throws ParseException NombreDeLaEspecif anLeSi = new NombreDeLaEspecif (Systemin) anLeSisimboloPrograma()

se crea el objeto anLeSi para poder aplicar los meacutetodos de anaacutelisis se asocia la entrada standard ( Systemin ) a la entrada para el analizador generado se aplica al objeto creado el meacutetodo de anaacutelisis correspondiente al siacutembolo inicial de la gramaacutetica

loz Seccioacuten de sintaxisEn esta seccioacuten se describe la sintaxis del lenguaje para el que se desea generar el analizador usaacutendose pa-ra ello una notacioacuten parecida a la BNF En lo que sigue se expone la forma de las producciones tal y como se escriben en JavaCC ponieacutendolas en comparacioacuten con las producciones de la notacioacuten BNF-Ampliada

bull Reglas sintaacutecticasEl conjunto de reglas que definen un siacutembolo no terminal

ltNombreSimbgt = α1

| α2

∙ ∙ ∙

| αn

se codifica de la siguiente manera

void nombreSimb () α1-jcc

| α2-jcc

∙ ∙ ∙

| αn-jcc

8

- el nombre del siacutembolo no terminal (parte izquierda de las reglas) se escribe como el encabeza-miento de un meacutetodo en Java sin argumentos y sin valor de devolucioacuten (meacutetodo de tipo void) se sigue como en Java la costumbre de escribir los nombres de los meacutetodos empezando con letra mi-nuacutescula

- el siacutembolo dos puntos representa la separacioacuten entre las partes izquierda y derechas de las reglas (en vez del siacutembolo = de la notacioacuten BNF)

- el siacutembolo barra vertical representa la separacioacuten entre distintas partes derechas con la misma parte izquierda (igual que en la notacioacuten BNF)

- despueacutes del siacutembolo dos puntos se pone un bloque de coacutedigo Java este coacutedigo se suele dedicar a la realizacioacuten de tareas semaacutenticas si se pretende hacer soacutelo un anaacutelisis sintaacutectico el bloque que-daraacute vaciacuteo (aunque siempre es obligada su presencia)

- el conjunto de las alternativas (partes derechas de las reglas) estaacute delimitado mediante llaves como si constituyese un bloque de coacutedigo Java

- αi-jcc representa la codificacioacuten de una alternativa (parte derecha) empleaacutendose una nota-cioacuten que se describe a continuacioacuten

bull Siacutembolos no terminales en las partes derechasEn la notacioacuten BNF se pone ltNombreSimbgt en la notacioacuten JavaCC se escribe

nombreSimb()

esto es se pone como si fuese una llamada en Java a un meacutetodo sin argumentos

bull Siacutembolos terminales en las partes derechasSe considera una clasificacioacuten en las piezas sintaacutecticas (siacutembolos terminales de una gramaacutetica sintaacutectica) distinguieacutendose entre piezas sintaacutecticas nominales y anoacutenimas

son nominales las piezas sintaacutecticas a las que se asocia un nombre en otra parte de la especificacioacuten (maacutes adelante se describe la manera de indicar esta asociacioacuten) ese nombre asociado a la pieza es el que se usa al escribir las partes derechas de las producciones En la notacioacuten JavaCC un siacutembolo terminal no-minal se escribe poniendo el nombre delimitado por los caracteres lt y gt

ltnombreTerminalgt

son anoacutenimas las piezas sintaacutecticas que no tienen nombre asociado no se precisa porque la pieza se re -presenta por su propia secuencia de caracteres (lexema) En la notacioacuten JavaCC un siacutembolo terminal anoacutenimo se escribe poniendo su lexema delimitado por comillas por ejemplo

= lt lt= END

bull Metasiacutembolos de opcionalidad y de repeticioacutenEn la notacioacuten BNF se emplean los corchetes para indicar opcionalidad en la notacioacuten JavaCC se utilizan tambieacuten esos mismos siacutembolos

[ α ] en ambas notaciones

En la notacioacuten BNF se emplean las llaves para indicar una repeticioacuten de cero o maacutes veces en la notacioacuten JavaCC se utilizan los pareacutentesis y un asterisco detraacutes del pareacutentesis de cerrar

α en notacioacuten BNF-Ampliada

( α ) en notacioacuten JavaCC

bull Otros metasiacutembolos de la notacioacuten JavaCC

9

La opcionalidad tambieacuten se puede indicar empleando el metasiacutembolo de interrogacioacuten

( α )

Para indicar repeticioacuten de una o maacutes veces se utiliza el metasiacutembolo operador aditivo suma

( α )+

bull Control del final de fichero de entradaPara controlar el final del fichero en el texto analizado ha de antildeadirse una regla sintaacutectica especial que tenga (seguacuten la notacioacuten BNF) la forma

ltProgramaCompletogt = ltProgramagt FF

donde

ltProgramaCompletogt es un nuevo siacutembolo (no terminal) auxiliar antildeadido

ltProgramagt es el siacutembolo inicial de la gramaacutetica sintaacutectica

FF es una representacioacuten del final del fichero

En JavaCC el final de fichero se escribe con la notacioacuten ltEOFgt siendo EOF una palabra reservada la regla sintaacutectica especial antildeadida a la especificacioacuten quedariacutea codificada asiacute

void programaCompleto() programa() ltEOFgt

bull Representacioacuten de la produccioacuten vaciacuteaLas producciones vaciacuteas (reglas sintaacutecticas cuya parte derecha es la palabra vaciacutea ε ) no se pueden repre-sentar expliacutecitamente en una especificacioacuten leacutexico-sintaacutectica escrita en JavaCC Para indicar la presencia de una produccioacuten vaciacutea han de aplicarse las posibilidades de la notacioacuten BNF-Ampliada seguacuten esta nota-cioacuten cuando se indica la opcionalidad de un componente se estaacute especificando impliacutecitamente la palabra vaciacutea (en el caso de que el componente no esteacute presente)

Asiacute pues si se quisiera escribir en JavaCC la construccioacuten sintaacutectica expresada por las reglas

ltComponentegt = α

| ε

deberiacutean de considerarse con la notacioacuten equivalente

ltComponentegt = [ α ]

y si hubiera varias alternativas no vaciacuteas

ltComponentegt = α | β

| ε

habriacutea que considerarlas como

ltComponentegt = [ α | β ]

o tambieacuten podriacutea verse como

ltComponentegt = α

10

| [ β ]

Como ejemplo sea la especificacioacuten sintaacutectica de una llamada a una funcioacuten que puede tener cero o maacutes paraacutemetros separados por comas aunque no haya paraacutemetros los pareacutentesis estaacuten presentes Las siguien-tes reglas definen esta estructura

ltLlamadagt = id ( ltParametrosgt )

ltParametrosgt = ltExpresiongt ltExpresiongt | ε

En JavaCC la definicioacuten del siacutembolo ltParametrosgt se puede escribir como sigue (noacutetese que aquiacute los pareacutentesis empleados para aplicar el operador son metasiacutembolos)

void parametros() [ expresion() ( expresion() ) ]

loz Seccioacuten de lexicografiacuteaEn esta seccioacuten se indica la lexicografiacutea del lenguaje para el que se va a generar el analizador la notacioacuten de JavaCC que representa la forma de cada una de las piezas sintaacutecticas es una variante de las bien conoci-das expresiones regulares

bull El nombre de las piezas sintaacutecticas nominalesPara asociar un nombre a una pieza sintaacutectica y para al mismo tiempo definir la forma de la pieza se em-plea la notacioacuten

TOKEN lt nombreTerminal Expresioacuten-Regular gt

- TOKEN es una palabra reservada detraacutes de ella se pone el separador dos puntos

- nombreTerminal representa el nombre que se quiere asociar a la pieza sintaacutectica

- Expresioacuten-Regular indica una expresioacuten regular que especifica la forma de la pieza

- el nombre y la expresioacuten se separan mediante el caraacutecter dos puntos y conjuntamente se deli -mitan mediante los caracteres lt y gt a su vez toda la asociacioacuten se delimita con llaves

bull Expresiones regulares en JavaCCLas expresiones regulares en una especificacioacuten JavaCC se escriben de manera parecida a la forma utiliza-da en otras conocidas notaciones aunque con ciertas peculiaridades A continuacioacuten se mencionan los as-pectos maacutes significativos de las expresiones regulares JavaCC y algunos ejemplos

Los operadores de las expresiones son

| unioacuten

repeticioacuten cero o maacutes veces

+ repeticioacuten una o maacutes veces

opcionalidad

11

Los pareacutentesis permiten fijar el orden de aplicacioacuten de los operadores Una peculiaridad de las expresiones regulares JavaCC es que los operadores de repeticioacuten y de opcionalidad requieren que el componente al que se aplican esteacute en todo caso delimitado por pareacutentesis

Se pueden citar las siguientes caracteriacutesticas baacutesicas de las expresiones regulares en JavaCC

Se tiene la posibilidad de definir conjuntos de caracteres poniendo entre corchetes la relacioacuten de carac-teres separados por comas y cada caraacutecter delimitado por comillas En un conjunto se puede fijar un ran-go de caracteres poniendo el primero y el uacuteltimo separados por el siacutembolo guioacuten (-) A veces resulta maacutes coacutemodo indicar los caracteres que no pertenecen al conjunto para ello se puede definir el comple -mentario de un conjunto poniendo el siacutembolo ~ delante del corchete inicial

Para indicar que una secuencia de caracteres colocados consecutivamente constituye por siacute misma una expresioacuten regular (o un componente de una expresioacuten) se escribe la secuencia delimitada por comillas El siacutembolo se emplea como metacaraacutecter para indicar que el siacutembolo que estaacute inmediatamente a su derecha se considere como caraacutecter del alfabeto no como posible metacaraacutecter (operador delimitador)

En una expresioacuten regular se pueden incluir como si fueran caracteres las secuencias de escape con la misma notacioacuten y el mismo significado que en Java o en C

Noacutetese que en una produccioacuten sintaacutectica la opcionalidad se puede especificar de dos formas con corchetes o con el siacutembolo de interrogacioacuten sin embargo en una expresioacuten regular la opcionalidad soacutelo se puede es-pecificar mediante el siacutembolo de interrogacioacuten (los corchetes en las expresiones regulares son para definir conjuntos de caracteres)

A continuacioacuten se muestran ejemplos de expresiones regulares escritas seguacuten la notacioacuten JavaCC

ENDsecuencia de las tres letras mayuacutesculas que conforman la palabra END

whilesecuencia de las cinco letras minuacutesculas que conforman la palabra while

ncaraacutecter representativo del fin de liacutenea (secuencia de escape)

rcaraacutecter representativo del retorno de carro (secuencia de escape)

caraacutecter dos puntos

=secuencia de dos caracteres (siacutembolo de asignacioacuten en Pascal)

gtcaraacutecter representativo del operador de relacioacuten mayor

(i | I)(f | F)palabra ldquoifrdquo escrita con letras minuacutesculas o mayuacutesculas (cuatro combinaciones)

if | IFpalabra ldquoifrdquo escrita soacutelo con minuacutesculas o soacutelo con mayuacutesculas (dos combinaciones)

12

u | o | i | e | auna cualquiera de las vocales minuacutesculas (pero soacutelo una de ellas)

[ A E I O U ]una cualquiera de las vocales mayuacutesculas (pero soacutelo una de ellas)

[ a - z ntilde ]una cualquiera de las letras minuacutesculas incluyendo la letra ntilde

[ a - z A - Z 0 - 9 ]una cualquiera de las letras o de las cifras decimales

([ 0 - 9 ])+constante entera (nuacutemero de una o maacutes cifras)

noacutetese que es incorrecto poner [ 0 - 9 ]+

( + | - )([ 0 - 9 ])+constante entera precedida opcionalmente por un signo

noacutetese que la primera cifra ha de seguir inmediatamente al signo (si existe)

([+ -])([ 0 - 9 ])+representa lo mismo que la expresioacuten anterior

[ a - z ] ([ a - z 0 - 9 ])una letra minuacutescula seguida de cero o maacutes letras minuacutesculas o cifras decimales

~[a - z ntilde A - Z Ntilde]cualquier caraacutecter que no sea una letra

noacutetese que la expresioacuten representa un uacutenico caraacutecter

~[n r]cualquier caraacutecter (un uacutenico caraacutecter) excepto el fin de liacutenea y el retorno de carro

~[ ]cualquier caraacutecter del alfabeto (un uacutenico caraacutecter)

([ 0 - 9 ])+ ( ([ 0 - 9 ])+)constante aritmeacutetica con parte decimal opcional

Los modernossecuencia de catorce caracteres Los modernos

noacutetese el uso de para imponer la condicioacuten de caraacutecter del siacutembolo comillas

nsecuencia de cuatro caracteres n

noacutetese que aquiacute no se representa la secuencia de escape

bull Piezas sintaacutecticas internas (ldquoprivaterdquo)

13

Es posible definir piezas sintaacutecticas auxiliares llamadas ldquointernasrdquo porque no se comunican al analizador sintaacutectico sino que soacutelo sirven para facilitar la escritura de la forma de las piezas sintaacutecticas que siacute se co-munican con el uso de las piezas internas es posible evitar repeticiones de escritura y hacer maacutes legible la especificacioacuten de las caracteriacutesticas lexicograacuteficas Las piezas internas permiten dar nombre a subexpre-siones regulares que a su vez pueden emplearse como componentes para escribir expresiones maacutes com-plejas que incorporan esos nombres Para indicar que una pieza sintaacutectica se considere como interna se pone el siacutembolo delante del nombre asociado a la pieza

A continuacioacuten se muestra un ejemplo tiacutepico de utilizacioacuten de las piezas internas en una especificacioacuten le-xicograacutefica en JavaCC Se quieren definir tres piezas sintaacutecticas que representan un nombre una constante entera y una constante decimal una posible especificacioacuten es

TOKEN ltnombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN ltcteEntera ( [ 0 - 9 ] )+ gt

TOKEN ltcteDecimal ( [ 0 - 9 ] )+ ( [ 0 - 9 ] )+ gt

Con la inclusioacuten de tres piezas sintaacutecticas internas auxiliares esta misma especificacioacuten podriacutea escribirse de la siguiente manera

TOKEN lt letra [ a - z A - Z ntilde Ntilde ] gt

TOKEN lt cifra [ 0 - 9 ] gt

TOKEN lt numero ( ltcifragt )+ gt

TOKEN lt nombre ( ltletragt )+ gt

TOKEN lt cteEntera ltnumerogt gt

TOKEN lt cteDecimal ltnumerogt ltnumerogt gt

Los nombres letra cifra y numero son exclusivos (internos) del analizador lexicograacutefico y no se pueden emplear como nombres de piezas en la especificacioacuten de las reglas sintaacutecticas cuando esos nom-bres se usan en la definicioacuten de otras piezas han de escribirse delimitados por los siacutembolos lt y gt

bull Distincioacuten restringida entre minuacutesculas y mayuacutesculasSe tiene la opcioacuten Ignore_Case para determinar si en el anaacutelisis del texto de entrada se distingue entre las letras minuacutesculas y las mayuacutesculas el valor por defecto false indica que siacute se diferencian si se asig-na el valor true a esta opcioacuten entonces no se diferencian en ninguacuten caso con independencia de que en los patrones que se definan para las piezas sintaacutecticas se pongan minuacutesculas o mayuacutesculas

Pero a veces conviene que la diferenciacioacuten entre minuacutesculas y mayuacutesculas no se aplique de manera ge-neral a toda la entrada sino en particular a las secuencias de caracteres que se acoplen a ciertos patrones Para este fin existe la posibilidad de establecer que en la expresioacuten regular que define el patroacuten de una determinada pieza sintaacutectica no se diferencie entre minuacutesculas y mayuacutesculas ello se consigue poniendo

14

[ IGNORE_CASE ]

inmediatamente detraacutes de la palabra TOKEN con la que empieza la especificacioacuten de la pieza sintaacutectica en la que se pretende la no distincioacuten entre minuacutesculas y mayuacutesculas

Noacutetese que ignore_case tiene dos significados

- es el nombre de un paraacutemetro de la seccioacuten de opciones (puede ponerse en minuacutesculas o mayuacutesculas)- es una palabra reservada que puede aplicarse (escribieacutendola entre corchetes y en mayuacutesculas) a la de -finicioacuten de una pieza sintaacutectica

Por ejemplo sea un lenguaje que en general distingue entre minuacutesculas y mayuacutesculas pero en la codifi -cacioacuten del exponente de una constante real exponencial puede ponerse la letra e en minuacutescula o en ma-yuacutescula En la especificacioacuten JavaCC para ese lenguaje la opcioacuten Ignore_Case ha de tomar el valor false en la definicioacuten de una constante exponencial son posibles las siguientes formas equivalentes

1) sin hacer uso de IGNORE_CASE

TOKEN lt cifras ( [ 0 - 9 ] )+ gt

TOKEN lt signo [+ -] gt

TOKEN lt constante

ltcifrasgt ( ltcifrasgt) [e E] (ltsignogt) ltcifrasgt gt

2) aplicando IGNORE_CASE

TOKEN lt cifras ( [ 0 - 9 ] )+ gt

TOKEN lt signo [+ -] gt

TOKEN [IGNORE_CASE] lt constante

ltcifrasgt ( ltcifrasgt) e (ltsignogt) ltcifrasgt gt

bull Secuencias de caracteres sin efecto en la estructura sintaacutecticaEn los lenguajes de programacioacuten suele haber secuencias de caracteres que no forman parte de la estructu-ra sintaacutectica de un programa esas secuencias son detectadas por el analizador lexicograacutefico pero no son comunicadas al sintaacutectico no han de considerarse como piezas sino que han de saltarse En JavaCC se pueden definir estas secuencias mediante la palabra reservada SKIP la notacioacuten de una especificacioacuten con la palabra SKIP de una secuencia que ha de saltarse es la misma que la notacioacuten usada para las espe-cificaciones con la palabra TOKEN de las piezas que han de comunicarse

Los dos casos maacutes habituales de secuencias que no se comunican al analizador sintaacutectico son

a) los caracteres separadores que no tienen significado en el texto analizadoen los lenguajes de codificacioacuten en formato libre hay caracteres del texto fuente que han de ignorarse en la mayoriacutea de los casos estos caracteres son el espacio en blanco el tabulador el retorno de carro y el fin de liacutenea esta caracteriacutestica se especifica

SKIP |t | r |n

b) los comentarios

15

en el anaacutelisis sintaacutectico de un programa los comentarios han de ignorarse una manera de especificar esta circunstancia es poniendo en una definicioacuten SKIP la expresioacuten regular que denota la forma de los comentarios del lenguaje por ejemplo para los comentarios de una liacutenea de Java se pondriacutea

SKIP

lt (~ [n r]) gt

Tareas asociadas a la estructura sintaacutecticaJavaCC genera un analizador sintaacutectico descendente recursivo constituido por un conjunto de meacutetodos ca-da siacutembolo no terminal de la gramaacutetica sintaacutectica tiene asociado un meacutetodo de anaacutelisis (el meacutetodo tiene el mismo nombre que el siacutembolo correspondiente) Un meacutetodo de anaacutelisis ademaacutes de la realizacioacuten de la parte del anaacutelisis sintaacutectico que le corresponde puede llevar a cabo otras tareas complementarias que sirvan para efectuar un anaacutelisis maacutes complejo del texto de entrada

loz Bloque para un siacutemboloAl describir la forma de las reglas sintaacutecticas de un siacutembolo no terminal en una especificacioacuten JavaCC ya se ha visto que detraacutes de la definicioacuten del nombre del siacutembolo y antes de empezar con las diferentes alter-nativas que tiene asociadas se incluye un bloque de coacutedigo Java (en el ejemplo de presentacioacuten este bloque estaacute vaciacuteo en todos los siacutembolos no terminales de la gramaacutetica) Cada bloque situado en esa posicioacuten puede considerarse ligado al siacutembolo no terminal en cuya descripcioacuten estaacute incluido el coacutedigo Java del bloque se traslada literalmente al principio del meacutetodo generado correspondiente al siacutembolo no terminal y por lo tan-to es lo primero que se considera (se ejecuta) cuando en el transcurso del proceso de anaacutelisis se realiza una llamada al meacutetodo Asiacute pues cada siacutembolo no terminal siempre tiene asociado un uacutenico bloque de coacute-digo Java ndashbloque de presencia obligada aunque pueda estar vaciacuteo- en este bloque se pueden poner de-claraciones y sentencias

loz Acciones sintaacutecticasAdemaacutes del bloque asociado a cada uno de los siacutembolos no terminales de la gramaacutetica se pueden incluir otros bloques de coacutedigo Java intercalados en cualquiera de las partes derechas de las reglas sintaacutecticas Los bloques incluidos en las partes derechas se denominan ldquoacciones sintaacutecticasrdquo (parser actions) ya que son trozos de coacutedigo que pueden considerarse asociados a los puntos de la estructura sintaacutectica donde estaacuten co-locados Una accioacuten sintaacutectica se puede poner en cualquier posicioacuten al principio al final o en el medio de la parte derecha de una regla precedida o seguida de siacutembolos terminales o no terminales el coacutedigo Java de una accioacuten sintaacutectica se traslada literalmente al meacutetodo asociado al siacutembolo no terminal de la parte izquierda de la regla de manera que se ejecuta cada vez que el analizador transcurre por el punto de la estructura sintaacutectica donde se ha incorporado la accioacuten (esto es las acciones sintaacutecticas se incorporan al coacutedigo del analizador sintaacutectico) No obstante su nombre en los bloques de las acciones sintaacutecticas suele ponerse coacutedigo dedicado a la realizacioacuten de tareas semaacutenticas

Si en el bloque asociado a un siacutembolo no terminal se ponen declaraciones los nombres ahiacute definidos son accesibles en todas las acciones sintaacutecticas intercaladas en las partes derechas de las reglas de ese siacutembolo la justificacioacuten es sencilla el coacutedigo del bloque asociado al siacutembolo se incluye al principio de un meacutetodo y el coacutedigo de las acciones pertenece a ese mismo meacutetodo

∆ Ejemplo 1

Se considera de nuevo el ejemplo de presentacioacuten ahora se pretende completar el anaacutelisis leacutexico-sintaacutec-tico con la realizacioacuten de las siguientes tareas

- cada vez que se analiza una expresioacuten un teacutermino o un factor se graba el nombre del siacutembolo no terminal correspondiente y

16

- cuando se analiza un teacutermino en el que hay operadores multiplicativos se cuenta su nuacutemero y se graba el resultado (noacutetese que se cuentan los operadores de cada teacutermino pero no se acumula la cuenta de todos los operadores de la expresioacuten)

Para la realizacioacuten de estas tareas se antildeaden bloques de coacutedigo Java En la especificacioacuten que se describe a continuacioacuten puede apreciarse- para los siacutembolos no terminales expresion termino y factor se ha incluido en los blo-ques correspondientes la operacioacuten de grabacioacuten de su nombre que se ejecutaraacute cada vez que se efec-tuacutee una llamada al meacutetodo- en el bloque asociado a termino se tiene la declaracioacuten del contador de operadores (que seraacute una variable local del meacutetodo)- en la parte derecha de la regla que define la estructura de un teacutermino se han incluido dos acciones sintaacutecticas una para contar la cantidad de operadores (situada detraacutes de la pieza sintaacutectica del opera-dor) y otra para grabar la cantidad de operadores del teacutermino (situada al final de la parte derecha)- se ha definido el meacutetodo grabar para facilitar la escritura del coacutedigo Java

options Ignore_Case = true

PARSER_BEGIN (ExprMin) public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

private static void grabar(String nombre) Systemoutprintln ( -gt + nombre + n)

PARSER_END (ExprMin)

void unaExpresion()

expresion() ltEOFgt

void expresion() grabar(Expresion)

termino() ( + termino() )

void termino() int nAst = 0 grabar(Termino)

17

factor() ( nAst++ factor() ) if (nAst gt 0) Systemoutprintln (n Asteriscos + nAst + n)

void factor() grabar(Factor)

ltconstantegt | ltvariablegt | ( expresion() )

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP | t | n | r

Cuando se incluyen acciones sintaacutecticas la legibilidad del aspecto sintaacutectico de la especificacioacuten queda oscurecida para paliar ese inconveniente puede antildeadirse un comentario que muestre la regla sintaacutectica escueta sin las acciones por ejemplo podriacutea escribirse

void termino() int nAst = 0 grabar(Termino)

factor() ( factor() )

factor()

( nAst++ factor() )

if (nAst gt 0) Systemoutprintln (n Asteriscos + nAst + n)

loz Valor comunicado por un meacutetodoEn todo lo visto hasta ahora los meacutetodos de anaacutelisis sintaacutectico generados por JavaCC no devuelven valor alguno al terminar su tarea de anaacutelisis ya que en la especificacioacuten siempre se ha declarado el nombre de un meacutetodo precedido de void (son meacutetodos de tipo void) Pero en general un meacutetodo de anaacutelisis sintaacutec-tico puede ser de cualquier tipo (primitivo o no) por ello un meacutetodo puede devolver un valor de cualquier tipo (de cualquier clase) al meacutetodo desde el que se efectuoacute la llamada

Esta posibilidad de devolucioacuten de valores tiene una utilidad fundamental en la implementacioacuten de analiza-dores de lenguajes permite la comunicacioacuten de datos (atributos de las entidades analizadas) entre los dife -rentes meacutetodos que colaboran en el anaacutelisis del programa completo

En una especificacioacuten JavaCC la llamada a un meacutetodo ocurre cuando en la parte derecha de una regla estaacute el nombre del siacutembolo no terminal asociado al meacutetodo Cuando un meacutetodo devuelve un valor (tiene un tipo distinto de void) es habitual la recepcioacuten del valor devuelto en una variable en la propia especificacioacuten

18

se puede indicar el nombre de la variable a la que se asignaraacute el valor devuelto tras la llamada para ello se emplea una notacioacuten ideacutentica a la forma que tienen las sentencias de asignacioacuten en el lenguaje Java

dato = nombreSimb()

donde dato es el nombre de la variable a la que se asigna el valor devuelto por el meacutetodo llamado esa va -riable ha de estar oportunamente declarada del mismo tipo que dicho meacutetodo

∆ Ejemplo 2

En el mismo ejemplo de presentacioacuten se desea ademaacutes del anaacutelisis leacutexico-sintaacutectico de la entrada contar la cantidad de operadores que hay en la expresioacuten A continuacioacuten se expone una especificacioacuten JavaCC que es una posible solucioacuten soacutelo se muestran las reglas sintaacutecticas (el resto de la especificacioacuten es igual que en el ejemplo de presentacioacuten)

En la especificacioacuten que se propone puede apreciarse- los siacutembolos no terminales expresion termino y factor se han declarado de tipo int los meacutetodos correspondientes devuelven un valor de tipo entero- el valor devuelto por los meacutetodos sirve para propagar a traveacutes de la estructura sintaacutectica la cuenta de los operadores que hay en el texto analizado- por tratarse de meacutetodos que devuelven un valor es preciso incluir las correspondientes sentencias de devolucioacuten return

void unaExpresion() int nOper nOper = expresion() ltEOFgt Systemoutprintln (Cantidad de operadores = + nOper)

int expresion() int n i n = termino() ( + n++ i = termino() n = n + i )

return n

int termino() int n i n = factor() ( n++ i = factor() n = n + i )

return n

int factor() int n ltconstantegt return 0 | ltvariablegt return 0 | ( n = expresion() ) return n

La especificacioacuten de un factor tambieacuten podriacutea escribirse de esta otra manera

19

int factor() int n = 0

( ltconstantegt | ltvariablegt | ( n = expresion() ) )

return n

A continuacioacuten se expone un nuevo ejemplo para ilustrar la utilidad que proporciona el valor devuelto por un meacutetodo de anaacutelisis sintaacutectico para la propagacioacuten (ascendente) de atributos (propiedades de las entida-des analizadas)

∆ Ejemplo 3

Se pretende ampliar el analizador del ejemplo de presentacioacuten para que se emita un mensaje indicativo cuando la expresioacuten analizada sea una expresioacuten en la que soacutelo aparecen constantes en la solucioacuten que se muestra soacutelo se incluyen las reglas sintaacutecticas (en el resto de la especificacioacuten no hay modificacio-nes)

void unaExpresion() boolean esCte

expresion() ltEOFgt

esCte = expresion() ltEOFgt

if (esCte) Systemoutprintln(nExpresion constanten)

boolean expresion() boolean esCte esTambienCte

termino() ( + termino() )

esCte = termino() ( + esTambienCte = termino() esCte = esCte ampamp esTambienCte )

return esCte

boolean termino() boolean esCte esTambienCte

factor() ( factor() )

esCte = factor() ( esTambienCte = factor() esCte = esCte ampamp esTambienCte )

return esCte

20

boolean factor() boolean esCte

ltconstantegt return true | ltvariablegt return false | ( esCte = expresion() ) return esCte

bull Reconsideracioacuten sobre las producciones vaciacuteasAnteriormente se ha comentado que para representar una produccioacuten vaciacutea en una especificacioacuten JavaCC hay que hacer uso de los metasiacutembolos de opcionalidad para ilustrar esta idea se ha incluido como ejem-plo la especificacioacuten de una lista de paraacutemetros compuesta por cero o maacutes expresiones separadas entre siacute por el caraacutecter coma

void parametros() [ expresion() ( expresion() ) ]

Pero si se aprovecha la posibilidad de incorporar acciones sintaacutecticas se tiene una manera de especificar directamente producciones vaciacuteas para conseguirlo basta considerar una produccioacuten vaciacutea (no tiene nada en su parte derecha) acompantildeada de una accioacuten sintaacutectica cuyo bloque de coacutedigo esteacute vaciacuteo Seguacuten esta posibilidad la especificacioacuten anterior podriacutea tambieacuten escribirse asiacute

void parametros() expresion() ( expresion() ) |

En general la accioacuten sintaacutectica asociada a una produccioacuten vaciacutea no tiene por queacute ser un bloque vaciacuteo de coacutedigo por ejemplo si se quiere que el meacutetodo que analiza los paraacutemetros de una llamada devuelva la cantidad de paraacutemetros encontrados se podriacutea escribir la siguiente especificacioacuten

int parametros()

int cantidad

( expresion() cantidad = 1

( expresion() cantidad++ )

| cantidad = 0 )

return cantidad

y si no quisiera ponerse expliacutecitamente la alternativa de la produccioacuten vaciacutea podriacutea escribirse

int parametros() int cantidad = 0

21

[ expresion() cantidad++ ( expresion() cantidad++ ) ]

return cantidad

Comunicacioacuten entre los analizadores leacutexico y sintaacutecticoCada vez que el analizador sintaacutectico realiza una llamada al analizador lexicograacutefico recibe como resultado de la llamada una representacioacuten de la pieza sintaacutectica encontrada en el texto analizado En los analizadores generados por JavaCC la comunicacioacuten de la pieza se efectuacutea mediante un valor de clase Token Esta cla-se estaacute definida en el fichero Tokenjava que se obtiene en todo caso siempre con el mismo contenido cualquiera que sea la especificacioacuten proporcionada como entrada al generador

Entre los campos de la clase Token se encuentran los siguientes

public int kind

nuacutemero entero que sirve de representacioacuten interna de la pieza sintaacutectica estos nuacutemeros asociados a las piezas son asignados automaacuteticamente por JavaCC los valores considerados pueden consultar-se en el fichero generado de nombre Constantsjava

public String image

cadena que contiene de la secuencia de caracteres (lexema) que constituyen la pieza sintaacutectica co-municada

public int beginLine beginColumn endLine endColumn

posiciones ocupadas (nuacutemeros de liacutenea y columna en el fichero de entrada) por el comienzo y el fi-nal del lexema de la pieza sintaacutectica comunicada

La clase Token tiene otros campos y meacutetodos en el fichero Tokenjava se tienen comentarios des-criptivos sobre todos los campos y meacutetodos que forman parte de la clase todos los campos de la clase se declaran como public

Ya se ha comentado anteriormente que cuando un meacutetodo de anaacutelisis sintaacutectico (asociado a un siacutembolo no terminal) tiene tipo el valor devuelto tras una llamada se puede almacenar en una variable por si se desea utilizar en alguna accioacuten sintaacutectica para ello se escribe

valor = nombreSimbolo()

(siendo valor el nombre de la variable y nombreSimbolo el nombre del siacutembolo no terminal) De manera anaacuteloga el objeto de la clase Token representativo de la pieza sintaacutectica encontrada en la entrada por el analizador lexicograacutefico puede asignarse a una variable del mismo tipo para tener accesibles las ca -racteriacutesticas de la pieza por si se precisaran en alguna accioacuten sintaacutectica para ello en la parte de la especifi -cacioacuten sintaacutectica donde aparezca la pieza sintaacutectica se escribe

dato = lt nombrePieza gt

donde nombrePieza es el nombre de la pieza sintaacutectica y dato es el nombre de una variable de tipo Token en la que se anota el valor indicativo de las propiedades de la pieza sintaacutectica comunicada

Tambieacuten existe otra manera de acceder a las caracteriacutesticas de la pieza sintaacutectica que el analizador lexico -graacutefico comunica al analizador sintaacutectico en la clase del analizador sintaacutectico se tiene declarado el campo

[ static ] public Token token

22

cada vez que el analizador sintaacutectico recibe una pieza sintaacutectica comunicada por el analizador lexicograacutefico se deja anotada en el campo token y ese contenido no se altera hasta que sea proporcionada la siguiente pieza de la entrada

∆ Ejemplo 4

Se considera una ampliacioacuten del ejemplo de presentacioacuten ademaacutes del anaacutelisis de la entrada se pretende obtener una relacioacuten numerada de las variables y las constantes indicando el nuacutemero de liacutenea y el nuacuteme-ro de columna donde estaacuten situadas por ejemplo si en la entrada se tiene la expresioacuten (grabada en dos liacuteneas)

x (y + 11) + 22

en la salida se tendraacute

1- x linea 1 columna 1

2- y linea 1 columna 6

3- 11 linea 1 columna 10

4- 22 linea 2 columna 5

En la especificacioacuten que se propone como solucioacuten puede apreciarse- en la clase del analizador sintaacutectico se han incluido meacutetodos auxiliares en los que se usan los nom-bres image beginLine beginColumn que son campos de un objeto de clase Token

- en la especificacioacuten sintaacutectica de factor se emplea la variable pieza de tipo Token para reco-ger las caracteriacutesticas de las piezas sintaacutecticas detectadas- la variable numero hace de contador para numerar las constantes y variables

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin

static int numero = 0

private static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

private static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

private static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

23

PARSER_END (ExprMin)

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() Token pieza pieza = ltconstantegt numero++ grabarDatosPieza(numero pieza) | pieza = ltvariablegt numero++ grabarDatosPieza(numero pieza) | ( expresion() )

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP | n | t | r

Si se agrupan las dos acciones sintaacutecticas comunes la especificacioacuten de factor tambieacuten puede escri-birse de esta forma

void factor() Token pieza ( pieza = ltconstantegt | pieza = ltvariablegt

) numero++ grabarDatosPieza(numero pieza)

| ( expresion() )

24

Si se accede al campo token de la clase del analizador sintaacutectico se puede resolver este ejemplo pres-cindiendo de la variable pieza

void factor() ( ltconstantegt | ltvariablegt

) numero++ grabarDatosPieza(numero token)

| ( expresion() )

Tareas asociadas a las piezas sintaacutecticasYa se ha visto que es posible asociar bloques de coacutedigo Java a puntos de la estructura sintaacutectica para que se realicen ciertas operaciones cuando el analizador sintaacutectico pase por esa zona de la estructura Tambieacuten es posible asociar coacutedigo Java a las piezas sintaacutecticas para que el analizador lexicograacutefico efectuacutee determina-das tareas cuando detecte en la entrada analizada piezas que tengan asociado coacutedigo

loz Accioacuten ligada a una pieza sintaacutecticaSi se asocia un bloque de coacutedigo a una pieza sintaacutectica se consigue que cada vez que se detecta en la en-trada analizada la presencia de esa pieza se ejecute en ese preciso momento el coacutedigo del bloque asociado a la pieza Estas acciones se denominan ldquoacciones lexicograacuteficasrdquo (lexical actions) el coacutedigo del bloque se traslada literalmente al coacutedigo del analizador lexicograacutefico generado para que se ejecute oportunamente cuando se detecte la pieza ligada al bloque de coacutedigo La accioacuten lexicograacutefica se ejecuta inmediatamente antes que la pieza sea comunicada al analizador sintaacutectico

Se puede asociar una accioacuten lexicograacutefica a una pieza sintaacutectica declarada con TOKEN y tambieacuten a una se-cuencia de caracteres declarada con SKIP ademaacutes de eacutestas hay otras dos formas de declaraciones lexico-graacuteficas (no mencionadas en esta introduccioacuten) que tambieacuten pueden llevar una accioacuten asociada

En una especificacioacuten JavaCC la forma de una pieza sintaacutectica (o bien de una secuencia de caracteres) se define poniendo su descripcioacuten delimitada entre llaves detraacutes de la indicacioacuten TOKEN (o bien SKIP) si se quiere asociar una accioacuten lexicograacutefica a la pieza (o a la secuencia) hay que colocar el bloque de coacutedigo de la accioacuten a continuacioacuten de la descripcioacuten de la forma de la pieza (o de la secuencia) el bloque (delimi-tado por llaves) queda situado entre la descripcioacuten y la llave que cierra la especificacioacuten

La pieza sintaacutectica especial que representa el final del fichero (EOF) tambieacuten puede llevar asociada una ac-cioacuten lexicograacutefica para ello hay que poner

ltgt TOKEN lt EOF gt coacutedigo de la accioacuten asociada

para justificar la presencia de los caracteres ltgt delante de la palabra TOKEN hay que basarse en el con-cepto de ldquoestado lexicograacuteficordquo (cuya explicacioacuten queda fuera del alcance de esta introduccioacuten)

∆ Ejemplo 5

Se considera nuevamente el ejemplo de presentacioacuten como resultado del anaacutelisis de una expresioacuten se pretende obtener una relacioacuten de los pareacutentesis contenidos en la expresioacuten en el mismo orden en que se encuentran ademaacutes por cada salto de liacutenea del texto de entrada se grabaraacute en la salida el caraacutecter y

25

se pasaraacute a una nueva liacutenea tambieacuten se anotaraacute la frase Final del trabajo cuando se alcance el final del fichero de entrada Asiacute por ejemplo si la entrada analizada es

(a + b) (( x )) (y + 7)

los resultados obtenidos seraacuten

() (())()

Final del trabajo

En la especificacioacuten JavaCC que se propone como solucioacuten soacutelo se muestran las reglas sintaacutecticas y le -xicograacuteficas (en el resto no hay cambios) en esta solucioacuten puede apreciarse- las piezas sintaacutecticas correspondientes a los pareacutentesis se han definido de manera nominal para po-der asociarles una accioacuten lexicograacutefica- los caracteres incluidos en la pieza de tipo SKIP se han descompuesto en dos grupos ya que la ac-cioacuten lexicograacutefica incorporada soacutelo afecta a uno de ellos

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ltpAbrirgt expresion() ltpCerrargt

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

TOKEN

26

lt pAbrir ( gt Systemoutprint(()

TOKEN lt pCerrar ) gt Systemoutprint())

ltgtTOKEN lt EOF gt Systemoutprintln ( Final del trabajo )

SKIP n Systemoutprint( n)

SKIP | t | r

loz Bloque de declaraciones lexicograacuteficasCada definicioacuten de una pieza sintaacutectica puede llevar asociada una accioacuten lexicograacutefica que se ejecuta in-mediatamente despueacutes de detectar en la entrada un lexema que se ajusta a la definicioacuten de la pieza cada accioacuten lexicograacutefica es un bloque de coacutedigo Java que puede contener sus propias declaraciones Pero a ve-ces interesa tener declaraciones que sean compartidas por todas las acciones lexicograacuteficas con indepen-dencia de la pieza a la que esteacute asociada cada una de ellas

Para incluir declaraciones que puedan usarse desde el coacutedigo asociado a cualquiera de las piezas sintaacutecti -cas se dispone de una construccioacuten con el siguiente formato

TOKEN_MGR_DECLS

que consta de una palabra reservada seguida del siacutembolo dos puntos seguido de un bloque de coacutedigo Java (delimitado por llaves) esta declaracioacuten se puede colocar en cualquier parte de una especificacioacuten JavaCC si se incluye soacutelo puede ponerse una vez

Todas las declaraciones de variables o de meacutetodos puestas en el bloque de declaraciones lexicograacuteficas compartidas son accesibles desde el coacutedigo de cualquier accioacuten lexicograacutefica la justificacioacuten es sencilla el bloque de declaraciones se traslada al principio del coacutedigo del analizador lexicograacutefico generado y las ac-ciones lexicograacuteficas tambieacuten se trasladan a ese analizador

loz Declaraciones lexicograacuteficas predefinidasDe lo expuesto hasta ahora se aprecia que en una accioacuten lexicograacutefica asociada a una pieza sintaacutectica re-sultan accesibles

- las declaraciones de su propio bloque (si tiene declaraciones)- los componentes del bloque de declaraciones compartidas (si estaacute incluido en la especificacioacuten)

Ademaacutes de esto se tienen otras declaraciones pertenecientes a la clase TokenManager que constituye el analizador lexicograacutefico que tambieacuten son utilizables desde cualquiera de las acciones lexico-graacuteficas estas declaraciones pueden considerarse como ldquopredefinidasrdquo ya que son incorporadas automaacuteti-camente por el generador Entre estas declaraciones accesibles se encuentran las siguientes

[ static ] StringBuffer imagecontiene la cadena de caracteres representativa de la pieza sintaacutectica actual que se acaba de recono-cer en le entrada se trata de un campo de la clase y se declara como static cuando el analizador

27

se ha generado con la opcioacuten (por defecto) Static = true si se generase con la opcioacuten Sta-tic = false no quedariacutea declarada como static

Token matchedToken

variable de clase Token que representa la pieza sintaacutectica actual la que se acaba de encontrar en la entrada se trata de una variable local del meacutetodo al que se ha trasladado el coacutedigo de la accioacuten (y por ello accesible desde ese coacutedigo)

El campo image puede usarse tanto si la accioacuten lexicograacutefica estaacute asociada a una pieza declarada con TOKEN como si lo estaacute a una secuencia declarada con SKIP Sin embargo la variable matchedToken no puede usarse en una accioacuten asociada a un patroacuten especificado como SKIP el motivo es que lo declara-do como SKIP no constituye propiamente una pieza sintaacutectica ya que no se va a comunicar al analizador sintaacutectico

∆ Ejemplo 6

Se propone aquiacute una nueva solucioacuten para el ejemplo nordm 4 expuesto anteriormente

Esta nueva solucioacuten tal y como se expone aquiacute admite algunas mejoras ya que su pretensioacuten soacutelo es ser-vir de ilustracioacuten para algunos aspectos que se acaban de citar (maacutes adelante se propone otra solucioacuten al-ternativa)

En la especificacioacuten de la solucioacuten es posible apreciar esto- las acciones lexicograacuteficas pueden llevar sus propias declaraciones las variables nLineaVar nColumnaVar nLineaCte y nColumnaCte se declaran dentro de las acciones

- la variable matchedToken a la que se hace referencia en el coacutedigo de la accioacuten lexicograacutefica aso-ciada a una variable contiene la informacioacuten sobre la pieza sintaacutectica que se acaba de detectar una va-riable de la expresioacuten analizada anaacutelogamente con la referencia relativa a una constante- el nombre image que aparece en las acciones lexicograacuteficas se refiere al campo de la clase Expr-MinTokenManager (analizador lexicograacutefico) noacutetese que en efecto se comunica como un paraacuteme-tro de clase StringBuffer

- la variable numero se emplea para numerar las constantes y variables encontradas

- los nombres beginLine y beginColumn son campos (puacuteblicos) de la clase Token por ello se pueden aplicar a un objeto de esa clase

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

void unaExpresion()

28

expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n StringBuffer lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

TOKEN lt variable [a-z] gt

int nLineaVar nColumnaVar numero++ nLineaVar = matchedTokenbeginLine nColumnaVar = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaVar nColumnaVar)

TOKEN lt constante ( [0-9] ) + gt

int nLineaCte nColumnaCte

29

numero++ nLineaCte = matchedTokenbeginLine nColumnaCte = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaCte nColumnaCte)

SKIP | n | t | r

La especificacioacuten precedente se puede escribir de otra manera maacutes escueta como se indica a continua-cioacuten (soacutelo se muestran las modificaciones) sobre esta nueva especificacioacuten puede mencionarse- se han eliminado por innecesarias las variables declaradas dentro de las acciones lexicograacuteficas- el nombre image que aparece en el bloque de las declaraciones compartidas se refiere al campo de la clase Token noacutetese que en efecto se aplica a un objeto de esa clase y se comunica como un paraacute -metro de tipo String

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

TOKEN lt variable [a-z] gt

numero++ grabarDatosPieza(numero matchedToken)

TOKEN lt constante ( [0-9] ) + gt

numero++ grabarDatosPieza(numero matchedToken)

loz Accioacuten lexicograacutefica comuacutenSi hubiera que realizar la misma tarea tras la deteccioacuten de todas y cada una de las piezas sintaacutecticas una posibilidad incoacutemoda seriacutea repetir el mismo coacutedigo en todas las acciones lexicograacuteficas para evitar tanta reiteracioacuten se tiene la opcioacuten Common_Token_Action su valor por defecto es false pero si se le asigna el valor true es posible definir una ldquoaccioacuten lexicograacutefica comuacutenrdquo que se escribe una uacutenica vez y se aplica a todas las piezas sintaacutecticas

30

Cuando la opcioacuten Common_Token_Action se establece con el valor true es obligado incluir el blo-que de declaraciones lexicograacuteficas compartidas (TOKEN_MGR_DECLS) Ademaacutes en ese bloque se preci-sa la definicioacuten de un meacutetodo cuyo encabezamiento sea

[ static ] void CommonTokenAction(Token t)

el coacutedigo de este meacutetodo constituye la accioacuten lexicograacutefica comuacuten que se ejecutaraacute despueacutes de la deteccioacuten de cualquier pieza sintaacutectica el meacutetodo tiene un paraacutemetro de tipo Token mediante el que se puede co-municar la informacioacuten correspondiente a la pieza que se acaba de encontrar en la entrada analizada

Si una pieza sintaacutectica tiene asociada una accioacuten lexicograacutefica propia la accioacuten lexicograacutefica comuacuten (si es-taacute definida) se ejecutaraacute inmediatamente despueacutes de realizar la accioacuten propia

La accioacuten lexicograacutefica comuacuten se aplica a todas las piezas sintaacutecticas definidas como TOKEN bien sea no-minalmente (en las reglas lexicograacuteficas) o bien anoacutenimamente (incorporadas dentro de las reglas sintaacutecti-cas) sin embargo no se aplica cuando la secuencia acoplada se corresponde con una expresioacuten regular es-pecificada como SKIP (noacutetese que por el contrario una especificacioacuten SKIP siacute puede tener asociada una accioacuten lexicograacutefica propia)

El coacutedigo del meacutetodo CommonTokenAction se traslada como un componente maacutes del bloque de decla-raciones compartidas (TOKEN_MGR_DECLS) al principio de la clase del analizador lexicograacutefico

∆ Ejemplo 7

Se pretende ahora ampliar el ejemplo de presentacioacuten con la obtencioacuten de una relacioacuten numerada de los lexemas de las piezas sintaacutecticas encontradas en la entrada analizada en el caso de las variables y de las constantes antes del lexema se mostraraacute un indicativo (Var Const) por ejemplo si la expresioacuten de la entrada es

( a + 987 ) b

se grabaraacute la salida

( 1Var a 2+ 3Const 987 4) 5 6Var b 7 8

En la solucioacuten que se propone a continuacioacuten se muestran la definicioacuten de las opciones y las reglas sin-taacutecticas y lexicograacuteficas (en el resto no hay modificaciones) puede resaltarse lo siguiente- las llamadas al meacutetodo CommonTokenAction se incorporan automaacuteticamente no hay que incluir-las en la especificacioacuten como paraacutemetro de llamada a este meacutetodo siempre se emplea el objeto que re -presenta la pieza que se acaba de detectar- la accioacuten lexicograacutefica comuacuten realiza la actualizacioacuten del contador de piezas y la grabacioacuten del lexe-ma de la pieza detectada con su nuacutemero de orden- la accioacuten lexicograacutefica comuacuten tambieacuten se aplica a las piezas sintaacutecticas anoacutenimas (pareacutentesis y ope-radores) y a la pieza especial que representa el final del fichero (EOF)

- las piezas sintaacutecticas ltconstantegt y ltvariablegt tienen asociada una accioacuten lexicograacutefica propia que se ejecuta antes de la accioacuten lexicograacutefica comuacuten- los caracteres que se saltan (SKIP) no estaacuten afectados por la accioacuten lexicograacutefica comuacuten

options Ignore_Case = true Common_Token_Action = true

31

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarIndicativo(String indicativo) Systemoutprint(indicativo + )

static void grabarLexema (int num String lexema) Systemoutprint(lexema + + num + n)

static void CommonTokenAction(Token pieza) numero++ grabarLexema(numero piezaimage)

TOKEN lt variable [a-z] gt

grabarIndicativo(Var)

32

TOKEN lt constante ( [0-9] ) + gt

grabarIndicativo(Const)

SKIP | n | t | r

A modo de ejemplo de aplicacioacuten de la accioacuten lexicograacutefica comuacuten en lo que sigue se ofrece una solucioacuten para la grabacioacuten (en el fichero predefinido de salida) del texto analizado con las liacuteneas numeradas

∆ Ejemplo 8 [ Listado del texto analizado ]Se desea completar el ejemplo de presentacioacuten con la grabacioacuten de un listado con las liacuteneas numeradas del texto analizado (noacutetese que una expresioacuten puede escribirse ocupando varias liacuteneas) No se muestra la especificacioacuten sintaacutectica debido a que no hay ninguna modificacioacuten en ella Los aspectos resentildeables de la solucioacuten que se propone son- se usa la variable numLin como contador de liacuteneas

- en la variable linea se van yuxtaponiendo los lexemas de las piezas sintaacutecticas de una liacutenea esta yuxtaposicioacuten se realiza mediante la accioacuten lexicograacutefica comuacuten- las secuencias reconocidas con los patrones especificados como SKIP no implican la ejecucioacuten de la accioacuten lexicograacutefica comuacuten por ello su lexema (almacenado en la variable image) ha de yuxtaponer-se expliacutecitamente- hay que separar el patroacuten correspondiente al fin de liacutenea para detectar el momento en que ha de gra-barse la liacutenea completa cuyo final se ha alcanzado

options Ignore_Case = true Common_Token_Action = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(nnAnalisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

TOKEN_MGR_DECLS static int numLin = 0 static String linea =

33

static void CommonTokenAction(Token pieza) linea = linea + piezaimage

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP lt | t | r gt linea = linea + image

SKIP n numLin++ Systemoutprintln(numLin + + linea) linea =

A modo de recapitulacioacutenↄ Accioacuten lexicograacutefica Accioacuten sintaacutectica

bull accioacuten lexicograacutefica bloque de coacutedigo Java asociado a una pieza sintaacutectica nominal (TOKEN) o a una secuencia de caracteres (SKIP) se ejecuta cuando se detecta en la entrada la pieza o la secuencia corres-pondiente no se puede aplicar a piezas sintaacutecticas anoacutenimas (autodefinidas con comillas)bull accioacuten sintaacutectica bloque de coacutedigo Java asociado a un punto de la estructura sintaacutectica (el punto indica-do por el sitio de la produccioacuten donde se inserta el bloque) se ejecuta cuando el anaacutelisis de la entrada pa-sa por ese punto de la estructura

ↄ Valor de una pieza sintaacutectica comunicadaEl analizador lexicograacutefico comunica al analizador sintaacutectico una pieza a traveacutes de un valor de la clase Token (fichero Tokenjava) un objeto de la clase Token representa las caracteriacutesticas de la pieza comunicada En la clase Token entre otros se tienen los campos

String imagelexema de la pieza

int beginLine beginColumn endLine endColumnposicioacuten del lexema en el fichero analizado (indicada por los nuacutemeros de fila y de columna de su comienzo y de su terminacioacuten)

ↄ Valor asociado a un siacutembolo

34

En las producciones que especifican la sintaxis se pueden incluir asignaciones que sirven para dejar ano-tado el valor asociado a un siacutembolo

bull siacutembolo no terminal valor = nombreSimbolo()el meacutetodo de anaacutelisis asociado a nombreSimbolo se ha declarado de un cierto tipo (distinto del tipo void) valor ha de ser una variable del mismo tipo que el meacutetodo

bull siacutembolo terminal dato = lt nombrePieza gtel valor asignado es el valor de la clase Token representativo de la pieza sintaacutectica (nominal) dato ha de ser una variable de tipo Token

ↄ Miradas sobre la pieza sintaacutectica comunicadaLa pieza sintaacutectica comunicada puede mirarse desde cada uno de los dos analizadores

bull analizador sintaacutecticola pieza comunicada (desde el analizador lexicograacutefico) se tiene anotada en un campo de la clase del analizador sintaacutectico declarado como

(static) public Token token

este valor es accesible desde el coacutedigo de todos los meacutetodos de anaacutelisis sintaacutectico el contenido del campo variacutea cuando se recibe una nueva pieza sintaacutectica

bull analizador lexicograacuteficola pieza dispuesta para ser comunicada (al analizador sintaacutectico) se tiene anotada en una variable local declarada como

Token matchedToken

este valor es accesible para el coacutedigo de todas las acciones lexicograacuteficas

ↄ El lexema de la pieza comunicadaEl lexema de la pieza sintaacutectica comunicada se encuentra disponible en dos sitios distintos que compar-ten el mismo nombre pero que son de distinto tipo y se usan de distinta manera

bull String image

es un campo de la clase TOKEN puede consultarse en relacioacuten con un objeto de esa clase que esteacute disponible

bull StringBuffer image

es un campo de la clase del analizador lexicograacutefico ( ∙ ∙ ∙ ∙ TokenManagerjava) es accesible desde el coacutedigo de la clase y por lo tanto desde todas las acciones lexicograacuteficas (bloques de coacute-digo que estaacuten incorporados al analizador lexicograacutefico)

se trata de un valor disponible tanto para las piezas sintaacutecticas nominales (TOKEN) como para las secuencias que se saltan (SKIP)

ↄ Resumen relativo a la lexicografiacutea

TOKEN pieza sintaacutectica nominal∙ ∙ ∙ pieza sintaacutectica anoacutenimaSKIP secuencia de caracteres que no forman pieza

35

Precisiones sobre el analizador sintaacutectico generadoPuede decirse que en su funcionamiento maacutes sencillo cuando no se emplean las posibilidades de examen por adelantado (ldquolookaheadrdquo) JavaCC comprueba que las reglas sintaacutecticas cumplen la condicioacuten LL(1) y generan un analizador descendente-predictivo-recursivo Pero esta apreciacioacuten hay que matizarla ya que el analizador sintaacutectico generado no se ajusta en sentido estricto al modelo de analizador LL(1)

En el modelo de anaacutelisis LL(1) el orden del examen de las distintas alternativas de expansioacuten de un siacutembo-lo no terminal es indiferente ya que se conocen los siacutembolos directores de cada una de ellas (que resultan ser conjuntos disjuntos) en la implementacioacuten del subprograma de anaacutelisis asociado al siacutembolo no terminal ltNombreSimbgt cuyas producciones son

ltNombreSimbgt = α1

| α2

∙ ∙ ∙

| αn

se podriacutea preguntar en cualquier orden si la pieza actual pertenece al conjunto de siacutembolos directores de las distintas reglas Sin embargo en el analizador sintaacutectico generado por JavaCC la implementacioacuten del meacute-todo asociado al siacutembolo no terminal ltNombreSimbgt no sigue esa pauta

Si las anteriores reglas sintaacutecticas se transcriben en JavaCC de la forma

void nombreSimb () α1-jcc

| α2-jcc

∙ ∙ ∙

| αn-jcc

1

accioacutenlexicograacutefica

comuacuten

Commom -TokenAction2

accioacutenlexicograacutefica

propia

(accioacuten ligadaa una pieza)3

bloque dedeclaracioneslexicograacuteficas

TOKEN_MGR_DECLS4

declaracioneslexicograacuteficaspredefinidas

(disponibilidad

de uso)TOKENsisisisi∙ ∙ ∙sino--SKIPnosisisi

Las indicaciones si de las columnas 3 y 4 son consecuencia de las indicaciones de la columna 2

La accioacuten lexicograacutefica comuacuten se ejecuta despueacutes de la accioacuten lexicograacutefica propia

36

el meacutetodo de anaacutelisis generado procede de la siguiente manera

se van considerando las alternativas sucesivamente en el mismo orden en el que estaacuten especifica-das primero la de α1 (α1-jcc) despueacutes la de α2 (α2-jcc) etc

para cada alternativa seleccionada se comprueba si la pieza por adelantado coincide con alguno de los siacutembolos iniciales de la parte derecha y si hay coincidencia se prosigue el anaacutelisis con ella si no hay coincidencia se pasa a la alternativa siguiente

siguiendo con este orden si se llega al final de las alternativas sin haberse encontrado coincidencia alguna se produce un error sintaacutectico

En el analizador generado por JavaCC hay una peculiaridad que no se tiene en los analizadores LL(1) es-trictamente considerados la palabra vaciacutea siacute se considera como posible siacutembolo inicial de la parte derecha una regla (esto ocurre siempre que la parte derecha es anulable) De esta peculiaridad se deriva una conse-cuencia que ha de tenerse en cuenta al escribir la especificacioacuten dado que siempre es posible considerar la palabra vaciacutea como la siguiente pieza por adelantado si se llega a examinar la alternativa correspondiente a una produccioacuten anulable el analizador generado siempre seleccionaraacute esa alternativa con independencia de la siguiente pieza que esteacute presente en la entrada que queda por analizar

Asiacute pues si alguna de las producciones que definen un siacutembolo no terminal es anulable es importante el or-den en que se escriben las alternativas en una especificacioacuten sintaacutectica JavaCC el funcionamiento del ana-lizador generado siacute depende de la colocacioacuten elegida para las reglas

Para ilustrar estas peculiaridades del analizador sintaacutectico generado por JavaCC a continuacioacuten se expone un ejemplo

∆ Ejemplo 9

Se quiere obtener un analizador leacutexico-sintaacutectico de textos que se ajustan al siguiente formato una se-cuencia de uno o maacutes nombres seguida del siacutembolo igual seguido de un nuacutemero entero si la secuencia tiene maacutes de un nombre han de estar separados bien por una coma en todos los casos o bien por el siacutem-bolo dos puntos en todos los casos esto es son correctas las secuencias

uno dos tres = 123exclusivo = 0Abajo Arriba = 57

y son incorrectas las secuenciasuno dos tres = 123

exclusivo inclusive = 0

fuera dentro = 56

Una gramaacutetica que define las secuencias con este formato es

ltListagt = nombre ltOtrosNombresgt = numero

ltOtrosNombresgt = nombre nombre | nombre nombre | ε

se trata de una gramaacutetica que cumple la condicioacuten LL(1)- el siacutembolo director para la alternativa vaciacutea es el siacutembolo igual es evidente pues que las tres reglas de ltOtrosNombresgt tienen siacutembolos directores distintos

37

- la decisioacuten de abandonar la repeticioacuten del anaacutelisis de un nuevo nombre (en las dos primeras alterna-tivas de ltOtrosNombresgt) se puede tomar ante la presencia del siacutembolo igual

En la implementacioacuten de un analizador sintaacutectico LL(1) en sentido estricto la seleccioacuten de la alternativa vaciacutea para expandir el siacutembolo ltOtrosNombresgt se hariacutea tras comprobar expliacutecitamente que la pie-za sintaacutectica por adelantado es el siacutembolo igual

El funcionamiento del analizador generado por JavaCC depende del orden de colocacioacuten de las reglas A continuacioacuten se exponen varias posibilidades de especificacioacuten y se comenta el analizador generado en cada caso

bull Especificacioacuten primeraSe transcriben literalmente las reglas tal y como se han escrito antes en la notacioacuten BNF noacutetese que para poder codificar la parte derecha de la regla vaciacutea ha de incluirse una accioacuten sintaacutectica vaciacutea

PARSER_BEGIN (Igualdad)

public class Igualdad

public static void main (String[] argum) throws ParseException Igualdad analisis = new Igualdad(Systemin) analisissecuencia() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (Igualdad)

void secuencia() lista() ltEOFgt

void lista() lt nombre gt otrosNombres() = lt numero gt

void otrosNombres() lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt ) |

TOKEN lt nombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN lt numero ( [ 0 - 9 ] )+ gt

SKIP | r | n

38

El analizador generado funciona correctamente puesto que en el meacutetodo otrosNombres se selec-ciona la alternativa vaciacutea despueacutes de haber comprobado que la pieza por adelantado no es siacutembolo ini-cial de ninguna de las dos alternativas precedentes (no es una coma ni un siacutembolo dos puntos)

bull Especificacioacuten segundaSe considera la misma especificacioacuten anterior pero cambiando el orden de colocacioacuten de las reglas ahora la produccioacuten vaciacutea se coloca como primera alternativa (soacutelo se muestran las partes modifica-das)

void otrosNombres() | lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt )

Con la entrada de esta especificacioacuten el generador JavaCC produce un mensaje en el que se avisa de que las dos alternativas que estaacuten colocadas detraacutes de la alternativa vaciacutea nunca podraacuten seleccionarse aun asiacute se genera un analizador Pero es un analizador que funciona incorrectamente asiacute por ejemplo la entrada

mxa nyz = 57

se considera incorrecta despueacutes de visto el primer nombre se intenta aplicar la produccioacuten vaciacutea (se espera encontrar el siacutembolo igual) pero habriacutea que aplicar la regla cuyo siacutembolo director es la coma

bull Especificacioacuten terceraSe utilizan las posibilidades de la notacioacuten BNF-Ampliada para prescindir de la especificacioacuten expliacuteci-ta de la palabra vaciacutea se aprovecha que JavaCC admite el operador + para indicar repeticiones de una o maacutes veces la alternativa vaciacutea estaacute impliacutecitamente contemplada en la posibilidad de cero repeti-ciones que contempla el operador (soacutelo se muestran las partes modificadas en la especificacioacuten)

void otrosNombres() ( lt nombre gt )+ | ( lt nombre gt )

El meacutetodo otrosNombres() generado hace las comprobaciones en el siguiente orden

- se comprueba si la pieza por adelantado es una coma (siacutembolo inicial de la primera alternativa) y en este caso se selecciona la primera regla- si no es una coma se comprueba si es un siacutembolo dos puntos (siacutembolo inicial de la segunda alter-nativa) y en este caso se selecciona la segunda regla- si no es un siacutembolo dos puntos se considera la palabra vaciacutea como siacutembolo inicial (siempre posi-ble impliacutecitamente) para la segunda alternativa en este caso para que no haya error la pieza por adelantado ha de ser un siacutembolo igual

Se trata pues de un analizador que funciona correctamente

bull Especificacioacuten cuarta

39

Se pone la misma especificacioacuten anterior pero cambiando el orden de consideracioacuten de la regla vaciacutea ahora se incluye impliacutecitamente en la primera alternativa (soacutelo se muestran las partes modificadas)

void otrosNombres() ( lt nombre gt ) | ( lt nombre gt )+

Para esta especificacioacuten el generador JavaCC emite un aviso en el que se indica que la segunda alter -nativa nunca se seleccionaraacute no obstante se genera un analizador que no funciona como se supone que deberiacutea hacerlo por ejemplo el anaacutelisis de la entrada

nyz mxa = 1

produce un error despueacutes de tratado el primer nombre se intenta aplicar la produccioacuten vaciacutea (corres-pondiente a la repeticioacuten de cero veces contemplada en la primera regla) por lo que no se encuentra el esperado siacutembolo igual

40

Page 9: JavaCC

∙ ∙ ∙ ∙

public class NombreDeLaEspecif

∙ ∙ ∙ ∙

∙ ∙ ∙ ∙

PARSER_END (NombreDeLaEspecif)

Una posible versioacuten sencilla de la clase directora es

public class NombreDeLaEspecif public static void main (String[] argum) throws ParseException NombreDeLaEspecif anLeSi = new NombreDeLaEspecif (Systemin) anLeSisimboloPrograma()

se crea el objeto anLeSi para poder aplicar los meacutetodos de anaacutelisis se asocia la entrada standard ( Systemin ) a la entrada para el analizador generado se aplica al objeto creado el meacutetodo de anaacutelisis correspondiente al siacutembolo inicial de la gramaacutetica

loz Seccioacuten de sintaxisEn esta seccioacuten se describe la sintaxis del lenguaje para el que se desea generar el analizador usaacutendose pa-ra ello una notacioacuten parecida a la BNF En lo que sigue se expone la forma de las producciones tal y como se escriben en JavaCC ponieacutendolas en comparacioacuten con las producciones de la notacioacuten BNF-Ampliada

bull Reglas sintaacutecticasEl conjunto de reglas que definen un siacutembolo no terminal

ltNombreSimbgt = α1

| α2

∙ ∙ ∙

| αn

se codifica de la siguiente manera

void nombreSimb () α1-jcc

| α2-jcc

∙ ∙ ∙

| αn-jcc

8

- el nombre del siacutembolo no terminal (parte izquierda de las reglas) se escribe como el encabeza-miento de un meacutetodo en Java sin argumentos y sin valor de devolucioacuten (meacutetodo de tipo void) se sigue como en Java la costumbre de escribir los nombres de los meacutetodos empezando con letra mi-nuacutescula

- el siacutembolo dos puntos representa la separacioacuten entre las partes izquierda y derechas de las reglas (en vez del siacutembolo = de la notacioacuten BNF)

- el siacutembolo barra vertical representa la separacioacuten entre distintas partes derechas con la misma parte izquierda (igual que en la notacioacuten BNF)

- despueacutes del siacutembolo dos puntos se pone un bloque de coacutedigo Java este coacutedigo se suele dedicar a la realizacioacuten de tareas semaacutenticas si se pretende hacer soacutelo un anaacutelisis sintaacutectico el bloque que-daraacute vaciacuteo (aunque siempre es obligada su presencia)

- el conjunto de las alternativas (partes derechas de las reglas) estaacute delimitado mediante llaves como si constituyese un bloque de coacutedigo Java

- αi-jcc representa la codificacioacuten de una alternativa (parte derecha) empleaacutendose una nota-cioacuten que se describe a continuacioacuten

bull Siacutembolos no terminales en las partes derechasEn la notacioacuten BNF se pone ltNombreSimbgt en la notacioacuten JavaCC se escribe

nombreSimb()

esto es se pone como si fuese una llamada en Java a un meacutetodo sin argumentos

bull Siacutembolos terminales en las partes derechasSe considera una clasificacioacuten en las piezas sintaacutecticas (siacutembolos terminales de una gramaacutetica sintaacutectica) distinguieacutendose entre piezas sintaacutecticas nominales y anoacutenimas

son nominales las piezas sintaacutecticas a las que se asocia un nombre en otra parte de la especificacioacuten (maacutes adelante se describe la manera de indicar esta asociacioacuten) ese nombre asociado a la pieza es el que se usa al escribir las partes derechas de las producciones En la notacioacuten JavaCC un siacutembolo terminal no-minal se escribe poniendo el nombre delimitado por los caracteres lt y gt

ltnombreTerminalgt

son anoacutenimas las piezas sintaacutecticas que no tienen nombre asociado no se precisa porque la pieza se re -presenta por su propia secuencia de caracteres (lexema) En la notacioacuten JavaCC un siacutembolo terminal anoacutenimo se escribe poniendo su lexema delimitado por comillas por ejemplo

= lt lt= END

bull Metasiacutembolos de opcionalidad y de repeticioacutenEn la notacioacuten BNF se emplean los corchetes para indicar opcionalidad en la notacioacuten JavaCC se utilizan tambieacuten esos mismos siacutembolos

[ α ] en ambas notaciones

En la notacioacuten BNF se emplean las llaves para indicar una repeticioacuten de cero o maacutes veces en la notacioacuten JavaCC se utilizan los pareacutentesis y un asterisco detraacutes del pareacutentesis de cerrar

α en notacioacuten BNF-Ampliada

( α ) en notacioacuten JavaCC

bull Otros metasiacutembolos de la notacioacuten JavaCC

9

La opcionalidad tambieacuten se puede indicar empleando el metasiacutembolo de interrogacioacuten

( α )

Para indicar repeticioacuten de una o maacutes veces se utiliza el metasiacutembolo operador aditivo suma

( α )+

bull Control del final de fichero de entradaPara controlar el final del fichero en el texto analizado ha de antildeadirse una regla sintaacutectica especial que tenga (seguacuten la notacioacuten BNF) la forma

ltProgramaCompletogt = ltProgramagt FF

donde

ltProgramaCompletogt es un nuevo siacutembolo (no terminal) auxiliar antildeadido

ltProgramagt es el siacutembolo inicial de la gramaacutetica sintaacutectica

FF es una representacioacuten del final del fichero

En JavaCC el final de fichero se escribe con la notacioacuten ltEOFgt siendo EOF una palabra reservada la regla sintaacutectica especial antildeadida a la especificacioacuten quedariacutea codificada asiacute

void programaCompleto() programa() ltEOFgt

bull Representacioacuten de la produccioacuten vaciacuteaLas producciones vaciacuteas (reglas sintaacutecticas cuya parte derecha es la palabra vaciacutea ε ) no se pueden repre-sentar expliacutecitamente en una especificacioacuten leacutexico-sintaacutectica escrita en JavaCC Para indicar la presencia de una produccioacuten vaciacutea han de aplicarse las posibilidades de la notacioacuten BNF-Ampliada seguacuten esta nota-cioacuten cuando se indica la opcionalidad de un componente se estaacute especificando impliacutecitamente la palabra vaciacutea (en el caso de que el componente no esteacute presente)

Asiacute pues si se quisiera escribir en JavaCC la construccioacuten sintaacutectica expresada por las reglas

ltComponentegt = α

| ε

deberiacutean de considerarse con la notacioacuten equivalente

ltComponentegt = [ α ]

y si hubiera varias alternativas no vaciacuteas

ltComponentegt = α | β

| ε

habriacutea que considerarlas como

ltComponentegt = [ α | β ]

o tambieacuten podriacutea verse como

ltComponentegt = α

10

| [ β ]

Como ejemplo sea la especificacioacuten sintaacutectica de una llamada a una funcioacuten que puede tener cero o maacutes paraacutemetros separados por comas aunque no haya paraacutemetros los pareacutentesis estaacuten presentes Las siguien-tes reglas definen esta estructura

ltLlamadagt = id ( ltParametrosgt )

ltParametrosgt = ltExpresiongt ltExpresiongt | ε

En JavaCC la definicioacuten del siacutembolo ltParametrosgt se puede escribir como sigue (noacutetese que aquiacute los pareacutentesis empleados para aplicar el operador son metasiacutembolos)

void parametros() [ expresion() ( expresion() ) ]

loz Seccioacuten de lexicografiacuteaEn esta seccioacuten se indica la lexicografiacutea del lenguaje para el que se va a generar el analizador la notacioacuten de JavaCC que representa la forma de cada una de las piezas sintaacutecticas es una variante de las bien conoci-das expresiones regulares

bull El nombre de las piezas sintaacutecticas nominalesPara asociar un nombre a una pieza sintaacutectica y para al mismo tiempo definir la forma de la pieza se em-plea la notacioacuten

TOKEN lt nombreTerminal Expresioacuten-Regular gt

- TOKEN es una palabra reservada detraacutes de ella se pone el separador dos puntos

- nombreTerminal representa el nombre que se quiere asociar a la pieza sintaacutectica

- Expresioacuten-Regular indica una expresioacuten regular que especifica la forma de la pieza

- el nombre y la expresioacuten se separan mediante el caraacutecter dos puntos y conjuntamente se deli -mitan mediante los caracteres lt y gt a su vez toda la asociacioacuten se delimita con llaves

bull Expresiones regulares en JavaCCLas expresiones regulares en una especificacioacuten JavaCC se escriben de manera parecida a la forma utiliza-da en otras conocidas notaciones aunque con ciertas peculiaridades A continuacioacuten se mencionan los as-pectos maacutes significativos de las expresiones regulares JavaCC y algunos ejemplos

Los operadores de las expresiones son

| unioacuten

repeticioacuten cero o maacutes veces

+ repeticioacuten una o maacutes veces

opcionalidad

11

Los pareacutentesis permiten fijar el orden de aplicacioacuten de los operadores Una peculiaridad de las expresiones regulares JavaCC es que los operadores de repeticioacuten y de opcionalidad requieren que el componente al que se aplican esteacute en todo caso delimitado por pareacutentesis

Se pueden citar las siguientes caracteriacutesticas baacutesicas de las expresiones regulares en JavaCC

Se tiene la posibilidad de definir conjuntos de caracteres poniendo entre corchetes la relacioacuten de carac-teres separados por comas y cada caraacutecter delimitado por comillas En un conjunto se puede fijar un ran-go de caracteres poniendo el primero y el uacuteltimo separados por el siacutembolo guioacuten (-) A veces resulta maacutes coacutemodo indicar los caracteres que no pertenecen al conjunto para ello se puede definir el comple -mentario de un conjunto poniendo el siacutembolo ~ delante del corchete inicial

Para indicar que una secuencia de caracteres colocados consecutivamente constituye por siacute misma una expresioacuten regular (o un componente de una expresioacuten) se escribe la secuencia delimitada por comillas El siacutembolo se emplea como metacaraacutecter para indicar que el siacutembolo que estaacute inmediatamente a su derecha se considere como caraacutecter del alfabeto no como posible metacaraacutecter (operador delimitador)

En una expresioacuten regular se pueden incluir como si fueran caracteres las secuencias de escape con la misma notacioacuten y el mismo significado que en Java o en C

Noacutetese que en una produccioacuten sintaacutectica la opcionalidad se puede especificar de dos formas con corchetes o con el siacutembolo de interrogacioacuten sin embargo en una expresioacuten regular la opcionalidad soacutelo se puede es-pecificar mediante el siacutembolo de interrogacioacuten (los corchetes en las expresiones regulares son para definir conjuntos de caracteres)

A continuacioacuten se muestran ejemplos de expresiones regulares escritas seguacuten la notacioacuten JavaCC

ENDsecuencia de las tres letras mayuacutesculas que conforman la palabra END

whilesecuencia de las cinco letras minuacutesculas que conforman la palabra while

ncaraacutecter representativo del fin de liacutenea (secuencia de escape)

rcaraacutecter representativo del retorno de carro (secuencia de escape)

caraacutecter dos puntos

=secuencia de dos caracteres (siacutembolo de asignacioacuten en Pascal)

gtcaraacutecter representativo del operador de relacioacuten mayor

(i | I)(f | F)palabra ldquoifrdquo escrita con letras minuacutesculas o mayuacutesculas (cuatro combinaciones)

if | IFpalabra ldquoifrdquo escrita soacutelo con minuacutesculas o soacutelo con mayuacutesculas (dos combinaciones)

12

u | o | i | e | auna cualquiera de las vocales minuacutesculas (pero soacutelo una de ellas)

[ A E I O U ]una cualquiera de las vocales mayuacutesculas (pero soacutelo una de ellas)

[ a - z ntilde ]una cualquiera de las letras minuacutesculas incluyendo la letra ntilde

[ a - z A - Z 0 - 9 ]una cualquiera de las letras o de las cifras decimales

([ 0 - 9 ])+constante entera (nuacutemero de una o maacutes cifras)

noacutetese que es incorrecto poner [ 0 - 9 ]+

( + | - )([ 0 - 9 ])+constante entera precedida opcionalmente por un signo

noacutetese que la primera cifra ha de seguir inmediatamente al signo (si existe)

([+ -])([ 0 - 9 ])+representa lo mismo que la expresioacuten anterior

[ a - z ] ([ a - z 0 - 9 ])una letra minuacutescula seguida de cero o maacutes letras minuacutesculas o cifras decimales

~[a - z ntilde A - Z Ntilde]cualquier caraacutecter que no sea una letra

noacutetese que la expresioacuten representa un uacutenico caraacutecter

~[n r]cualquier caraacutecter (un uacutenico caraacutecter) excepto el fin de liacutenea y el retorno de carro

~[ ]cualquier caraacutecter del alfabeto (un uacutenico caraacutecter)

([ 0 - 9 ])+ ( ([ 0 - 9 ])+)constante aritmeacutetica con parte decimal opcional

Los modernossecuencia de catorce caracteres Los modernos

noacutetese el uso de para imponer la condicioacuten de caraacutecter del siacutembolo comillas

nsecuencia de cuatro caracteres n

noacutetese que aquiacute no se representa la secuencia de escape

bull Piezas sintaacutecticas internas (ldquoprivaterdquo)

13

Es posible definir piezas sintaacutecticas auxiliares llamadas ldquointernasrdquo porque no se comunican al analizador sintaacutectico sino que soacutelo sirven para facilitar la escritura de la forma de las piezas sintaacutecticas que siacute se co-munican con el uso de las piezas internas es posible evitar repeticiones de escritura y hacer maacutes legible la especificacioacuten de las caracteriacutesticas lexicograacuteficas Las piezas internas permiten dar nombre a subexpre-siones regulares que a su vez pueden emplearse como componentes para escribir expresiones maacutes com-plejas que incorporan esos nombres Para indicar que una pieza sintaacutectica se considere como interna se pone el siacutembolo delante del nombre asociado a la pieza

A continuacioacuten se muestra un ejemplo tiacutepico de utilizacioacuten de las piezas internas en una especificacioacuten le-xicograacutefica en JavaCC Se quieren definir tres piezas sintaacutecticas que representan un nombre una constante entera y una constante decimal una posible especificacioacuten es

TOKEN ltnombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN ltcteEntera ( [ 0 - 9 ] )+ gt

TOKEN ltcteDecimal ( [ 0 - 9 ] )+ ( [ 0 - 9 ] )+ gt

Con la inclusioacuten de tres piezas sintaacutecticas internas auxiliares esta misma especificacioacuten podriacutea escribirse de la siguiente manera

TOKEN lt letra [ a - z A - Z ntilde Ntilde ] gt

TOKEN lt cifra [ 0 - 9 ] gt

TOKEN lt numero ( ltcifragt )+ gt

TOKEN lt nombre ( ltletragt )+ gt

TOKEN lt cteEntera ltnumerogt gt

TOKEN lt cteDecimal ltnumerogt ltnumerogt gt

Los nombres letra cifra y numero son exclusivos (internos) del analizador lexicograacutefico y no se pueden emplear como nombres de piezas en la especificacioacuten de las reglas sintaacutecticas cuando esos nom-bres se usan en la definicioacuten de otras piezas han de escribirse delimitados por los siacutembolos lt y gt

bull Distincioacuten restringida entre minuacutesculas y mayuacutesculasSe tiene la opcioacuten Ignore_Case para determinar si en el anaacutelisis del texto de entrada se distingue entre las letras minuacutesculas y las mayuacutesculas el valor por defecto false indica que siacute se diferencian si se asig-na el valor true a esta opcioacuten entonces no se diferencian en ninguacuten caso con independencia de que en los patrones que se definan para las piezas sintaacutecticas se pongan minuacutesculas o mayuacutesculas

Pero a veces conviene que la diferenciacioacuten entre minuacutesculas y mayuacutesculas no se aplique de manera ge-neral a toda la entrada sino en particular a las secuencias de caracteres que se acoplen a ciertos patrones Para este fin existe la posibilidad de establecer que en la expresioacuten regular que define el patroacuten de una determinada pieza sintaacutectica no se diferencie entre minuacutesculas y mayuacutesculas ello se consigue poniendo

14

[ IGNORE_CASE ]

inmediatamente detraacutes de la palabra TOKEN con la que empieza la especificacioacuten de la pieza sintaacutectica en la que se pretende la no distincioacuten entre minuacutesculas y mayuacutesculas

Noacutetese que ignore_case tiene dos significados

- es el nombre de un paraacutemetro de la seccioacuten de opciones (puede ponerse en minuacutesculas o mayuacutesculas)- es una palabra reservada que puede aplicarse (escribieacutendola entre corchetes y en mayuacutesculas) a la de -finicioacuten de una pieza sintaacutectica

Por ejemplo sea un lenguaje que en general distingue entre minuacutesculas y mayuacutesculas pero en la codifi -cacioacuten del exponente de una constante real exponencial puede ponerse la letra e en minuacutescula o en ma-yuacutescula En la especificacioacuten JavaCC para ese lenguaje la opcioacuten Ignore_Case ha de tomar el valor false en la definicioacuten de una constante exponencial son posibles las siguientes formas equivalentes

1) sin hacer uso de IGNORE_CASE

TOKEN lt cifras ( [ 0 - 9 ] )+ gt

TOKEN lt signo [+ -] gt

TOKEN lt constante

ltcifrasgt ( ltcifrasgt) [e E] (ltsignogt) ltcifrasgt gt

2) aplicando IGNORE_CASE

TOKEN lt cifras ( [ 0 - 9 ] )+ gt

TOKEN lt signo [+ -] gt

TOKEN [IGNORE_CASE] lt constante

ltcifrasgt ( ltcifrasgt) e (ltsignogt) ltcifrasgt gt

bull Secuencias de caracteres sin efecto en la estructura sintaacutecticaEn los lenguajes de programacioacuten suele haber secuencias de caracteres que no forman parte de la estructu-ra sintaacutectica de un programa esas secuencias son detectadas por el analizador lexicograacutefico pero no son comunicadas al sintaacutectico no han de considerarse como piezas sino que han de saltarse En JavaCC se pueden definir estas secuencias mediante la palabra reservada SKIP la notacioacuten de una especificacioacuten con la palabra SKIP de una secuencia que ha de saltarse es la misma que la notacioacuten usada para las espe-cificaciones con la palabra TOKEN de las piezas que han de comunicarse

Los dos casos maacutes habituales de secuencias que no se comunican al analizador sintaacutectico son

a) los caracteres separadores que no tienen significado en el texto analizadoen los lenguajes de codificacioacuten en formato libre hay caracteres del texto fuente que han de ignorarse en la mayoriacutea de los casos estos caracteres son el espacio en blanco el tabulador el retorno de carro y el fin de liacutenea esta caracteriacutestica se especifica

SKIP |t | r |n

b) los comentarios

15

en el anaacutelisis sintaacutectico de un programa los comentarios han de ignorarse una manera de especificar esta circunstancia es poniendo en una definicioacuten SKIP la expresioacuten regular que denota la forma de los comentarios del lenguaje por ejemplo para los comentarios de una liacutenea de Java se pondriacutea

SKIP

lt (~ [n r]) gt

Tareas asociadas a la estructura sintaacutecticaJavaCC genera un analizador sintaacutectico descendente recursivo constituido por un conjunto de meacutetodos ca-da siacutembolo no terminal de la gramaacutetica sintaacutectica tiene asociado un meacutetodo de anaacutelisis (el meacutetodo tiene el mismo nombre que el siacutembolo correspondiente) Un meacutetodo de anaacutelisis ademaacutes de la realizacioacuten de la parte del anaacutelisis sintaacutectico que le corresponde puede llevar a cabo otras tareas complementarias que sirvan para efectuar un anaacutelisis maacutes complejo del texto de entrada

loz Bloque para un siacutemboloAl describir la forma de las reglas sintaacutecticas de un siacutembolo no terminal en una especificacioacuten JavaCC ya se ha visto que detraacutes de la definicioacuten del nombre del siacutembolo y antes de empezar con las diferentes alter-nativas que tiene asociadas se incluye un bloque de coacutedigo Java (en el ejemplo de presentacioacuten este bloque estaacute vaciacuteo en todos los siacutembolos no terminales de la gramaacutetica) Cada bloque situado en esa posicioacuten puede considerarse ligado al siacutembolo no terminal en cuya descripcioacuten estaacute incluido el coacutedigo Java del bloque se traslada literalmente al principio del meacutetodo generado correspondiente al siacutembolo no terminal y por lo tan-to es lo primero que se considera (se ejecuta) cuando en el transcurso del proceso de anaacutelisis se realiza una llamada al meacutetodo Asiacute pues cada siacutembolo no terminal siempre tiene asociado un uacutenico bloque de coacute-digo Java ndashbloque de presencia obligada aunque pueda estar vaciacuteo- en este bloque se pueden poner de-claraciones y sentencias

loz Acciones sintaacutecticasAdemaacutes del bloque asociado a cada uno de los siacutembolos no terminales de la gramaacutetica se pueden incluir otros bloques de coacutedigo Java intercalados en cualquiera de las partes derechas de las reglas sintaacutecticas Los bloques incluidos en las partes derechas se denominan ldquoacciones sintaacutecticasrdquo (parser actions) ya que son trozos de coacutedigo que pueden considerarse asociados a los puntos de la estructura sintaacutectica donde estaacuten co-locados Una accioacuten sintaacutectica se puede poner en cualquier posicioacuten al principio al final o en el medio de la parte derecha de una regla precedida o seguida de siacutembolos terminales o no terminales el coacutedigo Java de una accioacuten sintaacutectica se traslada literalmente al meacutetodo asociado al siacutembolo no terminal de la parte izquierda de la regla de manera que se ejecuta cada vez que el analizador transcurre por el punto de la estructura sintaacutectica donde se ha incorporado la accioacuten (esto es las acciones sintaacutecticas se incorporan al coacutedigo del analizador sintaacutectico) No obstante su nombre en los bloques de las acciones sintaacutecticas suele ponerse coacutedigo dedicado a la realizacioacuten de tareas semaacutenticas

Si en el bloque asociado a un siacutembolo no terminal se ponen declaraciones los nombres ahiacute definidos son accesibles en todas las acciones sintaacutecticas intercaladas en las partes derechas de las reglas de ese siacutembolo la justificacioacuten es sencilla el coacutedigo del bloque asociado al siacutembolo se incluye al principio de un meacutetodo y el coacutedigo de las acciones pertenece a ese mismo meacutetodo

∆ Ejemplo 1

Se considera de nuevo el ejemplo de presentacioacuten ahora se pretende completar el anaacutelisis leacutexico-sintaacutec-tico con la realizacioacuten de las siguientes tareas

- cada vez que se analiza una expresioacuten un teacutermino o un factor se graba el nombre del siacutembolo no terminal correspondiente y

16

- cuando se analiza un teacutermino en el que hay operadores multiplicativos se cuenta su nuacutemero y se graba el resultado (noacutetese que se cuentan los operadores de cada teacutermino pero no se acumula la cuenta de todos los operadores de la expresioacuten)

Para la realizacioacuten de estas tareas se antildeaden bloques de coacutedigo Java En la especificacioacuten que se describe a continuacioacuten puede apreciarse- para los siacutembolos no terminales expresion termino y factor se ha incluido en los blo-ques correspondientes la operacioacuten de grabacioacuten de su nombre que se ejecutaraacute cada vez que se efec-tuacutee una llamada al meacutetodo- en el bloque asociado a termino se tiene la declaracioacuten del contador de operadores (que seraacute una variable local del meacutetodo)- en la parte derecha de la regla que define la estructura de un teacutermino se han incluido dos acciones sintaacutecticas una para contar la cantidad de operadores (situada detraacutes de la pieza sintaacutectica del opera-dor) y otra para grabar la cantidad de operadores del teacutermino (situada al final de la parte derecha)- se ha definido el meacutetodo grabar para facilitar la escritura del coacutedigo Java

options Ignore_Case = true

PARSER_BEGIN (ExprMin) public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

private static void grabar(String nombre) Systemoutprintln ( -gt + nombre + n)

PARSER_END (ExprMin)

void unaExpresion()

expresion() ltEOFgt

void expresion() grabar(Expresion)

termino() ( + termino() )

void termino() int nAst = 0 grabar(Termino)

17

factor() ( nAst++ factor() ) if (nAst gt 0) Systemoutprintln (n Asteriscos + nAst + n)

void factor() grabar(Factor)

ltconstantegt | ltvariablegt | ( expresion() )

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP | t | n | r

Cuando se incluyen acciones sintaacutecticas la legibilidad del aspecto sintaacutectico de la especificacioacuten queda oscurecida para paliar ese inconveniente puede antildeadirse un comentario que muestre la regla sintaacutectica escueta sin las acciones por ejemplo podriacutea escribirse

void termino() int nAst = 0 grabar(Termino)

factor() ( factor() )

factor()

( nAst++ factor() )

if (nAst gt 0) Systemoutprintln (n Asteriscos + nAst + n)

loz Valor comunicado por un meacutetodoEn todo lo visto hasta ahora los meacutetodos de anaacutelisis sintaacutectico generados por JavaCC no devuelven valor alguno al terminar su tarea de anaacutelisis ya que en la especificacioacuten siempre se ha declarado el nombre de un meacutetodo precedido de void (son meacutetodos de tipo void) Pero en general un meacutetodo de anaacutelisis sintaacutec-tico puede ser de cualquier tipo (primitivo o no) por ello un meacutetodo puede devolver un valor de cualquier tipo (de cualquier clase) al meacutetodo desde el que se efectuoacute la llamada

Esta posibilidad de devolucioacuten de valores tiene una utilidad fundamental en la implementacioacuten de analiza-dores de lenguajes permite la comunicacioacuten de datos (atributos de las entidades analizadas) entre los dife -rentes meacutetodos que colaboran en el anaacutelisis del programa completo

En una especificacioacuten JavaCC la llamada a un meacutetodo ocurre cuando en la parte derecha de una regla estaacute el nombre del siacutembolo no terminal asociado al meacutetodo Cuando un meacutetodo devuelve un valor (tiene un tipo distinto de void) es habitual la recepcioacuten del valor devuelto en una variable en la propia especificacioacuten

18

se puede indicar el nombre de la variable a la que se asignaraacute el valor devuelto tras la llamada para ello se emplea una notacioacuten ideacutentica a la forma que tienen las sentencias de asignacioacuten en el lenguaje Java

dato = nombreSimb()

donde dato es el nombre de la variable a la que se asigna el valor devuelto por el meacutetodo llamado esa va -riable ha de estar oportunamente declarada del mismo tipo que dicho meacutetodo

∆ Ejemplo 2

En el mismo ejemplo de presentacioacuten se desea ademaacutes del anaacutelisis leacutexico-sintaacutectico de la entrada contar la cantidad de operadores que hay en la expresioacuten A continuacioacuten se expone una especificacioacuten JavaCC que es una posible solucioacuten soacutelo se muestran las reglas sintaacutecticas (el resto de la especificacioacuten es igual que en el ejemplo de presentacioacuten)

En la especificacioacuten que se propone puede apreciarse- los siacutembolos no terminales expresion termino y factor se han declarado de tipo int los meacutetodos correspondientes devuelven un valor de tipo entero- el valor devuelto por los meacutetodos sirve para propagar a traveacutes de la estructura sintaacutectica la cuenta de los operadores que hay en el texto analizado- por tratarse de meacutetodos que devuelven un valor es preciso incluir las correspondientes sentencias de devolucioacuten return

void unaExpresion() int nOper nOper = expresion() ltEOFgt Systemoutprintln (Cantidad de operadores = + nOper)

int expresion() int n i n = termino() ( + n++ i = termino() n = n + i )

return n

int termino() int n i n = factor() ( n++ i = factor() n = n + i )

return n

int factor() int n ltconstantegt return 0 | ltvariablegt return 0 | ( n = expresion() ) return n

La especificacioacuten de un factor tambieacuten podriacutea escribirse de esta otra manera

19

int factor() int n = 0

( ltconstantegt | ltvariablegt | ( n = expresion() ) )

return n

A continuacioacuten se expone un nuevo ejemplo para ilustrar la utilidad que proporciona el valor devuelto por un meacutetodo de anaacutelisis sintaacutectico para la propagacioacuten (ascendente) de atributos (propiedades de las entida-des analizadas)

∆ Ejemplo 3

Se pretende ampliar el analizador del ejemplo de presentacioacuten para que se emita un mensaje indicativo cuando la expresioacuten analizada sea una expresioacuten en la que soacutelo aparecen constantes en la solucioacuten que se muestra soacutelo se incluyen las reglas sintaacutecticas (en el resto de la especificacioacuten no hay modificacio-nes)

void unaExpresion() boolean esCte

expresion() ltEOFgt

esCte = expresion() ltEOFgt

if (esCte) Systemoutprintln(nExpresion constanten)

boolean expresion() boolean esCte esTambienCte

termino() ( + termino() )

esCte = termino() ( + esTambienCte = termino() esCte = esCte ampamp esTambienCte )

return esCte

boolean termino() boolean esCte esTambienCte

factor() ( factor() )

esCte = factor() ( esTambienCte = factor() esCte = esCte ampamp esTambienCte )

return esCte

20

boolean factor() boolean esCte

ltconstantegt return true | ltvariablegt return false | ( esCte = expresion() ) return esCte

bull Reconsideracioacuten sobre las producciones vaciacuteasAnteriormente se ha comentado que para representar una produccioacuten vaciacutea en una especificacioacuten JavaCC hay que hacer uso de los metasiacutembolos de opcionalidad para ilustrar esta idea se ha incluido como ejem-plo la especificacioacuten de una lista de paraacutemetros compuesta por cero o maacutes expresiones separadas entre siacute por el caraacutecter coma

void parametros() [ expresion() ( expresion() ) ]

Pero si se aprovecha la posibilidad de incorporar acciones sintaacutecticas se tiene una manera de especificar directamente producciones vaciacuteas para conseguirlo basta considerar una produccioacuten vaciacutea (no tiene nada en su parte derecha) acompantildeada de una accioacuten sintaacutectica cuyo bloque de coacutedigo esteacute vaciacuteo Seguacuten esta posibilidad la especificacioacuten anterior podriacutea tambieacuten escribirse asiacute

void parametros() expresion() ( expresion() ) |

En general la accioacuten sintaacutectica asociada a una produccioacuten vaciacutea no tiene por queacute ser un bloque vaciacuteo de coacutedigo por ejemplo si se quiere que el meacutetodo que analiza los paraacutemetros de una llamada devuelva la cantidad de paraacutemetros encontrados se podriacutea escribir la siguiente especificacioacuten

int parametros()

int cantidad

( expresion() cantidad = 1

( expresion() cantidad++ )

| cantidad = 0 )

return cantidad

y si no quisiera ponerse expliacutecitamente la alternativa de la produccioacuten vaciacutea podriacutea escribirse

int parametros() int cantidad = 0

21

[ expresion() cantidad++ ( expresion() cantidad++ ) ]

return cantidad

Comunicacioacuten entre los analizadores leacutexico y sintaacutecticoCada vez que el analizador sintaacutectico realiza una llamada al analizador lexicograacutefico recibe como resultado de la llamada una representacioacuten de la pieza sintaacutectica encontrada en el texto analizado En los analizadores generados por JavaCC la comunicacioacuten de la pieza se efectuacutea mediante un valor de clase Token Esta cla-se estaacute definida en el fichero Tokenjava que se obtiene en todo caso siempre con el mismo contenido cualquiera que sea la especificacioacuten proporcionada como entrada al generador

Entre los campos de la clase Token se encuentran los siguientes

public int kind

nuacutemero entero que sirve de representacioacuten interna de la pieza sintaacutectica estos nuacutemeros asociados a las piezas son asignados automaacuteticamente por JavaCC los valores considerados pueden consultar-se en el fichero generado de nombre Constantsjava

public String image

cadena que contiene de la secuencia de caracteres (lexema) que constituyen la pieza sintaacutectica co-municada

public int beginLine beginColumn endLine endColumn

posiciones ocupadas (nuacutemeros de liacutenea y columna en el fichero de entrada) por el comienzo y el fi-nal del lexema de la pieza sintaacutectica comunicada

La clase Token tiene otros campos y meacutetodos en el fichero Tokenjava se tienen comentarios des-criptivos sobre todos los campos y meacutetodos que forman parte de la clase todos los campos de la clase se declaran como public

Ya se ha comentado anteriormente que cuando un meacutetodo de anaacutelisis sintaacutectico (asociado a un siacutembolo no terminal) tiene tipo el valor devuelto tras una llamada se puede almacenar en una variable por si se desea utilizar en alguna accioacuten sintaacutectica para ello se escribe

valor = nombreSimbolo()

(siendo valor el nombre de la variable y nombreSimbolo el nombre del siacutembolo no terminal) De manera anaacuteloga el objeto de la clase Token representativo de la pieza sintaacutectica encontrada en la entrada por el analizador lexicograacutefico puede asignarse a una variable del mismo tipo para tener accesibles las ca -racteriacutesticas de la pieza por si se precisaran en alguna accioacuten sintaacutectica para ello en la parte de la especifi -cacioacuten sintaacutectica donde aparezca la pieza sintaacutectica se escribe

dato = lt nombrePieza gt

donde nombrePieza es el nombre de la pieza sintaacutectica y dato es el nombre de una variable de tipo Token en la que se anota el valor indicativo de las propiedades de la pieza sintaacutectica comunicada

Tambieacuten existe otra manera de acceder a las caracteriacutesticas de la pieza sintaacutectica que el analizador lexico -graacutefico comunica al analizador sintaacutectico en la clase del analizador sintaacutectico se tiene declarado el campo

[ static ] public Token token

22

cada vez que el analizador sintaacutectico recibe una pieza sintaacutectica comunicada por el analizador lexicograacutefico se deja anotada en el campo token y ese contenido no se altera hasta que sea proporcionada la siguiente pieza de la entrada

∆ Ejemplo 4

Se considera una ampliacioacuten del ejemplo de presentacioacuten ademaacutes del anaacutelisis de la entrada se pretende obtener una relacioacuten numerada de las variables y las constantes indicando el nuacutemero de liacutenea y el nuacuteme-ro de columna donde estaacuten situadas por ejemplo si en la entrada se tiene la expresioacuten (grabada en dos liacuteneas)

x (y + 11) + 22

en la salida se tendraacute

1- x linea 1 columna 1

2- y linea 1 columna 6

3- 11 linea 1 columna 10

4- 22 linea 2 columna 5

En la especificacioacuten que se propone como solucioacuten puede apreciarse- en la clase del analizador sintaacutectico se han incluido meacutetodos auxiliares en los que se usan los nom-bres image beginLine beginColumn que son campos de un objeto de clase Token

- en la especificacioacuten sintaacutectica de factor se emplea la variable pieza de tipo Token para reco-ger las caracteriacutesticas de las piezas sintaacutecticas detectadas- la variable numero hace de contador para numerar las constantes y variables

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin

static int numero = 0

private static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

private static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

private static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

23

PARSER_END (ExprMin)

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() Token pieza pieza = ltconstantegt numero++ grabarDatosPieza(numero pieza) | pieza = ltvariablegt numero++ grabarDatosPieza(numero pieza) | ( expresion() )

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP | n | t | r

Si se agrupan las dos acciones sintaacutecticas comunes la especificacioacuten de factor tambieacuten puede escri-birse de esta forma

void factor() Token pieza ( pieza = ltconstantegt | pieza = ltvariablegt

) numero++ grabarDatosPieza(numero pieza)

| ( expresion() )

24

Si se accede al campo token de la clase del analizador sintaacutectico se puede resolver este ejemplo pres-cindiendo de la variable pieza

void factor() ( ltconstantegt | ltvariablegt

) numero++ grabarDatosPieza(numero token)

| ( expresion() )

Tareas asociadas a las piezas sintaacutecticasYa se ha visto que es posible asociar bloques de coacutedigo Java a puntos de la estructura sintaacutectica para que se realicen ciertas operaciones cuando el analizador sintaacutectico pase por esa zona de la estructura Tambieacuten es posible asociar coacutedigo Java a las piezas sintaacutecticas para que el analizador lexicograacutefico efectuacutee determina-das tareas cuando detecte en la entrada analizada piezas que tengan asociado coacutedigo

loz Accioacuten ligada a una pieza sintaacutecticaSi se asocia un bloque de coacutedigo a una pieza sintaacutectica se consigue que cada vez que se detecta en la en-trada analizada la presencia de esa pieza se ejecute en ese preciso momento el coacutedigo del bloque asociado a la pieza Estas acciones se denominan ldquoacciones lexicograacuteficasrdquo (lexical actions) el coacutedigo del bloque se traslada literalmente al coacutedigo del analizador lexicograacutefico generado para que se ejecute oportunamente cuando se detecte la pieza ligada al bloque de coacutedigo La accioacuten lexicograacutefica se ejecuta inmediatamente antes que la pieza sea comunicada al analizador sintaacutectico

Se puede asociar una accioacuten lexicograacutefica a una pieza sintaacutectica declarada con TOKEN y tambieacuten a una se-cuencia de caracteres declarada con SKIP ademaacutes de eacutestas hay otras dos formas de declaraciones lexico-graacuteficas (no mencionadas en esta introduccioacuten) que tambieacuten pueden llevar una accioacuten asociada

En una especificacioacuten JavaCC la forma de una pieza sintaacutectica (o bien de una secuencia de caracteres) se define poniendo su descripcioacuten delimitada entre llaves detraacutes de la indicacioacuten TOKEN (o bien SKIP) si se quiere asociar una accioacuten lexicograacutefica a la pieza (o a la secuencia) hay que colocar el bloque de coacutedigo de la accioacuten a continuacioacuten de la descripcioacuten de la forma de la pieza (o de la secuencia) el bloque (delimi-tado por llaves) queda situado entre la descripcioacuten y la llave que cierra la especificacioacuten

La pieza sintaacutectica especial que representa el final del fichero (EOF) tambieacuten puede llevar asociada una ac-cioacuten lexicograacutefica para ello hay que poner

ltgt TOKEN lt EOF gt coacutedigo de la accioacuten asociada

para justificar la presencia de los caracteres ltgt delante de la palabra TOKEN hay que basarse en el con-cepto de ldquoestado lexicograacuteficordquo (cuya explicacioacuten queda fuera del alcance de esta introduccioacuten)

∆ Ejemplo 5

Se considera nuevamente el ejemplo de presentacioacuten como resultado del anaacutelisis de una expresioacuten se pretende obtener una relacioacuten de los pareacutentesis contenidos en la expresioacuten en el mismo orden en que se encuentran ademaacutes por cada salto de liacutenea del texto de entrada se grabaraacute en la salida el caraacutecter y

25

se pasaraacute a una nueva liacutenea tambieacuten se anotaraacute la frase Final del trabajo cuando se alcance el final del fichero de entrada Asiacute por ejemplo si la entrada analizada es

(a + b) (( x )) (y + 7)

los resultados obtenidos seraacuten

() (())()

Final del trabajo

En la especificacioacuten JavaCC que se propone como solucioacuten soacutelo se muestran las reglas sintaacutecticas y le -xicograacuteficas (en el resto no hay cambios) en esta solucioacuten puede apreciarse- las piezas sintaacutecticas correspondientes a los pareacutentesis se han definido de manera nominal para po-der asociarles una accioacuten lexicograacutefica- los caracteres incluidos en la pieza de tipo SKIP se han descompuesto en dos grupos ya que la ac-cioacuten lexicograacutefica incorporada soacutelo afecta a uno de ellos

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ltpAbrirgt expresion() ltpCerrargt

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

TOKEN

26

lt pAbrir ( gt Systemoutprint(()

TOKEN lt pCerrar ) gt Systemoutprint())

ltgtTOKEN lt EOF gt Systemoutprintln ( Final del trabajo )

SKIP n Systemoutprint( n)

SKIP | t | r

loz Bloque de declaraciones lexicograacuteficasCada definicioacuten de una pieza sintaacutectica puede llevar asociada una accioacuten lexicograacutefica que se ejecuta in-mediatamente despueacutes de detectar en la entrada un lexema que se ajusta a la definicioacuten de la pieza cada accioacuten lexicograacutefica es un bloque de coacutedigo Java que puede contener sus propias declaraciones Pero a ve-ces interesa tener declaraciones que sean compartidas por todas las acciones lexicograacuteficas con indepen-dencia de la pieza a la que esteacute asociada cada una de ellas

Para incluir declaraciones que puedan usarse desde el coacutedigo asociado a cualquiera de las piezas sintaacutecti -cas se dispone de una construccioacuten con el siguiente formato

TOKEN_MGR_DECLS

que consta de una palabra reservada seguida del siacutembolo dos puntos seguido de un bloque de coacutedigo Java (delimitado por llaves) esta declaracioacuten se puede colocar en cualquier parte de una especificacioacuten JavaCC si se incluye soacutelo puede ponerse una vez

Todas las declaraciones de variables o de meacutetodos puestas en el bloque de declaraciones lexicograacuteficas compartidas son accesibles desde el coacutedigo de cualquier accioacuten lexicograacutefica la justificacioacuten es sencilla el bloque de declaraciones se traslada al principio del coacutedigo del analizador lexicograacutefico generado y las ac-ciones lexicograacuteficas tambieacuten se trasladan a ese analizador

loz Declaraciones lexicograacuteficas predefinidasDe lo expuesto hasta ahora se aprecia que en una accioacuten lexicograacutefica asociada a una pieza sintaacutectica re-sultan accesibles

- las declaraciones de su propio bloque (si tiene declaraciones)- los componentes del bloque de declaraciones compartidas (si estaacute incluido en la especificacioacuten)

Ademaacutes de esto se tienen otras declaraciones pertenecientes a la clase TokenManager que constituye el analizador lexicograacutefico que tambieacuten son utilizables desde cualquiera de las acciones lexico-graacuteficas estas declaraciones pueden considerarse como ldquopredefinidasrdquo ya que son incorporadas automaacuteti-camente por el generador Entre estas declaraciones accesibles se encuentran las siguientes

[ static ] StringBuffer imagecontiene la cadena de caracteres representativa de la pieza sintaacutectica actual que se acaba de recono-cer en le entrada se trata de un campo de la clase y se declara como static cuando el analizador

27

se ha generado con la opcioacuten (por defecto) Static = true si se generase con la opcioacuten Sta-tic = false no quedariacutea declarada como static

Token matchedToken

variable de clase Token que representa la pieza sintaacutectica actual la que se acaba de encontrar en la entrada se trata de una variable local del meacutetodo al que se ha trasladado el coacutedigo de la accioacuten (y por ello accesible desde ese coacutedigo)

El campo image puede usarse tanto si la accioacuten lexicograacutefica estaacute asociada a una pieza declarada con TOKEN como si lo estaacute a una secuencia declarada con SKIP Sin embargo la variable matchedToken no puede usarse en una accioacuten asociada a un patroacuten especificado como SKIP el motivo es que lo declara-do como SKIP no constituye propiamente una pieza sintaacutectica ya que no se va a comunicar al analizador sintaacutectico

∆ Ejemplo 6

Se propone aquiacute una nueva solucioacuten para el ejemplo nordm 4 expuesto anteriormente

Esta nueva solucioacuten tal y como se expone aquiacute admite algunas mejoras ya que su pretensioacuten soacutelo es ser-vir de ilustracioacuten para algunos aspectos que se acaban de citar (maacutes adelante se propone otra solucioacuten al-ternativa)

En la especificacioacuten de la solucioacuten es posible apreciar esto- las acciones lexicograacuteficas pueden llevar sus propias declaraciones las variables nLineaVar nColumnaVar nLineaCte y nColumnaCte se declaran dentro de las acciones

- la variable matchedToken a la que se hace referencia en el coacutedigo de la accioacuten lexicograacutefica aso-ciada a una variable contiene la informacioacuten sobre la pieza sintaacutectica que se acaba de detectar una va-riable de la expresioacuten analizada anaacutelogamente con la referencia relativa a una constante- el nombre image que aparece en las acciones lexicograacuteficas se refiere al campo de la clase Expr-MinTokenManager (analizador lexicograacutefico) noacutetese que en efecto se comunica como un paraacuteme-tro de clase StringBuffer

- la variable numero se emplea para numerar las constantes y variables encontradas

- los nombres beginLine y beginColumn son campos (puacuteblicos) de la clase Token por ello se pueden aplicar a un objeto de esa clase

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

void unaExpresion()

28

expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n StringBuffer lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

TOKEN lt variable [a-z] gt

int nLineaVar nColumnaVar numero++ nLineaVar = matchedTokenbeginLine nColumnaVar = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaVar nColumnaVar)

TOKEN lt constante ( [0-9] ) + gt

int nLineaCte nColumnaCte

29

numero++ nLineaCte = matchedTokenbeginLine nColumnaCte = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaCte nColumnaCte)

SKIP | n | t | r

La especificacioacuten precedente se puede escribir de otra manera maacutes escueta como se indica a continua-cioacuten (soacutelo se muestran las modificaciones) sobre esta nueva especificacioacuten puede mencionarse- se han eliminado por innecesarias las variables declaradas dentro de las acciones lexicograacuteficas- el nombre image que aparece en el bloque de las declaraciones compartidas se refiere al campo de la clase Token noacutetese que en efecto se aplica a un objeto de esa clase y se comunica como un paraacute -metro de tipo String

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

TOKEN lt variable [a-z] gt

numero++ grabarDatosPieza(numero matchedToken)

TOKEN lt constante ( [0-9] ) + gt

numero++ grabarDatosPieza(numero matchedToken)

loz Accioacuten lexicograacutefica comuacutenSi hubiera que realizar la misma tarea tras la deteccioacuten de todas y cada una de las piezas sintaacutecticas una posibilidad incoacutemoda seriacutea repetir el mismo coacutedigo en todas las acciones lexicograacuteficas para evitar tanta reiteracioacuten se tiene la opcioacuten Common_Token_Action su valor por defecto es false pero si se le asigna el valor true es posible definir una ldquoaccioacuten lexicograacutefica comuacutenrdquo que se escribe una uacutenica vez y se aplica a todas las piezas sintaacutecticas

30

Cuando la opcioacuten Common_Token_Action se establece con el valor true es obligado incluir el blo-que de declaraciones lexicograacuteficas compartidas (TOKEN_MGR_DECLS) Ademaacutes en ese bloque se preci-sa la definicioacuten de un meacutetodo cuyo encabezamiento sea

[ static ] void CommonTokenAction(Token t)

el coacutedigo de este meacutetodo constituye la accioacuten lexicograacutefica comuacuten que se ejecutaraacute despueacutes de la deteccioacuten de cualquier pieza sintaacutectica el meacutetodo tiene un paraacutemetro de tipo Token mediante el que se puede co-municar la informacioacuten correspondiente a la pieza que se acaba de encontrar en la entrada analizada

Si una pieza sintaacutectica tiene asociada una accioacuten lexicograacutefica propia la accioacuten lexicograacutefica comuacuten (si es-taacute definida) se ejecutaraacute inmediatamente despueacutes de realizar la accioacuten propia

La accioacuten lexicograacutefica comuacuten se aplica a todas las piezas sintaacutecticas definidas como TOKEN bien sea no-minalmente (en las reglas lexicograacuteficas) o bien anoacutenimamente (incorporadas dentro de las reglas sintaacutecti-cas) sin embargo no se aplica cuando la secuencia acoplada se corresponde con una expresioacuten regular es-pecificada como SKIP (noacutetese que por el contrario una especificacioacuten SKIP siacute puede tener asociada una accioacuten lexicograacutefica propia)

El coacutedigo del meacutetodo CommonTokenAction se traslada como un componente maacutes del bloque de decla-raciones compartidas (TOKEN_MGR_DECLS) al principio de la clase del analizador lexicograacutefico

∆ Ejemplo 7

Se pretende ahora ampliar el ejemplo de presentacioacuten con la obtencioacuten de una relacioacuten numerada de los lexemas de las piezas sintaacutecticas encontradas en la entrada analizada en el caso de las variables y de las constantes antes del lexema se mostraraacute un indicativo (Var Const) por ejemplo si la expresioacuten de la entrada es

( a + 987 ) b

se grabaraacute la salida

( 1Var a 2+ 3Const 987 4) 5 6Var b 7 8

En la solucioacuten que se propone a continuacioacuten se muestran la definicioacuten de las opciones y las reglas sin-taacutecticas y lexicograacuteficas (en el resto no hay modificaciones) puede resaltarse lo siguiente- las llamadas al meacutetodo CommonTokenAction se incorporan automaacuteticamente no hay que incluir-las en la especificacioacuten como paraacutemetro de llamada a este meacutetodo siempre se emplea el objeto que re -presenta la pieza que se acaba de detectar- la accioacuten lexicograacutefica comuacuten realiza la actualizacioacuten del contador de piezas y la grabacioacuten del lexe-ma de la pieza detectada con su nuacutemero de orden- la accioacuten lexicograacutefica comuacuten tambieacuten se aplica a las piezas sintaacutecticas anoacutenimas (pareacutentesis y ope-radores) y a la pieza especial que representa el final del fichero (EOF)

- las piezas sintaacutecticas ltconstantegt y ltvariablegt tienen asociada una accioacuten lexicograacutefica propia que se ejecuta antes de la accioacuten lexicograacutefica comuacuten- los caracteres que se saltan (SKIP) no estaacuten afectados por la accioacuten lexicograacutefica comuacuten

options Ignore_Case = true Common_Token_Action = true

31

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarIndicativo(String indicativo) Systemoutprint(indicativo + )

static void grabarLexema (int num String lexema) Systemoutprint(lexema + + num + n)

static void CommonTokenAction(Token pieza) numero++ grabarLexema(numero piezaimage)

TOKEN lt variable [a-z] gt

grabarIndicativo(Var)

32

TOKEN lt constante ( [0-9] ) + gt

grabarIndicativo(Const)

SKIP | n | t | r

A modo de ejemplo de aplicacioacuten de la accioacuten lexicograacutefica comuacuten en lo que sigue se ofrece una solucioacuten para la grabacioacuten (en el fichero predefinido de salida) del texto analizado con las liacuteneas numeradas

∆ Ejemplo 8 [ Listado del texto analizado ]Se desea completar el ejemplo de presentacioacuten con la grabacioacuten de un listado con las liacuteneas numeradas del texto analizado (noacutetese que una expresioacuten puede escribirse ocupando varias liacuteneas) No se muestra la especificacioacuten sintaacutectica debido a que no hay ninguna modificacioacuten en ella Los aspectos resentildeables de la solucioacuten que se propone son- se usa la variable numLin como contador de liacuteneas

- en la variable linea se van yuxtaponiendo los lexemas de las piezas sintaacutecticas de una liacutenea esta yuxtaposicioacuten se realiza mediante la accioacuten lexicograacutefica comuacuten- las secuencias reconocidas con los patrones especificados como SKIP no implican la ejecucioacuten de la accioacuten lexicograacutefica comuacuten por ello su lexema (almacenado en la variable image) ha de yuxtaponer-se expliacutecitamente- hay que separar el patroacuten correspondiente al fin de liacutenea para detectar el momento en que ha de gra-barse la liacutenea completa cuyo final se ha alcanzado

options Ignore_Case = true Common_Token_Action = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(nnAnalisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

TOKEN_MGR_DECLS static int numLin = 0 static String linea =

33

static void CommonTokenAction(Token pieza) linea = linea + piezaimage

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP lt | t | r gt linea = linea + image

SKIP n numLin++ Systemoutprintln(numLin + + linea) linea =

A modo de recapitulacioacutenↄ Accioacuten lexicograacutefica Accioacuten sintaacutectica

bull accioacuten lexicograacutefica bloque de coacutedigo Java asociado a una pieza sintaacutectica nominal (TOKEN) o a una secuencia de caracteres (SKIP) se ejecuta cuando se detecta en la entrada la pieza o la secuencia corres-pondiente no se puede aplicar a piezas sintaacutecticas anoacutenimas (autodefinidas con comillas)bull accioacuten sintaacutectica bloque de coacutedigo Java asociado a un punto de la estructura sintaacutectica (el punto indica-do por el sitio de la produccioacuten donde se inserta el bloque) se ejecuta cuando el anaacutelisis de la entrada pa-sa por ese punto de la estructura

ↄ Valor de una pieza sintaacutectica comunicadaEl analizador lexicograacutefico comunica al analizador sintaacutectico una pieza a traveacutes de un valor de la clase Token (fichero Tokenjava) un objeto de la clase Token representa las caracteriacutesticas de la pieza comunicada En la clase Token entre otros se tienen los campos

String imagelexema de la pieza

int beginLine beginColumn endLine endColumnposicioacuten del lexema en el fichero analizado (indicada por los nuacutemeros de fila y de columna de su comienzo y de su terminacioacuten)

ↄ Valor asociado a un siacutembolo

34

En las producciones que especifican la sintaxis se pueden incluir asignaciones que sirven para dejar ano-tado el valor asociado a un siacutembolo

bull siacutembolo no terminal valor = nombreSimbolo()el meacutetodo de anaacutelisis asociado a nombreSimbolo se ha declarado de un cierto tipo (distinto del tipo void) valor ha de ser una variable del mismo tipo que el meacutetodo

bull siacutembolo terminal dato = lt nombrePieza gtel valor asignado es el valor de la clase Token representativo de la pieza sintaacutectica (nominal) dato ha de ser una variable de tipo Token

ↄ Miradas sobre la pieza sintaacutectica comunicadaLa pieza sintaacutectica comunicada puede mirarse desde cada uno de los dos analizadores

bull analizador sintaacutecticola pieza comunicada (desde el analizador lexicograacutefico) se tiene anotada en un campo de la clase del analizador sintaacutectico declarado como

(static) public Token token

este valor es accesible desde el coacutedigo de todos los meacutetodos de anaacutelisis sintaacutectico el contenido del campo variacutea cuando se recibe una nueva pieza sintaacutectica

bull analizador lexicograacuteficola pieza dispuesta para ser comunicada (al analizador sintaacutectico) se tiene anotada en una variable local declarada como

Token matchedToken

este valor es accesible para el coacutedigo de todas las acciones lexicograacuteficas

ↄ El lexema de la pieza comunicadaEl lexema de la pieza sintaacutectica comunicada se encuentra disponible en dos sitios distintos que compar-ten el mismo nombre pero que son de distinto tipo y se usan de distinta manera

bull String image

es un campo de la clase TOKEN puede consultarse en relacioacuten con un objeto de esa clase que esteacute disponible

bull StringBuffer image

es un campo de la clase del analizador lexicograacutefico ( ∙ ∙ ∙ ∙ TokenManagerjava) es accesible desde el coacutedigo de la clase y por lo tanto desde todas las acciones lexicograacuteficas (bloques de coacute-digo que estaacuten incorporados al analizador lexicograacutefico)

se trata de un valor disponible tanto para las piezas sintaacutecticas nominales (TOKEN) como para las secuencias que se saltan (SKIP)

ↄ Resumen relativo a la lexicografiacutea

TOKEN pieza sintaacutectica nominal∙ ∙ ∙ pieza sintaacutectica anoacutenimaSKIP secuencia de caracteres que no forman pieza

35

Precisiones sobre el analizador sintaacutectico generadoPuede decirse que en su funcionamiento maacutes sencillo cuando no se emplean las posibilidades de examen por adelantado (ldquolookaheadrdquo) JavaCC comprueba que las reglas sintaacutecticas cumplen la condicioacuten LL(1) y generan un analizador descendente-predictivo-recursivo Pero esta apreciacioacuten hay que matizarla ya que el analizador sintaacutectico generado no se ajusta en sentido estricto al modelo de analizador LL(1)

En el modelo de anaacutelisis LL(1) el orden del examen de las distintas alternativas de expansioacuten de un siacutembo-lo no terminal es indiferente ya que se conocen los siacutembolos directores de cada una de ellas (que resultan ser conjuntos disjuntos) en la implementacioacuten del subprograma de anaacutelisis asociado al siacutembolo no terminal ltNombreSimbgt cuyas producciones son

ltNombreSimbgt = α1

| α2

∙ ∙ ∙

| αn

se podriacutea preguntar en cualquier orden si la pieza actual pertenece al conjunto de siacutembolos directores de las distintas reglas Sin embargo en el analizador sintaacutectico generado por JavaCC la implementacioacuten del meacute-todo asociado al siacutembolo no terminal ltNombreSimbgt no sigue esa pauta

Si las anteriores reglas sintaacutecticas se transcriben en JavaCC de la forma

void nombreSimb () α1-jcc

| α2-jcc

∙ ∙ ∙

| αn-jcc

1

accioacutenlexicograacutefica

comuacuten

Commom -TokenAction2

accioacutenlexicograacutefica

propia

(accioacuten ligadaa una pieza)3

bloque dedeclaracioneslexicograacuteficas

TOKEN_MGR_DECLS4

declaracioneslexicograacuteficaspredefinidas

(disponibilidad

de uso)TOKENsisisisi∙ ∙ ∙sino--SKIPnosisisi

Las indicaciones si de las columnas 3 y 4 son consecuencia de las indicaciones de la columna 2

La accioacuten lexicograacutefica comuacuten se ejecuta despueacutes de la accioacuten lexicograacutefica propia

36

el meacutetodo de anaacutelisis generado procede de la siguiente manera

se van considerando las alternativas sucesivamente en el mismo orden en el que estaacuten especifica-das primero la de α1 (α1-jcc) despueacutes la de α2 (α2-jcc) etc

para cada alternativa seleccionada se comprueba si la pieza por adelantado coincide con alguno de los siacutembolos iniciales de la parte derecha y si hay coincidencia se prosigue el anaacutelisis con ella si no hay coincidencia se pasa a la alternativa siguiente

siguiendo con este orden si se llega al final de las alternativas sin haberse encontrado coincidencia alguna se produce un error sintaacutectico

En el analizador generado por JavaCC hay una peculiaridad que no se tiene en los analizadores LL(1) es-trictamente considerados la palabra vaciacutea siacute se considera como posible siacutembolo inicial de la parte derecha una regla (esto ocurre siempre que la parte derecha es anulable) De esta peculiaridad se deriva una conse-cuencia que ha de tenerse en cuenta al escribir la especificacioacuten dado que siempre es posible considerar la palabra vaciacutea como la siguiente pieza por adelantado si se llega a examinar la alternativa correspondiente a una produccioacuten anulable el analizador generado siempre seleccionaraacute esa alternativa con independencia de la siguiente pieza que esteacute presente en la entrada que queda por analizar

Asiacute pues si alguna de las producciones que definen un siacutembolo no terminal es anulable es importante el or-den en que se escriben las alternativas en una especificacioacuten sintaacutectica JavaCC el funcionamiento del ana-lizador generado siacute depende de la colocacioacuten elegida para las reglas

Para ilustrar estas peculiaridades del analizador sintaacutectico generado por JavaCC a continuacioacuten se expone un ejemplo

∆ Ejemplo 9

Se quiere obtener un analizador leacutexico-sintaacutectico de textos que se ajustan al siguiente formato una se-cuencia de uno o maacutes nombres seguida del siacutembolo igual seguido de un nuacutemero entero si la secuencia tiene maacutes de un nombre han de estar separados bien por una coma en todos los casos o bien por el siacutem-bolo dos puntos en todos los casos esto es son correctas las secuencias

uno dos tres = 123exclusivo = 0Abajo Arriba = 57

y son incorrectas las secuenciasuno dos tres = 123

exclusivo inclusive = 0

fuera dentro = 56

Una gramaacutetica que define las secuencias con este formato es

ltListagt = nombre ltOtrosNombresgt = numero

ltOtrosNombresgt = nombre nombre | nombre nombre | ε

se trata de una gramaacutetica que cumple la condicioacuten LL(1)- el siacutembolo director para la alternativa vaciacutea es el siacutembolo igual es evidente pues que las tres reglas de ltOtrosNombresgt tienen siacutembolos directores distintos

37

- la decisioacuten de abandonar la repeticioacuten del anaacutelisis de un nuevo nombre (en las dos primeras alterna-tivas de ltOtrosNombresgt) se puede tomar ante la presencia del siacutembolo igual

En la implementacioacuten de un analizador sintaacutectico LL(1) en sentido estricto la seleccioacuten de la alternativa vaciacutea para expandir el siacutembolo ltOtrosNombresgt se hariacutea tras comprobar expliacutecitamente que la pie-za sintaacutectica por adelantado es el siacutembolo igual

El funcionamiento del analizador generado por JavaCC depende del orden de colocacioacuten de las reglas A continuacioacuten se exponen varias posibilidades de especificacioacuten y se comenta el analizador generado en cada caso

bull Especificacioacuten primeraSe transcriben literalmente las reglas tal y como se han escrito antes en la notacioacuten BNF noacutetese que para poder codificar la parte derecha de la regla vaciacutea ha de incluirse una accioacuten sintaacutectica vaciacutea

PARSER_BEGIN (Igualdad)

public class Igualdad

public static void main (String[] argum) throws ParseException Igualdad analisis = new Igualdad(Systemin) analisissecuencia() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (Igualdad)

void secuencia() lista() ltEOFgt

void lista() lt nombre gt otrosNombres() = lt numero gt

void otrosNombres() lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt ) |

TOKEN lt nombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN lt numero ( [ 0 - 9 ] )+ gt

SKIP | r | n

38

El analizador generado funciona correctamente puesto que en el meacutetodo otrosNombres se selec-ciona la alternativa vaciacutea despueacutes de haber comprobado que la pieza por adelantado no es siacutembolo ini-cial de ninguna de las dos alternativas precedentes (no es una coma ni un siacutembolo dos puntos)

bull Especificacioacuten segundaSe considera la misma especificacioacuten anterior pero cambiando el orden de colocacioacuten de las reglas ahora la produccioacuten vaciacutea se coloca como primera alternativa (soacutelo se muestran las partes modifica-das)

void otrosNombres() | lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt )

Con la entrada de esta especificacioacuten el generador JavaCC produce un mensaje en el que se avisa de que las dos alternativas que estaacuten colocadas detraacutes de la alternativa vaciacutea nunca podraacuten seleccionarse aun asiacute se genera un analizador Pero es un analizador que funciona incorrectamente asiacute por ejemplo la entrada

mxa nyz = 57

se considera incorrecta despueacutes de visto el primer nombre se intenta aplicar la produccioacuten vaciacutea (se espera encontrar el siacutembolo igual) pero habriacutea que aplicar la regla cuyo siacutembolo director es la coma

bull Especificacioacuten terceraSe utilizan las posibilidades de la notacioacuten BNF-Ampliada para prescindir de la especificacioacuten expliacuteci-ta de la palabra vaciacutea se aprovecha que JavaCC admite el operador + para indicar repeticiones de una o maacutes veces la alternativa vaciacutea estaacute impliacutecitamente contemplada en la posibilidad de cero repeti-ciones que contempla el operador (soacutelo se muestran las partes modificadas en la especificacioacuten)

void otrosNombres() ( lt nombre gt )+ | ( lt nombre gt )

El meacutetodo otrosNombres() generado hace las comprobaciones en el siguiente orden

- se comprueba si la pieza por adelantado es una coma (siacutembolo inicial de la primera alternativa) y en este caso se selecciona la primera regla- si no es una coma se comprueba si es un siacutembolo dos puntos (siacutembolo inicial de la segunda alter-nativa) y en este caso se selecciona la segunda regla- si no es un siacutembolo dos puntos se considera la palabra vaciacutea como siacutembolo inicial (siempre posi-ble impliacutecitamente) para la segunda alternativa en este caso para que no haya error la pieza por adelantado ha de ser un siacutembolo igual

Se trata pues de un analizador que funciona correctamente

bull Especificacioacuten cuarta

39

Se pone la misma especificacioacuten anterior pero cambiando el orden de consideracioacuten de la regla vaciacutea ahora se incluye impliacutecitamente en la primera alternativa (soacutelo se muestran las partes modificadas)

void otrosNombres() ( lt nombre gt ) | ( lt nombre gt )+

Para esta especificacioacuten el generador JavaCC emite un aviso en el que se indica que la segunda alter -nativa nunca se seleccionaraacute no obstante se genera un analizador que no funciona como se supone que deberiacutea hacerlo por ejemplo el anaacutelisis de la entrada

nyz mxa = 1

produce un error despueacutes de tratado el primer nombre se intenta aplicar la produccioacuten vaciacutea (corres-pondiente a la repeticioacuten de cero veces contemplada en la primera regla) por lo que no se encuentra el esperado siacutembolo igual

40

Page 10: JavaCC

- el nombre del siacutembolo no terminal (parte izquierda de las reglas) se escribe como el encabeza-miento de un meacutetodo en Java sin argumentos y sin valor de devolucioacuten (meacutetodo de tipo void) se sigue como en Java la costumbre de escribir los nombres de los meacutetodos empezando con letra mi-nuacutescula

- el siacutembolo dos puntos representa la separacioacuten entre las partes izquierda y derechas de las reglas (en vez del siacutembolo = de la notacioacuten BNF)

- el siacutembolo barra vertical representa la separacioacuten entre distintas partes derechas con la misma parte izquierda (igual que en la notacioacuten BNF)

- despueacutes del siacutembolo dos puntos se pone un bloque de coacutedigo Java este coacutedigo se suele dedicar a la realizacioacuten de tareas semaacutenticas si se pretende hacer soacutelo un anaacutelisis sintaacutectico el bloque que-daraacute vaciacuteo (aunque siempre es obligada su presencia)

- el conjunto de las alternativas (partes derechas de las reglas) estaacute delimitado mediante llaves como si constituyese un bloque de coacutedigo Java

- αi-jcc representa la codificacioacuten de una alternativa (parte derecha) empleaacutendose una nota-cioacuten que se describe a continuacioacuten

bull Siacutembolos no terminales en las partes derechasEn la notacioacuten BNF se pone ltNombreSimbgt en la notacioacuten JavaCC se escribe

nombreSimb()

esto es se pone como si fuese una llamada en Java a un meacutetodo sin argumentos

bull Siacutembolos terminales en las partes derechasSe considera una clasificacioacuten en las piezas sintaacutecticas (siacutembolos terminales de una gramaacutetica sintaacutectica) distinguieacutendose entre piezas sintaacutecticas nominales y anoacutenimas

son nominales las piezas sintaacutecticas a las que se asocia un nombre en otra parte de la especificacioacuten (maacutes adelante se describe la manera de indicar esta asociacioacuten) ese nombre asociado a la pieza es el que se usa al escribir las partes derechas de las producciones En la notacioacuten JavaCC un siacutembolo terminal no-minal se escribe poniendo el nombre delimitado por los caracteres lt y gt

ltnombreTerminalgt

son anoacutenimas las piezas sintaacutecticas que no tienen nombre asociado no se precisa porque la pieza se re -presenta por su propia secuencia de caracteres (lexema) En la notacioacuten JavaCC un siacutembolo terminal anoacutenimo se escribe poniendo su lexema delimitado por comillas por ejemplo

= lt lt= END

bull Metasiacutembolos de opcionalidad y de repeticioacutenEn la notacioacuten BNF se emplean los corchetes para indicar opcionalidad en la notacioacuten JavaCC se utilizan tambieacuten esos mismos siacutembolos

[ α ] en ambas notaciones

En la notacioacuten BNF se emplean las llaves para indicar una repeticioacuten de cero o maacutes veces en la notacioacuten JavaCC se utilizan los pareacutentesis y un asterisco detraacutes del pareacutentesis de cerrar

α en notacioacuten BNF-Ampliada

( α ) en notacioacuten JavaCC

bull Otros metasiacutembolos de la notacioacuten JavaCC

9

La opcionalidad tambieacuten se puede indicar empleando el metasiacutembolo de interrogacioacuten

( α )

Para indicar repeticioacuten de una o maacutes veces se utiliza el metasiacutembolo operador aditivo suma

( α )+

bull Control del final de fichero de entradaPara controlar el final del fichero en el texto analizado ha de antildeadirse una regla sintaacutectica especial que tenga (seguacuten la notacioacuten BNF) la forma

ltProgramaCompletogt = ltProgramagt FF

donde

ltProgramaCompletogt es un nuevo siacutembolo (no terminal) auxiliar antildeadido

ltProgramagt es el siacutembolo inicial de la gramaacutetica sintaacutectica

FF es una representacioacuten del final del fichero

En JavaCC el final de fichero se escribe con la notacioacuten ltEOFgt siendo EOF una palabra reservada la regla sintaacutectica especial antildeadida a la especificacioacuten quedariacutea codificada asiacute

void programaCompleto() programa() ltEOFgt

bull Representacioacuten de la produccioacuten vaciacuteaLas producciones vaciacuteas (reglas sintaacutecticas cuya parte derecha es la palabra vaciacutea ε ) no se pueden repre-sentar expliacutecitamente en una especificacioacuten leacutexico-sintaacutectica escrita en JavaCC Para indicar la presencia de una produccioacuten vaciacutea han de aplicarse las posibilidades de la notacioacuten BNF-Ampliada seguacuten esta nota-cioacuten cuando se indica la opcionalidad de un componente se estaacute especificando impliacutecitamente la palabra vaciacutea (en el caso de que el componente no esteacute presente)

Asiacute pues si se quisiera escribir en JavaCC la construccioacuten sintaacutectica expresada por las reglas

ltComponentegt = α

| ε

deberiacutean de considerarse con la notacioacuten equivalente

ltComponentegt = [ α ]

y si hubiera varias alternativas no vaciacuteas

ltComponentegt = α | β

| ε

habriacutea que considerarlas como

ltComponentegt = [ α | β ]

o tambieacuten podriacutea verse como

ltComponentegt = α

10

| [ β ]

Como ejemplo sea la especificacioacuten sintaacutectica de una llamada a una funcioacuten que puede tener cero o maacutes paraacutemetros separados por comas aunque no haya paraacutemetros los pareacutentesis estaacuten presentes Las siguien-tes reglas definen esta estructura

ltLlamadagt = id ( ltParametrosgt )

ltParametrosgt = ltExpresiongt ltExpresiongt | ε

En JavaCC la definicioacuten del siacutembolo ltParametrosgt se puede escribir como sigue (noacutetese que aquiacute los pareacutentesis empleados para aplicar el operador son metasiacutembolos)

void parametros() [ expresion() ( expresion() ) ]

loz Seccioacuten de lexicografiacuteaEn esta seccioacuten se indica la lexicografiacutea del lenguaje para el que se va a generar el analizador la notacioacuten de JavaCC que representa la forma de cada una de las piezas sintaacutecticas es una variante de las bien conoci-das expresiones regulares

bull El nombre de las piezas sintaacutecticas nominalesPara asociar un nombre a una pieza sintaacutectica y para al mismo tiempo definir la forma de la pieza se em-plea la notacioacuten

TOKEN lt nombreTerminal Expresioacuten-Regular gt

- TOKEN es una palabra reservada detraacutes de ella se pone el separador dos puntos

- nombreTerminal representa el nombre que se quiere asociar a la pieza sintaacutectica

- Expresioacuten-Regular indica una expresioacuten regular que especifica la forma de la pieza

- el nombre y la expresioacuten se separan mediante el caraacutecter dos puntos y conjuntamente se deli -mitan mediante los caracteres lt y gt a su vez toda la asociacioacuten se delimita con llaves

bull Expresiones regulares en JavaCCLas expresiones regulares en una especificacioacuten JavaCC se escriben de manera parecida a la forma utiliza-da en otras conocidas notaciones aunque con ciertas peculiaridades A continuacioacuten se mencionan los as-pectos maacutes significativos de las expresiones regulares JavaCC y algunos ejemplos

Los operadores de las expresiones son

| unioacuten

repeticioacuten cero o maacutes veces

+ repeticioacuten una o maacutes veces

opcionalidad

11

Los pareacutentesis permiten fijar el orden de aplicacioacuten de los operadores Una peculiaridad de las expresiones regulares JavaCC es que los operadores de repeticioacuten y de opcionalidad requieren que el componente al que se aplican esteacute en todo caso delimitado por pareacutentesis

Se pueden citar las siguientes caracteriacutesticas baacutesicas de las expresiones regulares en JavaCC

Se tiene la posibilidad de definir conjuntos de caracteres poniendo entre corchetes la relacioacuten de carac-teres separados por comas y cada caraacutecter delimitado por comillas En un conjunto se puede fijar un ran-go de caracteres poniendo el primero y el uacuteltimo separados por el siacutembolo guioacuten (-) A veces resulta maacutes coacutemodo indicar los caracteres que no pertenecen al conjunto para ello se puede definir el comple -mentario de un conjunto poniendo el siacutembolo ~ delante del corchete inicial

Para indicar que una secuencia de caracteres colocados consecutivamente constituye por siacute misma una expresioacuten regular (o un componente de una expresioacuten) se escribe la secuencia delimitada por comillas El siacutembolo se emplea como metacaraacutecter para indicar que el siacutembolo que estaacute inmediatamente a su derecha se considere como caraacutecter del alfabeto no como posible metacaraacutecter (operador delimitador)

En una expresioacuten regular se pueden incluir como si fueran caracteres las secuencias de escape con la misma notacioacuten y el mismo significado que en Java o en C

Noacutetese que en una produccioacuten sintaacutectica la opcionalidad se puede especificar de dos formas con corchetes o con el siacutembolo de interrogacioacuten sin embargo en una expresioacuten regular la opcionalidad soacutelo se puede es-pecificar mediante el siacutembolo de interrogacioacuten (los corchetes en las expresiones regulares son para definir conjuntos de caracteres)

A continuacioacuten se muestran ejemplos de expresiones regulares escritas seguacuten la notacioacuten JavaCC

ENDsecuencia de las tres letras mayuacutesculas que conforman la palabra END

whilesecuencia de las cinco letras minuacutesculas que conforman la palabra while

ncaraacutecter representativo del fin de liacutenea (secuencia de escape)

rcaraacutecter representativo del retorno de carro (secuencia de escape)

caraacutecter dos puntos

=secuencia de dos caracteres (siacutembolo de asignacioacuten en Pascal)

gtcaraacutecter representativo del operador de relacioacuten mayor

(i | I)(f | F)palabra ldquoifrdquo escrita con letras minuacutesculas o mayuacutesculas (cuatro combinaciones)

if | IFpalabra ldquoifrdquo escrita soacutelo con minuacutesculas o soacutelo con mayuacutesculas (dos combinaciones)

12

u | o | i | e | auna cualquiera de las vocales minuacutesculas (pero soacutelo una de ellas)

[ A E I O U ]una cualquiera de las vocales mayuacutesculas (pero soacutelo una de ellas)

[ a - z ntilde ]una cualquiera de las letras minuacutesculas incluyendo la letra ntilde

[ a - z A - Z 0 - 9 ]una cualquiera de las letras o de las cifras decimales

([ 0 - 9 ])+constante entera (nuacutemero de una o maacutes cifras)

noacutetese que es incorrecto poner [ 0 - 9 ]+

( + | - )([ 0 - 9 ])+constante entera precedida opcionalmente por un signo

noacutetese que la primera cifra ha de seguir inmediatamente al signo (si existe)

([+ -])([ 0 - 9 ])+representa lo mismo que la expresioacuten anterior

[ a - z ] ([ a - z 0 - 9 ])una letra minuacutescula seguida de cero o maacutes letras minuacutesculas o cifras decimales

~[a - z ntilde A - Z Ntilde]cualquier caraacutecter que no sea una letra

noacutetese que la expresioacuten representa un uacutenico caraacutecter

~[n r]cualquier caraacutecter (un uacutenico caraacutecter) excepto el fin de liacutenea y el retorno de carro

~[ ]cualquier caraacutecter del alfabeto (un uacutenico caraacutecter)

([ 0 - 9 ])+ ( ([ 0 - 9 ])+)constante aritmeacutetica con parte decimal opcional

Los modernossecuencia de catorce caracteres Los modernos

noacutetese el uso de para imponer la condicioacuten de caraacutecter del siacutembolo comillas

nsecuencia de cuatro caracteres n

noacutetese que aquiacute no se representa la secuencia de escape

bull Piezas sintaacutecticas internas (ldquoprivaterdquo)

13

Es posible definir piezas sintaacutecticas auxiliares llamadas ldquointernasrdquo porque no se comunican al analizador sintaacutectico sino que soacutelo sirven para facilitar la escritura de la forma de las piezas sintaacutecticas que siacute se co-munican con el uso de las piezas internas es posible evitar repeticiones de escritura y hacer maacutes legible la especificacioacuten de las caracteriacutesticas lexicograacuteficas Las piezas internas permiten dar nombre a subexpre-siones regulares que a su vez pueden emplearse como componentes para escribir expresiones maacutes com-plejas que incorporan esos nombres Para indicar que una pieza sintaacutectica se considere como interna se pone el siacutembolo delante del nombre asociado a la pieza

A continuacioacuten se muestra un ejemplo tiacutepico de utilizacioacuten de las piezas internas en una especificacioacuten le-xicograacutefica en JavaCC Se quieren definir tres piezas sintaacutecticas que representan un nombre una constante entera y una constante decimal una posible especificacioacuten es

TOKEN ltnombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN ltcteEntera ( [ 0 - 9 ] )+ gt

TOKEN ltcteDecimal ( [ 0 - 9 ] )+ ( [ 0 - 9 ] )+ gt

Con la inclusioacuten de tres piezas sintaacutecticas internas auxiliares esta misma especificacioacuten podriacutea escribirse de la siguiente manera

TOKEN lt letra [ a - z A - Z ntilde Ntilde ] gt

TOKEN lt cifra [ 0 - 9 ] gt

TOKEN lt numero ( ltcifragt )+ gt

TOKEN lt nombre ( ltletragt )+ gt

TOKEN lt cteEntera ltnumerogt gt

TOKEN lt cteDecimal ltnumerogt ltnumerogt gt

Los nombres letra cifra y numero son exclusivos (internos) del analizador lexicograacutefico y no se pueden emplear como nombres de piezas en la especificacioacuten de las reglas sintaacutecticas cuando esos nom-bres se usan en la definicioacuten de otras piezas han de escribirse delimitados por los siacutembolos lt y gt

bull Distincioacuten restringida entre minuacutesculas y mayuacutesculasSe tiene la opcioacuten Ignore_Case para determinar si en el anaacutelisis del texto de entrada se distingue entre las letras minuacutesculas y las mayuacutesculas el valor por defecto false indica que siacute se diferencian si se asig-na el valor true a esta opcioacuten entonces no se diferencian en ninguacuten caso con independencia de que en los patrones que se definan para las piezas sintaacutecticas se pongan minuacutesculas o mayuacutesculas

Pero a veces conviene que la diferenciacioacuten entre minuacutesculas y mayuacutesculas no se aplique de manera ge-neral a toda la entrada sino en particular a las secuencias de caracteres que se acoplen a ciertos patrones Para este fin existe la posibilidad de establecer que en la expresioacuten regular que define el patroacuten de una determinada pieza sintaacutectica no se diferencie entre minuacutesculas y mayuacutesculas ello se consigue poniendo

14

[ IGNORE_CASE ]

inmediatamente detraacutes de la palabra TOKEN con la que empieza la especificacioacuten de la pieza sintaacutectica en la que se pretende la no distincioacuten entre minuacutesculas y mayuacutesculas

Noacutetese que ignore_case tiene dos significados

- es el nombre de un paraacutemetro de la seccioacuten de opciones (puede ponerse en minuacutesculas o mayuacutesculas)- es una palabra reservada que puede aplicarse (escribieacutendola entre corchetes y en mayuacutesculas) a la de -finicioacuten de una pieza sintaacutectica

Por ejemplo sea un lenguaje que en general distingue entre minuacutesculas y mayuacutesculas pero en la codifi -cacioacuten del exponente de una constante real exponencial puede ponerse la letra e en minuacutescula o en ma-yuacutescula En la especificacioacuten JavaCC para ese lenguaje la opcioacuten Ignore_Case ha de tomar el valor false en la definicioacuten de una constante exponencial son posibles las siguientes formas equivalentes

1) sin hacer uso de IGNORE_CASE

TOKEN lt cifras ( [ 0 - 9 ] )+ gt

TOKEN lt signo [+ -] gt

TOKEN lt constante

ltcifrasgt ( ltcifrasgt) [e E] (ltsignogt) ltcifrasgt gt

2) aplicando IGNORE_CASE

TOKEN lt cifras ( [ 0 - 9 ] )+ gt

TOKEN lt signo [+ -] gt

TOKEN [IGNORE_CASE] lt constante

ltcifrasgt ( ltcifrasgt) e (ltsignogt) ltcifrasgt gt

bull Secuencias de caracteres sin efecto en la estructura sintaacutecticaEn los lenguajes de programacioacuten suele haber secuencias de caracteres que no forman parte de la estructu-ra sintaacutectica de un programa esas secuencias son detectadas por el analizador lexicograacutefico pero no son comunicadas al sintaacutectico no han de considerarse como piezas sino que han de saltarse En JavaCC se pueden definir estas secuencias mediante la palabra reservada SKIP la notacioacuten de una especificacioacuten con la palabra SKIP de una secuencia que ha de saltarse es la misma que la notacioacuten usada para las espe-cificaciones con la palabra TOKEN de las piezas que han de comunicarse

Los dos casos maacutes habituales de secuencias que no se comunican al analizador sintaacutectico son

a) los caracteres separadores que no tienen significado en el texto analizadoen los lenguajes de codificacioacuten en formato libre hay caracteres del texto fuente que han de ignorarse en la mayoriacutea de los casos estos caracteres son el espacio en blanco el tabulador el retorno de carro y el fin de liacutenea esta caracteriacutestica se especifica

SKIP |t | r |n

b) los comentarios

15

en el anaacutelisis sintaacutectico de un programa los comentarios han de ignorarse una manera de especificar esta circunstancia es poniendo en una definicioacuten SKIP la expresioacuten regular que denota la forma de los comentarios del lenguaje por ejemplo para los comentarios de una liacutenea de Java se pondriacutea

SKIP

lt (~ [n r]) gt

Tareas asociadas a la estructura sintaacutecticaJavaCC genera un analizador sintaacutectico descendente recursivo constituido por un conjunto de meacutetodos ca-da siacutembolo no terminal de la gramaacutetica sintaacutectica tiene asociado un meacutetodo de anaacutelisis (el meacutetodo tiene el mismo nombre que el siacutembolo correspondiente) Un meacutetodo de anaacutelisis ademaacutes de la realizacioacuten de la parte del anaacutelisis sintaacutectico que le corresponde puede llevar a cabo otras tareas complementarias que sirvan para efectuar un anaacutelisis maacutes complejo del texto de entrada

loz Bloque para un siacutemboloAl describir la forma de las reglas sintaacutecticas de un siacutembolo no terminal en una especificacioacuten JavaCC ya se ha visto que detraacutes de la definicioacuten del nombre del siacutembolo y antes de empezar con las diferentes alter-nativas que tiene asociadas se incluye un bloque de coacutedigo Java (en el ejemplo de presentacioacuten este bloque estaacute vaciacuteo en todos los siacutembolos no terminales de la gramaacutetica) Cada bloque situado en esa posicioacuten puede considerarse ligado al siacutembolo no terminal en cuya descripcioacuten estaacute incluido el coacutedigo Java del bloque se traslada literalmente al principio del meacutetodo generado correspondiente al siacutembolo no terminal y por lo tan-to es lo primero que se considera (se ejecuta) cuando en el transcurso del proceso de anaacutelisis se realiza una llamada al meacutetodo Asiacute pues cada siacutembolo no terminal siempre tiene asociado un uacutenico bloque de coacute-digo Java ndashbloque de presencia obligada aunque pueda estar vaciacuteo- en este bloque se pueden poner de-claraciones y sentencias

loz Acciones sintaacutecticasAdemaacutes del bloque asociado a cada uno de los siacutembolos no terminales de la gramaacutetica se pueden incluir otros bloques de coacutedigo Java intercalados en cualquiera de las partes derechas de las reglas sintaacutecticas Los bloques incluidos en las partes derechas se denominan ldquoacciones sintaacutecticasrdquo (parser actions) ya que son trozos de coacutedigo que pueden considerarse asociados a los puntos de la estructura sintaacutectica donde estaacuten co-locados Una accioacuten sintaacutectica se puede poner en cualquier posicioacuten al principio al final o en el medio de la parte derecha de una regla precedida o seguida de siacutembolos terminales o no terminales el coacutedigo Java de una accioacuten sintaacutectica se traslada literalmente al meacutetodo asociado al siacutembolo no terminal de la parte izquierda de la regla de manera que se ejecuta cada vez que el analizador transcurre por el punto de la estructura sintaacutectica donde se ha incorporado la accioacuten (esto es las acciones sintaacutecticas se incorporan al coacutedigo del analizador sintaacutectico) No obstante su nombre en los bloques de las acciones sintaacutecticas suele ponerse coacutedigo dedicado a la realizacioacuten de tareas semaacutenticas

Si en el bloque asociado a un siacutembolo no terminal se ponen declaraciones los nombres ahiacute definidos son accesibles en todas las acciones sintaacutecticas intercaladas en las partes derechas de las reglas de ese siacutembolo la justificacioacuten es sencilla el coacutedigo del bloque asociado al siacutembolo se incluye al principio de un meacutetodo y el coacutedigo de las acciones pertenece a ese mismo meacutetodo

∆ Ejemplo 1

Se considera de nuevo el ejemplo de presentacioacuten ahora se pretende completar el anaacutelisis leacutexico-sintaacutec-tico con la realizacioacuten de las siguientes tareas

- cada vez que se analiza una expresioacuten un teacutermino o un factor se graba el nombre del siacutembolo no terminal correspondiente y

16

- cuando se analiza un teacutermino en el que hay operadores multiplicativos se cuenta su nuacutemero y se graba el resultado (noacutetese que se cuentan los operadores de cada teacutermino pero no se acumula la cuenta de todos los operadores de la expresioacuten)

Para la realizacioacuten de estas tareas se antildeaden bloques de coacutedigo Java En la especificacioacuten que se describe a continuacioacuten puede apreciarse- para los siacutembolos no terminales expresion termino y factor se ha incluido en los blo-ques correspondientes la operacioacuten de grabacioacuten de su nombre que se ejecutaraacute cada vez que se efec-tuacutee una llamada al meacutetodo- en el bloque asociado a termino se tiene la declaracioacuten del contador de operadores (que seraacute una variable local del meacutetodo)- en la parte derecha de la regla que define la estructura de un teacutermino se han incluido dos acciones sintaacutecticas una para contar la cantidad de operadores (situada detraacutes de la pieza sintaacutectica del opera-dor) y otra para grabar la cantidad de operadores del teacutermino (situada al final de la parte derecha)- se ha definido el meacutetodo grabar para facilitar la escritura del coacutedigo Java

options Ignore_Case = true

PARSER_BEGIN (ExprMin) public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

private static void grabar(String nombre) Systemoutprintln ( -gt + nombre + n)

PARSER_END (ExprMin)

void unaExpresion()

expresion() ltEOFgt

void expresion() grabar(Expresion)

termino() ( + termino() )

void termino() int nAst = 0 grabar(Termino)

17

factor() ( nAst++ factor() ) if (nAst gt 0) Systemoutprintln (n Asteriscos + nAst + n)

void factor() grabar(Factor)

ltconstantegt | ltvariablegt | ( expresion() )

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP | t | n | r

Cuando se incluyen acciones sintaacutecticas la legibilidad del aspecto sintaacutectico de la especificacioacuten queda oscurecida para paliar ese inconveniente puede antildeadirse un comentario que muestre la regla sintaacutectica escueta sin las acciones por ejemplo podriacutea escribirse

void termino() int nAst = 0 grabar(Termino)

factor() ( factor() )

factor()

( nAst++ factor() )

if (nAst gt 0) Systemoutprintln (n Asteriscos + nAst + n)

loz Valor comunicado por un meacutetodoEn todo lo visto hasta ahora los meacutetodos de anaacutelisis sintaacutectico generados por JavaCC no devuelven valor alguno al terminar su tarea de anaacutelisis ya que en la especificacioacuten siempre se ha declarado el nombre de un meacutetodo precedido de void (son meacutetodos de tipo void) Pero en general un meacutetodo de anaacutelisis sintaacutec-tico puede ser de cualquier tipo (primitivo o no) por ello un meacutetodo puede devolver un valor de cualquier tipo (de cualquier clase) al meacutetodo desde el que se efectuoacute la llamada

Esta posibilidad de devolucioacuten de valores tiene una utilidad fundamental en la implementacioacuten de analiza-dores de lenguajes permite la comunicacioacuten de datos (atributos de las entidades analizadas) entre los dife -rentes meacutetodos que colaboran en el anaacutelisis del programa completo

En una especificacioacuten JavaCC la llamada a un meacutetodo ocurre cuando en la parte derecha de una regla estaacute el nombre del siacutembolo no terminal asociado al meacutetodo Cuando un meacutetodo devuelve un valor (tiene un tipo distinto de void) es habitual la recepcioacuten del valor devuelto en una variable en la propia especificacioacuten

18

se puede indicar el nombre de la variable a la que se asignaraacute el valor devuelto tras la llamada para ello se emplea una notacioacuten ideacutentica a la forma que tienen las sentencias de asignacioacuten en el lenguaje Java

dato = nombreSimb()

donde dato es el nombre de la variable a la que se asigna el valor devuelto por el meacutetodo llamado esa va -riable ha de estar oportunamente declarada del mismo tipo que dicho meacutetodo

∆ Ejemplo 2

En el mismo ejemplo de presentacioacuten se desea ademaacutes del anaacutelisis leacutexico-sintaacutectico de la entrada contar la cantidad de operadores que hay en la expresioacuten A continuacioacuten se expone una especificacioacuten JavaCC que es una posible solucioacuten soacutelo se muestran las reglas sintaacutecticas (el resto de la especificacioacuten es igual que en el ejemplo de presentacioacuten)

En la especificacioacuten que se propone puede apreciarse- los siacutembolos no terminales expresion termino y factor se han declarado de tipo int los meacutetodos correspondientes devuelven un valor de tipo entero- el valor devuelto por los meacutetodos sirve para propagar a traveacutes de la estructura sintaacutectica la cuenta de los operadores que hay en el texto analizado- por tratarse de meacutetodos que devuelven un valor es preciso incluir las correspondientes sentencias de devolucioacuten return

void unaExpresion() int nOper nOper = expresion() ltEOFgt Systemoutprintln (Cantidad de operadores = + nOper)

int expresion() int n i n = termino() ( + n++ i = termino() n = n + i )

return n

int termino() int n i n = factor() ( n++ i = factor() n = n + i )

return n

int factor() int n ltconstantegt return 0 | ltvariablegt return 0 | ( n = expresion() ) return n

La especificacioacuten de un factor tambieacuten podriacutea escribirse de esta otra manera

19

int factor() int n = 0

( ltconstantegt | ltvariablegt | ( n = expresion() ) )

return n

A continuacioacuten se expone un nuevo ejemplo para ilustrar la utilidad que proporciona el valor devuelto por un meacutetodo de anaacutelisis sintaacutectico para la propagacioacuten (ascendente) de atributos (propiedades de las entida-des analizadas)

∆ Ejemplo 3

Se pretende ampliar el analizador del ejemplo de presentacioacuten para que se emita un mensaje indicativo cuando la expresioacuten analizada sea una expresioacuten en la que soacutelo aparecen constantes en la solucioacuten que se muestra soacutelo se incluyen las reglas sintaacutecticas (en el resto de la especificacioacuten no hay modificacio-nes)

void unaExpresion() boolean esCte

expresion() ltEOFgt

esCte = expresion() ltEOFgt

if (esCte) Systemoutprintln(nExpresion constanten)

boolean expresion() boolean esCte esTambienCte

termino() ( + termino() )

esCte = termino() ( + esTambienCte = termino() esCte = esCte ampamp esTambienCte )

return esCte

boolean termino() boolean esCte esTambienCte

factor() ( factor() )

esCte = factor() ( esTambienCte = factor() esCte = esCte ampamp esTambienCte )

return esCte

20

boolean factor() boolean esCte

ltconstantegt return true | ltvariablegt return false | ( esCte = expresion() ) return esCte

bull Reconsideracioacuten sobre las producciones vaciacuteasAnteriormente se ha comentado que para representar una produccioacuten vaciacutea en una especificacioacuten JavaCC hay que hacer uso de los metasiacutembolos de opcionalidad para ilustrar esta idea se ha incluido como ejem-plo la especificacioacuten de una lista de paraacutemetros compuesta por cero o maacutes expresiones separadas entre siacute por el caraacutecter coma

void parametros() [ expresion() ( expresion() ) ]

Pero si se aprovecha la posibilidad de incorporar acciones sintaacutecticas se tiene una manera de especificar directamente producciones vaciacuteas para conseguirlo basta considerar una produccioacuten vaciacutea (no tiene nada en su parte derecha) acompantildeada de una accioacuten sintaacutectica cuyo bloque de coacutedigo esteacute vaciacuteo Seguacuten esta posibilidad la especificacioacuten anterior podriacutea tambieacuten escribirse asiacute

void parametros() expresion() ( expresion() ) |

En general la accioacuten sintaacutectica asociada a una produccioacuten vaciacutea no tiene por queacute ser un bloque vaciacuteo de coacutedigo por ejemplo si se quiere que el meacutetodo que analiza los paraacutemetros de una llamada devuelva la cantidad de paraacutemetros encontrados se podriacutea escribir la siguiente especificacioacuten

int parametros()

int cantidad

( expresion() cantidad = 1

( expresion() cantidad++ )

| cantidad = 0 )

return cantidad

y si no quisiera ponerse expliacutecitamente la alternativa de la produccioacuten vaciacutea podriacutea escribirse

int parametros() int cantidad = 0

21

[ expresion() cantidad++ ( expresion() cantidad++ ) ]

return cantidad

Comunicacioacuten entre los analizadores leacutexico y sintaacutecticoCada vez que el analizador sintaacutectico realiza una llamada al analizador lexicograacutefico recibe como resultado de la llamada una representacioacuten de la pieza sintaacutectica encontrada en el texto analizado En los analizadores generados por JavaCC la comunicacioacuten de la pieza se efectuacutea mediante un valor de clase Token Esta cla-se estaacute definida en el fichero Tokenjava que se obtiene en todo caso siempre con el mismo contenido cualquiera que sea la especificacioacuten proporcionada como entrada al generador

Entre los campos de la clase Token se encuentran los siguientes

public int kind

nuacutemero entero que sirve de representacioacuten interna de la pieza sintaacutectica estos nuacutemeros asociados a las piezas son asignados automaacuteticamente por JavaCC los valores considerados pueden consultar-se en el fichero generado de nombre Constantsjava

public String image

cadena que contiene de la secuencia de caracteres (lexema) que constituyen la pieza sintaacutectica co-municada

public int beginLine beginColumn endLine endColumn

posiciones ocupadas (nuacutemeros de liacutenea y columna en el fichero de entrada) por el comienzo y el fi-nal del lexema de la pieza sintaacutectica comunicada

La clase Token tiene otros campos y meacutetodos en el fichero Tokenjava se tienen comentarios des-criptivos sobre todos los campos y meacutetodos que forman parte de la clase todos los campos de la clase se declaran como public

Ya se ha comentado anteriormente que cuando un meacutetodo de anaacutelisis sintaacutectico (asociado a un siacutembolo no terminal) tiene tipo el valor devuelto tras una llamada se puede almacenar en una variable por si se desea utilizar en alguna accioacuten sintaacutectica para ello se escribe

valor = nombreSimbolo()

(siendo valor el nombre de la variable y nombreSimbolo el nombre del siacutembolo no terminal) De manera anaacuteloga el objeto de la clase Token representativo de la pieza sintaacutectica encontrada en la entrada por el analizador lexicograacutefico puede asignarse a una variable del mismo tipo para tener accesibles las ca -racteriacutesticas de la pieza por si se precisaran en alguna accioacuten sintaacutectica para ello en la parte de la especifi -cacioacuten sintaacutectica donde aparezca la pieza sintaacutectica se escribe

dato = lt nombrePieza gt

donde nombrePieza es el nombre de la pieza sintaacutectica y dato es el nombre de una variable de tipo Token en la que se anota el valor indicativo de las propiedades de la pieza sintaacutectica comunicada

Tambieacuten existe otra manera de acceder a las caracteriacutesticas de la pieza sintaacutectica que el analizador lexico -graacutefico comunica al analizador sintaacutectico en la clase del analizador sintaacutectico se tiene declarado el campo

[ static ] public Token token

22

cada vez que el analizador sintaacutectico recibe una pieza sintaacutectica comunicada por el analizador lexicograacutefico se deja anotada en el campo token y ese contenido no se altera hasta que sea proporcionada la siguiente pieza de la entrada

∆ Ejemplo 4

Se considera una ampliacioacuten del ejemplo de presentacioacuten ademaacutes del anaacutelisis de la entrada se pretende obtener una relacioacuten numerada de las variables y las constantes indicando el nuacutemero de liacutenea y el nuacuteme-ro de columna donde estaacuten situadas por ejemplo si en la entrada se tiene la expresioacuten (grabada en dos liacuteneas)

x (y + 11) + 22

en la salida se tendraacute

1- x linea 1 columna 1

2- y linea 1 columna 6

3- 11 linea 1 columna 10

4- 22 linea 2 columna 5

En la especificacioacuten que se propone como solucioacuten puede apreciarse- en la clase del analizador sintaacutectico se han incluido meacutetodos auxiliares en los que se usan los nom-bres image beginLine beginColumn que son campos de un objeto de clase Token

- en la especificacioacuten sintaacutectica de factor se emplea la variable pieza de tipo Token para reco-ger las caracteriacutesticas de las piezas sintaacutecticas detectadas- la variable numero hace de contador para numerar las constantes y variables

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin

static int numero = 0

private static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

private static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

private static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

23

PARSER_END (ExprMin)

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() Token pieza pieza = ltconstantegt numero++ grabarDatosPieza(numero pieza) | pieza = ltvariablegt numero++ grabarDatosPieza(numero pieza) | ( expresion() )

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP | n | t | r

Si se agrupan las dos acciones sintaacutecticas comunes la especificacioacuten de factor tambieacuten puede escri-birse de esta forma

void factor() Token pieza ( pieza = ltconstantegt | pieza = ltvariablegt

) numero++ grabarDatosPieza(numero pieza)

| ( expresion() )

24

Si se accede al campo token de la clase del analizador sintaacutectico se puede resolver este ejemplo pres-cindiendo de la variable pieza

void factor() ( ltconstantegt | ltvariablegt

) numero++ grabarDatosPieza(numero token)

| ( expresion() )

Tareas asociadas a las piezas sintaacutecticasYa se ha visto que es posible asociar bloques de coacutedigo Java a puntos de la estructura sintaacutectica para que se realicen ciertas operaciones cuando el analizador sintaacutectico pase por esa zona de la estructura Tambieacuten es posible asociar coacutedigo Java a las piezas sintaacutecticas para que el analizador lexicograacutefico efectuacutee determina-das tareas cuando detecte en la entrada analizada piezas que tengan asociado coacutedigo

loz Accioacuten ligada a una pieza sintaacutecticaSi se asocia un bloque de coacutedigo a una pieza sintaacutectica se consigue que cada vez que se detecta en la en-trada analizada la presencia de esa pieza se ejecute en ese preciso momento el coacutedigo del bloque asociado a la pieza Estas acciones se denominan ldquoacciones lexicograacuteficasrdquo (lexical actions) el coacutedigo del bloque se traslada literalmente al coacutedigo del analizador lexicograacutefico generado para que se ejecute oportunamente cuando se detecte la pieza ligada al bloque de coacutedigo La accioacuten lexicograacutefica se ejecuta inmediatamente antes que la pieza sea comunicada al analizador sintaacutectico

Se puede asociar una accioacuten lexicograacutefica a una pieza sintaacutectica declarada con TOKEN y tambieacuten a una se-cuencia de caracteres declarada con SKIP ademaacutes de eacutestas hay otras dos formas de declaraciones lexico-graacuteficas (no mencionadas en esta introduccioacuten) que tambieacuten pueden llevar una accioacuten asociada

En una especificacioacuten JavaCC la forma de una pieza sintaacutectica (o bien de una secuencia de caracteres) se define poniendo su descripcioacuten delimitada entre llaves detraacutes de la indicacioacuten TOKEN (o bien SKIP) si se quiere asociar una accioacuten lexicograacutefica a la pieza (o a la secuencia) hay que colocar el bloque de coacutedigo de la accioacuten a continuacioacuten de la descripcioacuten de la forma de la pieza (o de la secuencia) el bloque (delimi-tado por llaves) queda situado entre la descripcioacuten y la llave que cierra la especificacioacuten

La pieza sintaacutectica especial que representa el final del fichero (EOF) tambieacuten puede llevar asociada una ac-cioacuten lexicograacutefica para ello hay que poner

ltgt TOKEN lt EOF gt coacutedigo de la accioacuten asociada

para justificar la presencia de los caracteres ltgt delante de la palabra TOKEN hay que basarse en el con-cepto de ldquoestado lexicograacuteficordquo (cuya explicacioacuten queda fuera del alcance de esta introduccioacuten)

∆ Ejemplo 5

Se considera nuevamente el ejemplo de presentacioacuten como resultado del anaacutelisis de una expresioacuten se pretende obtener una relacioacuten de los pareacutentesis contenidos en la expresioacuten en el mismo orden en que se encuentran ademaacutes por cada salto de liacutenea del texto de entrada se grabaraacute en la salida el caraacutecter y

25

se pasaraacute a una nueva liacutenea tambieacuten se anotaraacute la frase Final del trabajo cuando se alcance el final del fichero de entrada Asiacute por ejemplo si la entrada analizada es

(a + b) (( x )) (y + 7)

los resultados obtenidos seraacuten

() (())()

Final del trabajo

En la especificacioacuten JavaCC que se propone como solucioacuten soacutelo se muestran las reglas sintaacutecticas y le -xicograacuteficas (en el resto no hay cambios) en esta solucioacuten puede apreciarse- las piezas sintaacutecticas correspondientes a los pareacutentesis se han definido de manera nominal para po-der asociarles una accioacuten lexicograacutefica- los caracteres incluidos en la pieza de tipo SKIP se han descompuesto en dos grupos ya que la ac-cioacuten lexicograacutefica incorporada soacutelo afecta a uno de ellos

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ltpAbrirgt expresion() ltpCerrargt

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

TOKEN

26

lt pAbrir ( gt Systemoutprint(()

TOKEN lt pCerrar ) gt Systemoutprint())

ltgtTOKEN lt EOF gt Systemoutprintln ( Final del trabajo )

SKIP n Systemoutprint( n)

SKIP | t | r

loz Bloque de declaraciones lexicograacuteficasCada definicioacuten de una pieza sintaacutectica puede llevar asociada una accioacuten lexicograacutefica que se ejecuta in-mediatamente despueacutes de detectar en la entrada un lexema que se ajusta a la definicioacuten de la pieza cada accioacuten lexicograacutefica es un bloque de coacutedigo Java que puede contener sus propias declaraciones Pero a ve-ces interesa tener declaraciones que sean compartidas por todas las acciones lexicograacuteficas con indepen-dencia de la pieza a la que esteacute asociada cada una de ellas

Para incluir declaraciones que puedan usarse desde el coacutedigo asociado a cualquiera de las piezas sintaacutecti -cas se dispone de una construccioacuten con el siguiente formato

TOKEN_MGR_DECLS

que consta de una palabra reservada seguida del siacutembolo dos puntos seguido de un bloque de coacutedigo Java (delimitado por llaves) esta declaracioacuten se puede colocar en cualquier parte de una especificacioacuten JavaCC si se incluye soacutelo puede ponerse una vez

Todas las declaraciones de variables o de meacutetodos puestas en el bloque de declaraciones lexicograacuteficas compartidas son accesibles desde el coacutedigo de cualquier accioacuten lexicograacutefica la justificacioacuten es sencilla el bloque de declaraciones se traslada al principio del coacutedigo del analizador lexicograacutefico generado y las ac-ciones lexicograacuteficas tambieacuten se trasladan a ese analizador

loz Declaraciones lexicograacuteficas predefinidasDe lo expuesto hasta ahora se aprecia que en una accioacuten lexicograacutefica asociada a una pieza sintaacutectica re-sultan accesibles

- las declaraciones de su propio bloque (si tiene declaraciones)- los componentes del bloque de declaraciones compartidas (si estaacute incluido en la especificacioacuten)

Ademaacutes de esto se tienen otras declaraciones pertenecientes a la clase TokenManager que constituye el analizador lexicograacutefico que tambieacuten son utilizables desde cualquiera de las acciones lexico-graacuteficas estas declaraciones pueden considerarse como ldquopredefinidasrdquo ya que son incorporadas automaacuteti-camente por el generador Entre estas declaraciones accesibles se encuentran las siguientes

[ static ] StringBuffer imagecontiene la cadena de caracteres representativa de la pieza sintaacutectica actual que se acaba de recono-cer en le entrada se trata de un campo de la clase y se declara como static cuando el analizador

27

se ha generado con la opcioacuten (por defecto) Static = true si se generase con la opcioacuten Sta-tic = false no quedariacutea declarada como static

Token matchedToken

variable de clase Token que representa la pieza sintaacutectica actual la que se acaba de encontrar en la entrada se trata de una variable local del meacutetodo al que se ha trasladado el coacutedigo de la accioacuten (y por ello accesible desde ese coacutedigo)

El campo image puede usarse tanto si la accioacuten lexicograacutefica estaacute asociada a una pieza declarada con TOKEN como si lo estaacute a una secuencia declarada con SKIP Sin embargo la variable matchedToken no puede usarse en una accioacuten asociada a un patroacuten especificado como SKIP el motivo es que lo declara-do como SKIP no constituye propiamente una pieza sintaacutectica ya que no se va a comunicar al analizador sintaacutectico

∆ Ejemplo 6

Se propone aquiacute una nueva solucioacuten para el ejemplo nordm 4 expuesto anteriormente

Esta nueva solucioacuten tal y como se expone aquiacute admite algunas mejoras ya que su pretensioacuten soacutelo es ser-vir de ilustracioacuten para algunos aspectos que se acaban de citar (maacutes adelante se propone otra solucioacuten al-ternativa)

En la especificacioacuten de la solucioacuten es posible apreciar esto- las acciones lexicograacuteficas pueden llevar sus propias declaraciones las variables nLineaVar nColumnaVar nLineaCte y nColumnaCte se declaran dentro de las acciones

- la variable matchedToken a la que se hace referencia en el coacutedigo de la accioacuten lexicograacutefica aso-ciada a una variable contiene la informacioacuten sobre la pieza sintaacutectica que se acaba de detectar una va-riable de la expresioacuten analizada anaacutelogamente con la referencia relativa a una constante- el nombre image que aparece en las acciones lexicograacuteficas se refiere al campo de la clase Expr-MinTokenManager (analizador lexicograacutefico) noacutetese que en efecto se comunica como un paraacuteme-tro de clase StringBuffer

- la variable numero se emplea para numerar las constantes y variables encontradas

- los nombres beginLine y beginColumn son campos (puacuteblicos) de la clase Token por ello se pueden aplicar a un objeto de esa clase

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

void unaExpresion()

28

expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n StringBuffer lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

TOKEN lt variable [a-z] gt

int nLineaVar nColumnaVar numero++ nLineaVar = matchedTokenbeginLine nColumnaVar = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaVar nColumnaVar)

TOKEN lt constante ( [0-9] ) + gt

int nLineaCte nColumnaCte

29

numero++ nLineaCte = matchedTokenbeginLine nColumnaCte = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaCte nColumnaCte)

SKIP | n | t | r

La especificacioacuten precedente se puede escribir de otra manera maacutes escueta como se indica a continua-cioacuten (soacutelo se muestran las modificaciones) sobre esta nueva especificacioacuten puede mencionarse- se han eliminado por innecesarias las variables declaradas dentro de las acciones lexicograacuteficas- el nombre image que aparece en el bloque de las declaraciones compartidas se refiere al campo de la clase Token noacutetese que en efecto se aplica a un objeto de esa clase y se comunica como un paraacute -metro de tipo String

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

TOKEN lt variable [a-z] gt

numero++ grabarDatosPieza(numero matchedToken)

TOKEN lt constante ( [0-9] ) + gt

numero++ grabarDatosPieza(numero matchedToken)

loz Accioacuten lexicograacutefica comuacutenSi hubiera que realizar la misma tarea tras la deteccioacuten de todas y cada una de las piezas sintaacutecticas una posibilidad incoacutemoda seriacutea repetir el mismo coacutedigo en todas las acciones lexicograacuteficas para evitar tanta reiteracioacuten se tiene la opcioacuten Common_Token_Action su valor por defecto es false pero si se le asigna el valor true es posible definir una ldquoaccioacuten lexicograacutefica comuacutenrdquo que se escribe una uacutenica vez y se aplica a todas las piezas sintaacutecticas

30

Cuando la opcioacuten Common_Token_Action se establece con el valor true es obligado incluir el blo-que de declaraciones lexicograacuteficas compartidas (TOKEN_MGR_DECLS) Ademaacutes en ese bloque se preci-sa la definicioacuten de un meacutetodo cuyo encabezamiento sea

[ static ] void CommonTokenAction(Token t)

el coacutedigo de este meacutetodo constituye la accioacuten lexicograacutefica comuacuten que se ejecutaraacute despueacutes de la deteccioacuten de cualquier pieza sintaacutectica el meacutetodo tiene un paraacutemetro de tipo Token mediante el que se puede co-municar la informacioacuten correspondiente a la pieza que se acaba de encontrar en la entrada analizada

Si una pieza sintaacutectica tiene asociada una accioacuten lexicograacutefica propia la accioacuten lexicograacutefica comuacuten (si es-taacute definida) se ejecutaraacute inmediatamente despueacutes de realizar la accioacuten propia

La accioacuten lexicograacutefica comuacuten se aplica a todas las piezas sintaacutecticas definidas como TOKEN bien sea no-minalmente (en las reglas lexicograacuteficas) o bien anoacutenimamente (incorporadas dentro de las reglas sintaacutecti-cas) sin embargo no se aplica cuando la secuencia acoplada se corresponde con una expresioacuten regular es-pecificada como SKIP (noacutetese que por el contrario una especificacioacuten SKIP siacute puede tener asociada una accioacuten lexicograacutefica propia)

El coacutedigo del meacutetodo CommonTokenAction se traslada como un componente maacutes del bloque de decla-raciones compartidas (TOKEN_MGR_DECLS) al principio de la clase del analizador lexicograacutefico

∆ Ejemplo 7

Se pretende ahora ampliar el ejemplo de presentacioacuten con la obtencioacuten de una relacioacuten numerada de los lexemas de las piezas sintaacutecticas encontradas en la entrada analizada en el caso de las variables y de las constantes antes del lexema se mostraraacute un indicativo (Var Const) por ejemplo si la expresioacuten de la entrada es

( a + 987 ) b

se grabaraacute la salida

( 1Var a 2+ 3Const 987 4) 5 6Var b 7 8

En la solucioacuten que se propone a continuacioacuten se muestran la definicioacuten de las opciones y las reglas sin-taacutecticas y lexicograacuteficas (en el resto no hay modificaciones) puede resaltarse lo siguiente- las llamadas al meacutetodo CommonTokenAction se incorporan automaacuteticamente no hay que incluir-las en la especificacioacuten como paraacutemetro de llamada a este meacutetodo siempre se emplea el objeto que re -presenta la pieza que se acaba de detectar- la accioacuten lexicograacutefica comuacuten realiza la actualizacioacuten del contador de piezas y la grabacioacuten del lexe-ma de la pieza detectada con su nuacutemero de orden- la accioacuten lexicograacutefica comuacuten tambieacuten se aplica a las piezas sintaacutecticas anoacutenimas (pareacutentesis y ope-radores) y a la pieza especial que representa el final del fichero (EOF)

- las piezas sintaacutecticas ltconstantegt y ltvariablegt tienen asociada una accioacuten lexicograacutefica propia que se ejecuta antes de la accioacuten lexicograacutefica comuacuten- los caracteres que se saltan (SKIP) no estaacuten afectados por la accioacuten lexicograacutefica comuacuten

options Ignore_Case = true Common_Token_Action = true

31

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarIndicativo(String indicativo) Systemoutprint(indicativo + )

static void grabarLexema (int num String lexema) Systemoutprint(lexema + + num + n)

static void CommonTokenAction(Token pieza) numero++ grabarLexema(numero piezaimage)

TOKEN lt variable [a-z] gt

grabarIndicativo(Var)

32

TOKEN lt constante ( [0-9] ) + gt

grabarIndicativo(Const)

SKIP | n | t | r

A modo de ejemplo de aplicacioacuten de la accioacuten lexicograacutefica comuacuten en lo que sigue se ofrece una solucioacuten para la grabacioacuten (en el fichero predefinido de salida) del texto analizado con las liacuteneas numeradas

∆ Ejemplo 8 [ Listado del texto analizado ]Se desea completar el ejemplo de presentacioacuten con la grabacioacuten de un listado con las liacuteneas numeradas del texto analizado (noacutetese que una expresioacuten puede escribirse ocupando varias liacuteneas) No se muestra la especificacioacuten sintaacutectica debido a que no hay ninguna modificacioacuten en ella Los aspectos resentildeables de la solucioacuten que se propone son- se usa la variable numLin como contador de liacuteneas

- en la variable linea se van yuxtaponiendo los lexemas de las piezas sintaacutecticas de una liacutenea esta yuxtaposicioacuten se realiza mediante la accioacuten lexicograacutefica comuacuten- las secuencias reconocidas con los patrones especificados como SKIP no implican la ejecucioacuten de la accioacuten lexicograacutefica comuacuten por ello su lexema (almacenado en la variable image) ha de yuxtaponer-se expliacutecitamente- hay que separar el patroacuten correspondiente al fin de liacutenea para detectar el momento en que ha de gra-barse la liacutenea completa cuyo final se ha alcanzado

options Ignore_Case = true Common_Token_Action = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(nnAnalisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

TOKEN_MGR_DECLS static int numLin = 0 static String linea =

33

static void CommonTokenAction(Token pieza) linea = linea + piezaimage

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP lt | t | r gt linea = linea + image

SKIP n numLin++ Systemoutprintln(numLin + + linea) linea =

A modo de recapitulacioacutenↄ Accioacuten lexicograacutefica Accioacuten sintaacutectica

bull accioacuten lexicograacutefica bloque de coacutedigo Java asociado a una pieza sintaacutectica nominal (TOKEN) o a una secuencia de caracteres (SKIP) se ejecuta cuando se detecta en la entrada la pieza o la secuencia corres-pondiente no se puede aplicar a piezas sintaacutecticas anoacutenimas (autodefinidas con comillas)bull accioacuten sintaacutectica bloque de coacutedigo Java asociado a un punto de la estructura sintaacutectica (el punto indica-do por el sitio de la produccioacuten donde se inserta el bloque) se ejecuta cuando el anaacutelisis de la entrada pa-sa por ese punto de la estructura

ↄ Valor de una pieza sintaacutectica comunicadaEl analizador lexicograacutefico comunica al analizador sintaacutectico una pieza a traveacutes de un valor de la clase Token (fichero Tokenjava) un objeto de la clase Token representa las caracteriacutesticas de la pieza comunicada En la clase Token entre otros se tienen los campos

String imagelexema de la pieza

int beginLine beginColumn endLine endColumnposicioacuten del lexema en el fichero analizado (indicada por los nuacutemeros de fila y de columna de su comienzo y de su terminacioacuten)

ↄ Valor asociado a un siacutembolo

34

En las producciones que especifican la sintaxis se pueden incluir asignaciones que sirven para dejar ano-tado el valor asociado a un siacutembolo

bull siacutembolo no terminal valor = nombreSimbolo()el meacutetodo de anaacutelisis asociado a nombreSimbolo se ha declarado de un cierto tipo (distinto del tipo void) valor ha de ser una variable del mismo tipo que el meacutetodo

bull siacutembolo terminal dato = lt nombrePieza gtel valor asignado es el valor de la clase Token representativo de la pieza sintaacutectica (nominal) dato ha de ser una variable de tipo Token

ↄ Miradas sobre la pieza sintaacutectica comunicadaLa pieza sintaacutectica comunicada puede mirarse desde cada uno de los dos analizadores

bull analizador sintaacutecticola pieza comunicada (desde el analizador lexicograacutefico) se tiene anotada en un campo de la clase del analizador sintaacutectico declarado como

(static) public Token token

este valor es accesible desde el coacutedigo de todos los meacutetodos de anaacutelisis sintaacutectico el contenido del campo variacutea cuando se recibe una nueva pieza sintaacutectica

bull analizador lexicograacuteficola pieza dispuesta para ser comunicada (al analizador sintaacutectico) se tiene anotada en una variable local declarada como

Token matchedToken

este valor es accesible para el coacutedigo de todas las acciones lexicograacuteficas

ↄ El lexema de la pieza comunicadaEl lexema de la pieza sintaacutectica comunicada se encuentra disponible en dos sitios distintos que compar-ten el mismo nombre pero que son de distinto tipo y se usan de distinta manera

bull String image

es un campo de la clase TOKEN puede consultarse en relacioacuten con un objeto de esa clase que esteacute disponible

bull StringBuffer image

es un campo de la clase del analizador lexicograacutefico ( ∙ ∙ ∙ ∙ TokenManagerjava) es accesible desde el coacutedigo de la clase y por lo tanto desde todas las acciones lexicograacuteficas (bloques de coacute-digo que estaacuten incorporados al analizador lexicograacutefico)

se trata de un valor disponible tanto para las piezas sintaacutecticas nominales (TOKEN) como para las secuencias que se saltan (SKIP)

ↄ Resumen relativo a la lexicografiacutea

TOKEN pieza sintaacutectica nominal∙ ∙ ∙ pieza sintaacutectica anoacutenimaSKIP secuencia de caracteres que no forman pieza

35

Precisiones sobre el analizador sintaacutectico generadoPuede decirse que en su funcionamiento maacutes sencillo cuando no se emplean las posibilidades de examen por adelantado (ldquolookaheadrdquo) JavaCC comprueba que las reglas sintaacutecticas cumplen la condicioacuten LL(1) y generan un analizador descendente-predictivo-recursivo Pero esta apreciacioacuten hay que matizarla ya que el analizador sintaacutectico generado no se ajusta en sentido estricto al modelo de analizador LL(1)

En el modelo de anaacutelisis LL(1) el orden del examen de las distintas alternativas de expansioacuten de un siacutembo-lo no terminal es indiferente ya que se conocen los siacutembolos directores de cada una de ellas (que resultan ser conjuntos disjuntos) en la implementacioacuten del subprograma de anaacutelisis asociado al siacutembolo no terminal ltNombreSimbgt cuyas producciones son

ltNombreSimbgt = α1

| α2

∙ ∙ ∙

| αn

se podriacutea preguntar en cualquier orden si la pieza actual pertenece al conjunto de siacutembolos directores de las distintas reglas Sin embargo en el analizador sintaacutectico generado por JavaCC la implementacioacuten del meacute-todo asociado al siacutembolo no terminal ltNombreSimbgt no sigue esa pauta

Si las anteriores reglas sintaacutecticas se transcriben en JavaCC de la forma

void nombreSimb () α1-jcc

| α2-jcc

∙ ∙ ∙

| αn-jcc

1

accioacutenlexicograacutefica

comuacuten

Commom -TokenAction2

accioacutenlexicograacutefica

propia

(accioacuten ligadaa una pieza)3

bloque dedeclaracioneslexicograacuteficas

TOKEN_MGR_DECLS4

declaracioneslexicograacuteficaspredefinidas

(disponibilidad

de uso)TOKENsisisisi∙ ∙ ∙sino--SKIPnosisisi

Las indicaciones si de las columnas 3 y 4 son consecuencia de las indicaciones de la columna 2

La accioacuten lexicograacutefica comuacuten se ejecuta despueacutes de la accioacuten lexicograacutefica propia

36

el meacutetodo de anaacutelisis generado procede de la siguiente manera

se van considerando las alternativas sucesivamente en el mismo orden en el que estaacuten especifica-das primero la de α1 (α1-jcc) despueacutes la de α2 (α2-jcc) etc

para cada alternativa seleccionada se comprueba si la pieza por adelantado coincide con alguno de los siacutembolos iniciales de la parte derecha y si hay coincidencia se prosigue el anaacutelisis con ella si no hay coincidencia se pasa a la alternativa siguiente

siguiendo con este orden si se llega al final de las alternativas sin haberse encontrado coincidencia alguna se produce un error sintaacutectico

En el analizador generado por JavaCC hay una peculiaridad que no se tiene en los analizadores LL(1) es-trictamente considerados la palabra vaciacutea siacute se considera como posible siacutembolo inicial de la parte derecha una regla (esto ocurre siempre que la parte derecha es anulable) De esta peculiaridad se deriva una conse-cuencia que ha de tenerse en cuenta al escribir la especificacioacuten dado que siempre es posible considerar la palabra vaciacutea como la siguiente pieza por adelantado si se llega a examinar la alternativa correspondiente a una produccioacuten anulable el analizador generado siempre seleccionaraacute esa alternativa con independencia de la siguiente pieza que esteacute presente en la entrada que queda por analizar

Asiacute pues si alguna de las producciones que definen un siacutembolo no terminal es anulable es importante el or-den en que se escriben las alternativas en una especificacioacuten sintaacutectica JavaCC el funcionamiento del ana-lizador generado siacute depende de la colocacioacuten elegida para las reglas

Para ilustrar estas peculiaridades del analizador sintaacutectico generado por JavaCC a continuacioacuten se expone un ejemplo

∆ Ejemplo 9

Se quiere obtener un analizador leacutexico-sintaacutectico de textos que se ajustan al siguiente formato una se-cuencia de uno o maacutes nombres seguida del siacutembolo igual seguido de un nuacutemero entero si la secuencia tiene maacutes de un nombre han de estar separados bien por una coma en todos los casos o bien por el siacutem-bolo dos puntos en todos los casos esto es son correctas las secuencias

uno dos tres = 123exclusivo = 0Abajo Arriba = 57

y son incorrectas las secuenciasuno dos tres = 123

exclusivo inclusive = 0

fuera dentro = 56

Una gramaacutetica que define las secuencias con este formato es

ltListagt = nombre ltOtrosNombresgt = numero

ltOtrosNombresgt = nombre nombre | nombre nombre | ε

se trata de una gramaacutetica que cumple la condicioacuten LL(1)- el siacutembolo director para la alternativa vaciacutea es el siacutembolo igual es evidente pues que las tres reglas de ltOtrosNombresgt tienen siacutembolos directores distintos

37

- la decisioacuten de abandonar la repeticioacuten del anaacutelisis de un nuevo nombre (en las dos primeras alterna-tivas de ltOtrosNombresgt) se puede tomar ante la presencia del siacutembolo igual

En la implementacioacuten de un analizador sintaacutectico LL(1) en sentido estricto la seleccioacuten de la alternativa vaciacutea para expandir el siacutembolo ltOtrosNombresgt se hariacutea tras comprobar expliacutecitamente que la pie-za sintaacutectica por adelantado es el siacutembolo igual

El funcionamiento del analizador generado por JavaCC depende del orden de colocacioacuten de las reglas A continuacioacuten se exponen varias posibilidades de especificacioacuten y se comenta el analizador generado en cada caso

bull Especificacioacuten primeraSe transcriben literalmente las reglas tal y como se han escrito antes en la notacioacuten BNF noacutetese que para poder codificar la parte derecha de la regla vaciacutea ha de incluirse una accioacuten sintaacutectica vaciacutea

PARSER_BEGIN (Igualdad)

public class Igualdad

public static void main (String[] argum) throws ParseException Igualdad analisis = new Igualdad(Systemin) analisissecuencia() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (Igualdad)

void secuencia() lista() ltEOFgt

void lista() lt nombre gt otrosNombres() = lt numero gt

void otrosNombres() lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt ) |

TOKEN lt nombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN lt numero ( [ 0 - 9 ] )+ gt

SKIP | r | n

38

El analizador generado funciona correctamente puesto que en el meacutetodo otrosNombres se selec-ciona la alternativa vaciacutea despueacutes de haber comprobado que la pieza por adelantado no es siacutembolo ini-cial de ninguna de las dos alternativas precedentes (no es una coma ni un siacutembolo dos puntos)

bull Especificacioacuten segundaSe considera la misma especificacioacuten anterior pero cambiando el orden de colocacioacuten de las reglas ahora la produccioacuten vaciacutea se coloca como primera alternativa (soacutelo se muestran las partes modifica-das)

void otrosNombres() | lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt )

Con la entrada de esta especificacioacuten el generador JavaCC produce un mensaje en el que se avisa de que las dos alternativas que estaacuten colocadas detraacutes de la alternativa vaciacutea nunca podraacuten seleccionarse aun asiacute se genera un analizador Pero es un analizador que funciona incorrectamente asiacute por ejemplo la entrada

mxa nyz = 57

se considera incorrecta despueacutes de visto el primer nombre se intenta aplicar la produccioacuten vaciacutea (se espera encontrar el siacutembolo igual) pero habriacutea que aplicar la regla cuyo siacutembolo director es la coma

bull Especificacioacuten terceraSe utilizan las posibilidades de la notacioacuten BNF-Ampliada para prescindir de la especificacioacuten expliacuteci-ta de la palabra vaciacutea se aprovecha que JavaCC admite el operador + para indicar repeticiones de una o maacutes veces la alternativa vaciacutea estaacute impliacutecitamente contemplada en la posibilidad de cero repeti-ciones que contempla el operador (soacutelo se muestran las partes modificadas en la especificacioacuten)

void otrosNombres() ( lt nombre gt )+ | ( lt nombre gt )

El meacutetodo otrosNombres() generado hace las comprobaciones en el siguiente orden

- se comprueba si la pieza por adelantado es una coma (siacutembolo inicial de la primera alternativa) y en este caso se selecciona la primera regla- si no es una coma se comprueba si es un siacutembolo dos puntos (siacutembolo inicial de la segunda alter-nativa) y en este caso se selecciona la segunda regla- si no es un siacutembolo dos puntos se considera la palabra vaciacutea como siacutembolo inicial (siempre posi-ble impliacutecitamente) para la segunda alternativa en este caso para que no haya error la pieza por adelantado ha de ser un siacutembolo igual

Se trata pues de un analizador que funciona correctamente

bull Especificacioacuten cuarta

39

Se pone la misma especificacioacuten anterior pero cambiando el orden de consideracioacuten de la regla vaciacutea ahora se incluye impliacutecitamente en la primera alternativa (soacutelo se muestran las partes modificadas)

void otrosNombres() ( lt nombre gt ) | ( lt nombre gt )+

Para esta especificacioacuten el generador JavaCC emite un aviso en el que se indica que la segunda alter -nativa nunca se seleccionaraacute no obstante se genera un analizador que no funciona como se supone que deberiacutea hacerlo por ejemplo el anaacutelisis de la entrada

nyz mxa = 1

produce un error despueacutes de tratado el primer nombre se intenta aplicar la produccioacuten vaciacutea (corres-pondiente a la repeticioacuten de cero veces contemplada en la primera regla) por lo que no se encuentra el esperado siacutembolo igual

40

Page 11: JavaCC

La opcionalidad tambieacuten se puede indicar empleando el metasiacutembolo de interrogacioacuten

( α )

Para indicar repeticioacuten de una o maacutes veces se utiliza el metasiacutembolo operador aditivo suma

( α )+

bull Control del final de fichero de entradaPara controlar el final del fichero en el texto analizado ha de antildeadirse una regla sintaacutectica especial que tenga (seguacuten la notacioacuten BNF) la forma

ltProgramaCompletogt = ltProgramagt FF

donde

ltProgramaCompletogt es un nuevo siacutembolo (no terminal) auxiliar antildeadido

ltProgramagt es el siacutembolo inicial de la gramaacutetica sintaacutectica

FF es una representacioacuten del final del fichero

En JavaCC el final de fichero se escribe con la notacioacuten ltEOFgt siendo EOF una palabra reservada la regla sintaacutectica especial antildeadida a la especificacioacuten quedariacutea codificada asiacute

void programaCompleto() programa() ltEOFgt

bull Representacioacuten de la produccioacuten vaciacuteaLas producciones vaciacuteas (reglas sintaacutecticas cuya parte derecha es la palabra vaciacutea ε ) no se pueden repre-sentar expliacutecitamente en una especificacioacuten leacutexico-sintaacutectica escrita en JavaCC Para indicar la presencia de una produccioacuten vaciacutea han de aplicarse las posibilidades de la notacioacuten BNF-Ampliada seguacuten esta nota-cioacuten cuando se indica la opcionalidad de un componente se estaacute especificando impliacutecitamente la palabra vaciacutea (en el caso de que el componente no esteacute presente)

Asiacute pues si se quisiera escribir en JavaCC la construccioacuten sintaacutectica expresada por las reglas

ltComponentegt = α

| ε

deberiacutean de considerarse con la notacioacuten equivalente

ltComponentegt = [ α ]

y si hubiera varias alternativas no vaciacuteas

ltComponentegt = α | β

| ε

habriacutea que considerarlas como

ltComponentegt = [ α | β ]

o tambieacuten podriacutea verse como

ltComponentegt = α

10

| [ β ]

Como ejemplo sea la especificacioacuten sintaacutectica de una llamada a una funcioacuten que puede tener cero o maacutes paraacutemetros separados por comas aunque no haya paraacutemetros los pareacutentesis estaacuten presentes Las siguien-tes reglas definen esta estructura

ltLlamadagt = id ( ltParametrosgt )

ltParametrosgt = ltExpresiongt ltExpresiongt | ε

En JavaCC la definicioacuten del siacutembolo ltParametrosgt se puede escribir como sigue (noacutetese que aquiacute los pareacutentesis empleados para aplicar el operador son metasiacutembolos)

void parametros() [ expresion() ( expresion() ) ]

loz Seccioacuten de lexicografiacuteaEn esta seccioacuten se indica la lexicografiacutea del lenguaje para el que se va a generar el analizador la notacioacuten de JavaCC que representa la forma de cada una de las piezas sintaacutecticas es una variante de las bien conoci-das expresiones regulares

bull El nombre de las piezas sintaacutecticas nominalesPara asociar un nombre a una pieza sintaacutectica y para al mismo tiempo definir la forma de la pieza se em-plea la notacioacuten

TOKEN lt nombreTerminal Expresioacuten-Regular gt

- TOKEN es una palabra reservada detraacutes de ella se pone el separador dos puntos

- nombreTerminal representa el nombre que se quiere asociar a la pieza sintaacutectica

- Expresioacuten-Regular indica una expresioacuten regular que especifica la forma de la pieza

- el nombre y la expresioacuten se separan mediante el caraacutecter dos puntos y conjuntamente se deli -mitan mediante los caracteres lt y gt a su vez toda la asociacioacuten se delimita con llaves

bull Expresiones regulares en JavaCCLas expresiones regulares en una especificacioacuten JavaCC se escriben de manera parecida a la forma utiliza-da en otras conocidas notaciones aunque con ciertas peculiaridades A continuacioacuten se mencionan los as-pectos maacutes significativos de las expresiones regulares JavaCC y algunos ejemplos

Los operadores de las expresiones son

| unioacuten

repeticioacuten cero o maacutes veces

+ repeticioacuten una o maacutes veces

opcionalidad

11

Los pareacutentesis permiten fijar el orden de aplicacioacuten de los operadores Una peculiaridad de las expresiones regulares JavaCC es que los operadores de repeticioacuten y de opcionalidad requieren que el componente al que se aplican esteacute en todo caso delimitado por pareacutentesis

Se pueden citar las siguientes caracteriacutesticas baacutesicas de las expresiones regulares en JavaCC

Se tiene la posibilidad de definir conjuntos de caracteres poniendo entre corchetes la relacioacuten de carac-teres separados por comas y cada caraacutecter delimitado por comillas En un conjunto se puede fijar un ran-go de caracteres poniendo el primero y el uacuteltimo separados por el siacutembolo guioacuten (-) A veces resulta maacutes coacutemodo indicar los caracteres que no pertenecen al conjunto para ello se puede definir el comple -mentario de un conjunto poniendo el siacutembolo ~ delante del corchete inicial

Para indicar que una secuencia de caracteres colocados consecutivamente constituye por siacute misma una expresioacuten regular (o un componente de una expresioacuten) se escribe la secuencia delimitada por comillas El siacutembolo se emplea como metacaraacutecter para indicar que el siacutembolo que estaacute inmediatamente a su derecha se considere como caraacutecter del alfabeto no como posible metacaraacutecter (operador delimitador)

En una expresioacuten regular se pueden incluir como si fueran caracteres las secuencias de escape con la misma notacioacuten y el mismo significado que en Java o en C

Noacutetese que en una produccioacuten sintaacutectica la opcionalidad se puede especificar de dos formas con corchetes o con el siacutembolo de interrogacioacuten sin embargo en una expresioacuten regular la opcionalidad soacutelo se puede es-pecificar mediante el siacutembolo de interrogacioacuten (los corchetes en las expresiones regulares son para definir conjuntos de caracteres)

A continuacioacuten se muestran ejemplos de expresiones regulares escritas seguacuten la notacioacuten JavaCC

ENDsecuencia de las tres letras mayuacutesculas que conforman la palabra END

whilesecuencia de las cinco letras minuacutesculas que conforman la palabra while

ncaraacutecter representativo del fin de liacutenea (secuencia de escape)

rcaraacutecter representativo del retorno de carro (secuencia de escape)

caraacutecter dos puntos

=secuencia de dos caracteres (siacutembolo de asignacioacuten en Pascal)

gtcaraacutecter representativo del operador de relacioacuten mayor

(i | I)(f | F)palabra ldquoifrdquo escrita con letras minuacutesculas o mayuacutesculas (cuatro combinaciones)

if | IFpalabra ldquoifrdquo escrita soacutelo con minuacutesculas o soacutelo con mayuacutesculas (dos combinaciones)

12

u | o | i | e | auna cualquiera de las vocales minuacutesculas (pero soacutelo una de ellas)

[ A E I O U ]una cualquiera de las vocales mayuacutesculas (pero soacutelo una de ellas)

[ a - z ntilde ]una cualquiera de las letras minuacutesculas incluyendo la letra ntilde

[ a - z A - Z 0 - 9 ]una cualquiera de las letras o de las cifras decimales

([ 0 - 9 ])+constante entera (nuacutemero de una o maacutes cifras)

noacutetese que es incorrecto poner [ 0 - 9 ]+

( + | - )([ 0 - 9 ])+constante entera precedida opcionalmente por un signo

noacutetese que la primera cifra ha de seguir inmediatamente al signo (si existe)

([+ -])([ 0 - 9 ])+representa lo mismo que la expresioacuten anterior

[ a - z ] ([ a - z 0 - 9 ])una letra minuacutescula seguida de cero o maacutes letras minuacutesculas o cifras decimales

~[a - z ntilde A - Z Ntilde]cualquier caraacutecter que no sea una letra

noacutetese que la expresioacuten representa un uacutenico caraacutecter

~[n r]cualquier caraacutecter (un uacutenico caraacutecter) excepto el fin de liacutenea y el retorno de carro

~[ ]cualquier caraacutecter del alfabeto (un uacutenico caraacutecter)

([ 0 - 9 ])+ ( ([ 0 - 9 ])+)constante aritmeacutetica con parte decimal opcional

Los modernossecuencia de catorce caracteres Los modernos

noacutetese el uso de para imponer la condicioacuten de caraacutecter del siacutembolo comillas

nsecuencia de cuatro caracteres n

noacutetese que aquiacute no se representa la secuencia de escape

bull Piezas sintaacutecticas internas (ldquoprivaterdquo)

13

Es posible definir piezas sintaacutecticas auxiliares llamadas ldquointernasrdquo porque no se comunican al analizador sintaacutectico sino que soacutelo sirven para facilitar la escritura de la forma de las piezas sintaacutecticas que siacute se co-munican con el uso de las piezas internas es posible evitar repeticiones de escritura y hacer maacutes legible la especificacioacuten de las caracteriacutesticas lexicograacuteficas Las piezas internas permiten dar nombre a subexpre-siones regulares que a su vez pueden emplearse como componentes para escribir expresiones maacutes com-plejas que incorporan esos nombres Para indicar que una pieza sintaacutectica se considere como interna se pone el siacutembolo delante del nombre asociado a la pieza

A continuacioacuten se muestra un ejemplo tiacutepico de utilizacioacuten de las piezas internas en una especificacioacuten le-xicograacutefica en JavaCC Se quieren definir tres piezas sintaacutecticas que representan un nombre una constante entera y una constante decimal una posible especificacioacuten es

TOKEN ltnombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN ltcteEntera ( [ 0 - 9 ] )+ gt

TOKEN ltcteDecimal ( [ 0 - 9 ] )+ ( [ 0 - 9 ] )+ gt

Con la inclusioacuten de tres piezas sintaacutecticas internas auxiliares esta misma especificacioacuten podriacutea escribirse de la siguiente manera

TOKEN lt letra [ a - z A - Z ntilde Ntilde ] gt

TOKEN lt cifra [ 0 - 9 ] gt

TOKEN lt numero ( ltcifragt )+ gt

TOKEN lt nombre ( ltletragt )+ gt

TOKEN lt cteEntera ltnumerogt gt

TOKEN lt cteDecimal ltnumerogt ltnumerogt gt

Los nombres letra cifra y numero son exclusivos (internos) del analizador lexicograacutefico y no se pueden emplear como nombres de piezas en la especificacioacuten de las reglas sintaacutecticas cuando esos nom-bres se usan en la definicioacuten de otras piezas han de escribirse delimitados por los siacutembolos lt y gt

bull Distincioacuten restringida entre minuacutesculas y mayuacutesculasSe tiene la opcioacuten Ignore_Case para determinar si en el anaacutelisis del texto de entrada se distingue entre las letras minuacutesculas y las mayuacutesculas el valor por defecto false indica que siacute se diferencian si se asig-na el valor true a esta opcioacuten entonces no se diferencian en ninguacuten caso con independencia de que en los patrones que se definan para las piezas sintaacutecticas se pongan minuacutesculas o mayuacutesculas

Pero a veces conviene que la diferenciacioacuten entre minuacutesculas y mayuacutesculas no se aplique de manera ge-neral a toda la entrada sino en particular a las secuencias de caracteres que se acoplen a ciertos patrones Para este fin existe la posibilidad de establecer que en la expresioacuten regular que define el patroacuten de una determinada pieza sintaacutectica no se diferencie entre minuacutesculas y mayuacutesculas ello se consigue poniendo

14

[ IGNORE_CASE ]

inmediatamente detraacutes de la palabra TOKEN con la que empieza la especificacioacuten de la pieza sintaacutectica en la que se pretende la no distincioacuten entre minuacutesculas y mayuacutesculas

Noacutetese que ignore_case tiene dos significados

- es el nombre de un paraacutemetro de la seccioacuten de opciones (puede ponerse en minuacutesculas o mayuacutesculas)- es una palabra reservada que puede aplicarse (escribieacutendola entre corchetes y en mayuacutesculas) a la de -finicioacuten de una pieza sintaacutectica

Por ejemplo sea un lenguaje que en general distingue entre minuacutesculas y mayuacutesculas pero en la codifi -cacioacuten del exponente de una constante real exponencial puede ponerse la letra e en minuacutescula o en ma-yuacutescula En la especificacioacuten JavaCC para ese lenguaje la opcioacuten Ignore_Case ha de tomar el valor false en la definicioacuten de una constante exponencial son posibles las siguientes formas equivalentes

1) sin hacer uso de IGNORE_CASE

TOKEN lt cifras ( [ 0 - 9 ] )+ gt

TOKEN lt signo [+ -] gt

TOKEN lt constante

ltcifrasgt ( ltcifrasgt) [e E] (ltsignogt) ltcifrasgt gt

2) aplicando IGNORE_CASE

TOKEN lt cifras ( [ 0 - 9 ] )+ gt

TOKEN lt signo [+ -] gt

TOKEN [IGNORE_CASE] lt constante

ltcifrasgt ( ltcifrasgt) e (ltsignogt) ltcifrasgt gt

bull Secuencias de caracteres sin efecto en la estructura sintaacutecticaEn los lenguajes de programacioacuten suele haber secuencias de caracteres que no forman parte de la estructu-ra sintaacutectica de un programa esas secuencias son detectadas por el analizador lexicograacutefico pero no son comunicadas al sintaacutectico no han de considerarse como piezas sino que han de saltarse En JavaCC se pueden definir estas secuencias mediante la palabra reservada SKIP la notacioacuten de una especificacioacuten con la palabra SKIP de una secuencia que ha de saltarse es la misma que la notacioacuten usada para las espe-cificaciones con la palabra TOKEN de las piezas que han de comunicarse

Los dos casos maacutes habituales de secuencias que no se comunican al analizador sintaacutectico son

a) los caracteres separadores que no tienen significado en el texto analizadoen los lenguajes de codificacioacuten en formato libre hay caracteres del texto fuente que han de ignorarse en la mayoriacutea de los casos estos caracteres son el espacio en blanco el tabulador el retorno de carro y el fin de liacutenea esta caracteriacutestica se especifica

SKIP |t | r |n

b) los comentarios

15

en el anaacutelisis sintaacutectico de un programa los comentarios han de ignorarse una manera de especificar esta circunstancia es poniendo en una definicioacuten SKIP la expresioacuten regular que denota la forma de los comentarios del lenguaje por ejemplo para los comentarios de una liacutenea de Java se pondriacutea

SKIP

lt (~ [n r]) gt

Tareas asociadas a la estructura sintaacutecticaJavaCC genera un analizador sintaacutectico descendente recursivo constituido por un conjunto de meacutetodos ca-da siacutembolo no terminal de la gramaacutetica sintaacutectica tiene asociado un meacutetodo de anaacutelisis (el meacutetodo tiene el mismo nombre que el siacutembolo correspondiente) Un meacutetodo de anaacutelisis ademaacutes de la realizacioacuten de la parte del anaacutelisis sintaacutectico que le corresponde puede llevar a cabo otras tareas complementarias que sirvan para efectuar un anaacutelisis maacutes complejo del texto de entrada

loz Bloque para un siacutemboloAl describir la forma de las reglas sintaacutecticas de un siacutembolo no terminal en una especificacioacuten JavaCC ya se ha visto que detraacutes de la definicioacuten del nombre del siacutembolo y antes de empezar con las diferentes alter-nativas que tiene asociadas se incluye un bloque de coacutedigo Java (en el ejemplo de presentacioacuten este bloque estaacute vaciacuteo en todos los siacutembolos no terminales de la gramaacutetica) Cada bloque situado en esa posicioacuten puede considerarse ligado al siacutembolo no terminal en cuya descripcioacuten estaacute incluido el coacutedigo Java del bloque se traslada literalmente al principio del meacutetodo generado correspondiente al siacutembolo no terminal y por lo tan-to es lo primero que se considera (se ejecuta) cuando en el transcurso del proceso de anaacutelisis se realiza una llamada al meacutetodo Asiacute pues cada siacutembolo no terminal siempre tiene asociado un uacutenico bloque de coacute-digo Java ndashbloque de presencia obligada aunque pueda estar vaciacuteo- en este bloque se pueden poner de-claraciones y sentencias

loz Acciones sintaacutecticasAdemaacutes del bloque asociado a cada uno de los siacutembolos no terminales de la gramaacutetica se pueden incluir otros bloques de coacutedigo Java intercalados en cualquiera de las partes derechas de las reglas sintaacutecticas Los bloques incluidos en las partes derechas se denominan ldquoacciones sintaacutecticasrdquo (parser actions) ya que son trozos de coacutedigo que pueden considerarse asociados a los puntos de la estructura sintaacutectica donde estaacuten co-locados Una accioacuten sintaacutectica se puede poner en cualquier posicioacuten al principio al final o en el medio de la parte derecha de una regla precedida o seguida de siacutembolos terminales o no terminales el coacutedigo Java de una accioacuten sintaacutectica se traslada literalmente al meacutetodo asociado al siacutembolo no terminal de la parte izquierda de la regla de manera que se ejecuta cada vez que el analizador transcurre por el punto de la estructura sintaacutectica donde se ha incorporado la accioacuten (esto es las acciones sintaacutecticas se incorporan al coacutedigo del analizador sintaacutectico) No obstante su nombre en los bloques de las acciones sintaacutecticas suele ponerse coacutedigo dedicado a la realizacioacuten de tareas semaacutenticas

Si en el bloque asociado a un siacutembolo no terminal se ponen declaraciones los nombres ahiacute definidos son accesibles en todas las acciones sintaacutecticas intercaladas en las partes derechas de las reglas de ese siacutembolo la justificacioacuten es sencilla el coacutedigo del bloque asociado al siacutembolo se incluye al principio de un meacutetodo y el coacutedigo de las acciones pertenece a ese mismo meacutetodo

∆ Ejemplo 1

Se considera de nuevo el ejemplo de presentacioacuten ahora se pretende completar el anaacutelisis leacutexico-sintaacutec-tico con la realizacioacuten de las siguientes tareas

- cada vez que se analiza una expresioacuten un teacutermino o un factor se graba el nombre del siacutembolo no terminal correspondiente y

16

- cuando se analiza un teacutermino en el que hay operadores multiplicativos se cuenta su nuacutemero y se graba el resultado (noacutetese que se cuentan los operadores de cada teacutermino pero no se acumula la cuenta de todos los operadores de la expresioacuten)

Para la realizacioacuten de estas tareas se antildeaden bloques de coacutedigo Java En la especificacioacuten que se describe a continuacioacuten puede apreciarse- para los siacutembolos no terminales expresion termino y factor se ha incluido en los blo-ques correspondientes la operacioacuten de grabacioacuten de su nombre que se ejecutaraacute cada vez que se efec-tuacutee una llamada al meacutetodo- en el bloque asociado a termino se tiene la declaracioacuten del contador de operadores (que seraacute una variable local del meacutetodo)- en la parte derecha de la regla que define la estructura de un teacutermino se han incluido dos acciones sintaacutecticas una para contar la cantidad de operadores (situada detraacutes de la pieza sintaacutectica del opera-dor) y otra para grabar la cantidad de operadores del teacutermino (situada al final de la parte derecha)- se ha definido el meacutetodo grabar para facilitar la escritura del coacutedigo Java

options Ignore_Case = true

PARSER_BEGIN (ExprMin) public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

private static void grabar(String nombre) Systemoutprintln ( -gt + nombre + n)

PARSER_END (ExprMin)

void unaExpresion()

expresion() ltEOFgt

void expresion() grabar(Expresion)

termino() ( + termino() )

void termino() int nAst = 0 grabar(Termino)

17

factor() ( nAst++ factor() ) if (nAst gt 0) Systemoutprintln (n Asteriscos + nAst + n)

void factor() grabar(Factor)

ltconstantegt | ltvariablegt | ( expresion() )

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP | t | n | r

Cuando se incluyen acciones sintaacutecticas la legibilidad del aspecto sintaacutectico de la especificacioacuten queda oscurecida para paliar ese inconveniente puede antildeadirse un comentario que muestre la regla sintaacutectica escueta sin las acciones por ejemplo podriacutea escribirse

void termino() int nAst = 0 grabar(Termino)

factor() ( factor() )

factor()

( nAst++ factor() )

if (nAst gt 0) Systemoutprintln (n Asteriscos + nAst + n)

loz Valor comunicado por un meacutetodoEn todo lo visto hasta ahora los meacutetodos de anaacutelisis sintaacutectico generados por JavaCC no devuelven valor alguno al terminar su tarea de anaacutelisis ya que en la especificacioacuten siempre se ha declarado el nombre de un meacutetodo precedido de void (son meacutetodos de tipo void) Pero en general un meacutetodo de anaacutelisis sintaacutec-tico puede ser de cualquier tipo (primitivo o no) por ello un meacutetodo puede devolver un valor de cualquier tipo (de cualquier clase) al meacutetodo desde el que se efectuoacute la llamada

Esta posibilidad de devolucioacuten de valores tiene una utilidad fundamental en la implementacioacuten de analiza-dores de lenguajes permite la comunicacioacuten de datos (atributos de las entidades analizadas) entre los dife -rentes meacutetodos que colaboran en el anaacutelisis del programa completo

En una especificacioacuten JavaCC la llamada a un meacutetodo ocurre cuando en la parte derecha de una regla estaacute el nombre del siacutembolo no terminal asociado al meacutetodo Cuando un meacutetodo devuelve un valor (tiene un tipo distinto de void) es habitual la recepcioacuten del valor devuelto en una variable en la propia especificacioacuten

18

se puede indicar el nombre de la variable a la que se asignaraacute el valor devuelto tras la llamada para ello se emplea una notacioacuten ideacutentica a la forma que tienen las sentencias de asignacioacuten en el lenguaje Java

dato = nombreSimb()

donde dato es el nombre de la variable a la que se asigna el valor devuelto por el meacutetodo llamado esa va -riable ha de estar oportunamente declarada del mismo tipo que dicho meacutetodo

∆ Ejemplo 2

En el mismo ejemplo de presentacioacuten se desea ademaacutes del anaacutelisis leacutexico-sintaacutectico de la entrada contar la cantidad de operadores que hay en la expresioacuten A continuacioacuten se expone una especificacioacuten JavaCC que es una posible solucioacuten soacutelo se muestran las reglas sintaacutecticas (el resto de la especificacioacuten es igual que en el ejemplo de presentacioacuten)

En la especificacioacuten que se propone puede apreciarse- los siacutembolos no terminales expresion termino y factor se han declarado de tipo int los meacutetodos correspondientes devuelven un valor de tipo entero- el valor devuelto por los meacutetodos sirve para propagar a traveacutes de la estructura sintaacutectica la cuenta de los operadores que hay en el texto analizado- por tratarse de meacutetodos que devuelven un valor es preciso incluir las correspondientes sentencias de devolucioacuten return

void unaExpresion() int nOper nOper = expresion() ltEOFgt Systemoutprintln (Cantidad de operadores = + nOper)

int expresion() int n i n = termino() ( + n++ i = termino() n = n + i )

return n

int termino() int n i n = factor() ( n++ i = factor() n = n + i )

return n

int factor() int n ltconstantegt return 0 | ltvariablegt return 0 | ( n = expresion() ) return n

La especificacioacuten de un factor tambieacuten podriacutea escribirse de esta otra manera

19

int factor() int n = 0

( ltconstantegt | ltvariablegt | ( n = expresion() ) )

return n

A continuacioacuten se expone un nuevo ejemplo para ilustrar la utilidad que proporciona el valor devuelto por un meacutetodo de anaacutelisis sintaacutectico para la propagacioacuten (ascendente) de atributos (propiedades de las entida-des analizadas)

∆ Ejemplo 3

Se pretende ampliar el analizador del ejemplo de presentacioacuten para que se emita un mensaje indicativo cuando la expresioacuten analizada sea una expresioacuten en la que soacutelo aparecen constantes en la solucioacuten que se muestra soacutelo se incluyen las reglas sintaacutecticas (en el resto de la especificacioacuten no hay modificacio-nes)

void unaExpresion() boolean esCte

expresion() ltEOFgt

esCte = expresion() ltEOFgt

if (esCte) Systemoutprintln(nExpresion constanten)

boolean expresion() boolean esCte esTambienCte

termino() ( + termino() )

esCte = termino() ( + esTambienCte = termino() esCte = esCte ampamp esTambienCte )

return esCte

boolean termino() boolean esCte esTambienCte

factor() ( factor() )

esCte = factor() ( esTambienCte = factor() esCte = esCte ampamp esTambienCte )

return esCte

20

boolean factor() boolean esCte

ltconstantegt return true | ltvariablegt return false | ( esCte = expresion() ) return esCte

bull Reconsideracioacuten sobre las producciones vaciacuteasAnteriormente se ha comentado que para representar una produccioacuten vaciacutea en una especificacioacuten JavaCC hay que hacer uso de los metasiacutembolos de opcionalidad para ilustrar esta idea se ha incluido como ejem-plo la especificacioacuten de una lista de paraacutemetros compuesta por cero o maacutes expresiones separadas entre siacute por el caraacutecter coma

void parametros() [ expresion() ( expresion() ) ]

Pero si se aprovecha la posibilidad de incorporar acciones sintaacutecticas se tiene una manera de especificar directamente producciones vaciacuteas para conseguirlo basta considerar una produccioacuten vaciacutea (no tiene nada en su parte derecha) acompantildeada de una accioacuten sintaacutectica cuyo bloque de coacutedigo esteacute vaciacuteo Seguacuten esta posibilidad la especificacioacuten anterior podriacutea tambieacuten escribirse asiacute

void parametros() expresion() ( expresion() ) |

En general la accioacuten sintaacutectica asociada a una produccioacuten vaciacutea no tiene por queacute ser un bloque vaciacuteo de coacutedigo por ejemplo si se quiere que el meacutetodo que analiza los paraacutemetros de una llamada devuelva la cantidad de paraacutemetros encontrados se podriacutea escribir la siguiente especificacioacuten

int parametros()

int cantidad

( expresion() cantidad = 1

( expresion() cantidad++ )

| cantidad = 0 )

return cantidad

y si no quisiera ponerse expliacutecitamente la alternativa de la produccioacuten vaciacutea podriacutea escribirse

int parametros() int cantidad = 0

21

[ expresion() cantidad++ ( expresion() cantidad++ ) ]

return cantidad

Comunicacioacuten entre los analizadores leacutexico y sintaacutecticoCada vez que el analizador sintaacutectico realiza una llamada al analizador lexicograacutefico recibe como resultado de la llamada una representacioacuten de la pieza sintaacutectica encontrada en el texto analizado En los analizadores generados por JavaCC la comunicacioacuten de la pieza se efectuacutea mediante un valor de clase Token Esta cla-se estaacute definida en el fichero Tokenjava que se obtiene en todo caso siempre con el mismo contenido cualquiera que sea la especificacioacuten proporcionada como entrada al generador

Entre los campos de la clase Token se encuentran los siguientes

public int kind

nuacutemero entero que sirve de representacioacuten interna de la pieza sintaacutectica estos nuacutemeros asociados a las piezas son asignados automaacuteticamente por JavaCC los valores considerados pueden consultar-se en el fichero generado de nombre Constantsjava

public String image

cadena que contiene de la secuencia de caracteres (lexema) que constituyen la pieza sintaacutectica co-municada

public int beginLine beginColumn endLine endColumn

posiciones ocupadas (nuacutemeros de liacutenea y columna en el fichero de entrada) por el comienzo y el fi-nal del lexema de la pieza sintaacutectica comunicada

La clase Token tiene otros campos y meacutetodos en el fichero Tokenjava se tienen comentarios des-criptivos sobre todos los campos y meacutetodos que forman parte de la clase todos los campos de la clase se declaran como public

Ya se ha comentado anteriormente que cuando un meacutetodo de anaacutelisis sintaacutectico (asociado a un siacutembolo no terminal) tiene tipo el valor devuelto tras una llamada se puede almacenar en una variable por si se desea utilizar en alguna accioacuten sintaacutectica para ello se escribe

valor = nombreSimbolo()

(siendo valor el nombre de la variable y nombreSimbolo el nombre del siacutembolo no terminal) De manera anaacuteloga el objeto de la clase Token representativo de la pieza sintaacutectica encontrada en la entrada por el analizador lexicograacutefico puede asignarse a una variable del mismo tipo para tener accesibles las ca -racteriacutesticas de la pieza por si se precisaran en alguna accioacuten sintaacutectica para ello en la parte de la especifi -cacioacuten sintaacutectica donde aparezca la pieza sintaacutectica se escribe

dato = lt nombrePieza gt

donde nombrePieza es el nombre de la pieza sintaacutectica y dato es el nombre de una variable de tipo Token en la que se anota el valor indicativo de las propiedades de la pieza sintaacutectica comunicada

Tambieacuten existe otra manera de acceder a las caracteriacutesticas de la pieza sintaacutectica que el analizador lexico -graacutefico comunica al analizador sintaacutectico en la clase del analizador sintaacutectico se tiene declarado el campo

[ static ] public Token token

22

cada vez que el analizador sintaacutectico recibe una pieza sintaacutectica comunicada por el analizador lexicograacutefico se deja anotada en el campo token y ese contenido no se altera hasta que sea proporcionada la siguiente pieza de la entrada

∆ Ejemplo 4

Se considera una ampliacioacuten del ejemplo de presentacioacuten ademaacutes del anaacutelisis de la entrada se pretende obtener una relacioacuten numerada de las variables y las constantes indicando el nuacutemero de liacutenea y el nuacuteme-ro de columna donde estaacuten situadas por ejemplo si en la entrada se tiene la expresioacuten (grabada en dos liacuteneas)

x (y + 11) + 22

en la salida se tendraacute

1- x linea 1 columna 1

2- y linea 1 columna 6

3- 11 linea 1 columna 10

4- 22 linea 2 columna 5

En la especificacioacuten que se propone como solucioacuten puede apreciarse- en la clase del analizador sintaacutectico se han incluido meacutetodos auxiliares en los que se usan los nom-bres image beginLine beginColumn que son campos de un objeto de clase Token

- en la especificacioacuten sintaacutectica de factor se emplea la variable pieza de tipo Token para reco-ger las caracteriacutesticas de las piezas sintaacutecticas detectadas- la variable numero hace de contador para numerar las constantes y variables

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin

static int numero = 0

private static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

private static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

private static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

23

PARSER_END (ExprMin)

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() Token pieza pieza = ltconstantegt numero++ grabarDatosPieza(numero pieza) | pieza = ltvariablegt numero++ grabarDatosPieza(numero pieza) | ( expresion() )

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP | n | t | r

Si se agrupan las dos acciones sintaacutecticas comunes la especificacioacuten de factor tambieacuten puede escri-birse de esta forma

void factor() Token pieza ( pieza = ltconstantegt | pieza = ltvariablegt

) numero++ grabarDatosPieza(numero pieza)

| ( expresion() )

24

Si se accede al campo token de la clase del analizador sintaacutectico se puede resolver este ejemplo pres-cindiendo de la variable pieza

void factor() ( ltconstantegt | ltvariablegt

) numero++ grabarDatosPieza(numero token)

| ( expresion() )

Tareas asociadas a las piezas sintaacutecticasYa se ha visto que es posible asociar bloques de coacutedigo Java a puntos de la estructura sintaacutectica para que se realicen ciertas operaciones cuando el analizador sintaacutectico pase por esa zona de la estructura Tambieacuten es posible asociar coacutedigo Java a las piezas sintaacutecticas para que el analizador lexicograacutefico efectuacutee determina-das tareas cuando detecte en la entrada analizada piezas que tengan asociado coacutedigo

loz Accioacuten ligada a una pieza sintaacutecticaSi se asocia un bloque de coacutedigo a una pieza sintaacutectica se consigue que cada vez que se detecta en la en-trada analizada la presencia de esa pieza se ejecute en ese preciso momento el coacutedigo del bloque asociado a la pieza Estas acciones se denominan ldquoacciones lexicograacuteficasrdquo (lexical actions) el coacutedigo del bloque se traslada literalmente al coacutedigo del analizador lexicograacutefico generado para que se ejecute oportunamente cuando se detecte la pieza ligada al bloque de coacutedigo La accioacuten lexicograacutefica se ejecuta inmediatamente antes que la pieza sea comunicada al analizador sintaacutectico

Se puede asociar una accioacuten lexicograacutefica a una pieza sintaacutectica declarada con TOKEN y tambieacuten a una se-cuencia de caracteres declarada con SKIP ademaacutes de eacutestas hay otras dos formas de declaraciones lexico-graacuteficas (no mencionadas en esta introduccioacuten) que tambieacuten pueden llevar una accioacuten asociada

En una especificacioacuten JavaCC la forma de una pieza sintaacutectica (o bien de una secuencia de caracteres) se define poniendo su descripcioacuten delimitada entre llaves detraacutes de la indicacioacuten TOKEN (o bien SKIP) si se quiere asociar una accioacuten lexicograacutefica a la pieza (o a la secuencia) hay que colocar el bloque de coacutedigo de la accioacuten a continuacioacuten de la descripcioacuten de la forma de la pieza (o de la secuencia) el bloque (delimi-tado por llaves) queda situado entre la descripcioacuten y la llave que cierra la especificacioacuten

La pieza sintaacutectica especial que representa el final del fichero (EOF) tambieacuten puede llevar asociada una ac-cioacuten lexicograacutefica para ello hay que poner

ltgt TOKEN lt EOF gt coacutedigo de la accioacuten asociada

para justificar la presencia de los caracteres ltgt delante de la palabra TOKEN hay que basarse en el con-cepto de ldquoestado lexicograacuteficordquo (cuya explicacioacuten queda fuera del alcance de esta introduccioacuten)

∆ Ejemplo 5

Se considera nuevamente el ejemplo de presentacioacuten como resultado del anaacutelisis de una expresioacuten se pretende obtener una relacioacuten de los pareacutentesis contenidos en la expresioacuten en el mismo orden en que se encuentran ademaacutes por cada salto de liacutenea del texto de entrada se grabaraacute en la salida el caraacutecter y

25

se pasaraacute a una nueva liacutenea tambieacuten se anotaraacute la frase Final del trabajo cuando se alcance el final del fichero de entrada Asiacute por ejemplo si la entrada analizada es

(a + b) (( x )) (y + 7)

los resultados obtenidos seraacuten

() (())()

Final del trabajo

En la especificacioacuten JavaCC que se propone como solucioacuten soacutelo se muestran las reglas sintaacutecticas y le -xicograacuteficas (en el resto no hay cambios) en esta solucioacuten puede apreciarse- las piezas sintaacutecticas correspondientes a los pareacutentesis se han definido de manera nominal para po-der asociarles una accioacuten lexicograacutefica- los caracteres incluidos en la pieza de tipo SKIP se han descompuesto en dos grupos ya que la ac-cioacuten lexicograacutefica incorporada soacutelo afecta a uno de ellos

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ltpAbrirgt expresion() ltpCerrargt

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

TOKEN

26

lt pAbrir ( gt Systemoutprint(()

TOKEN lt pCerrar ) gt Systemoutprint())

ltgtTOKEN lt EOF gt Systemoutprintln ( Final del trabajo )

SKIP n Systemoutprint( n)

SKIP | t | r

loz Bloque de declaraciones lexicograacuteficasCada definicioacuten de una pieza sintaacutectica puede llevar asociada una accioacuten lexicograacutefica que se ejecuta in-mediatamente despueacutes de detectar en la entrada un lexema que se ajusta a la definicioacuten de la pieza cada accioacuten lexicograacutefica es un bloque de coacutedigo Java que puede contener sus propias declaraciones Pero a ve-ces interesa tener declaraciones que sean compartidas por todas las acciones lexicograacuteficas con indepen-dencia de la pieza a la que esteacute asociada cada una de ellas

Para incluir declaraciones que puedan usarse desde el coacutedigo asociado a cualquiera de las piezas sintaacutecti -cas se dispone de una construccioacuten con el siguiente formato

TOKEN_MGR_DECLS

que consta de una palabra reservada seguida del siacutembolo dos puntos seguido de un bloque de coacutedigo Java (delimitado por llaves) esta declaracioacuten se puede colocar en cualquier parte de una especificacioacuten JavaCC si se incluye soacutelo puede ponerse una vez

Todas las declaraciones de variables o de meacutetodos puestas en el bloque de declaraciones lexicograacuteficas compartidas son accesibles desde el coacutedigo de cualquier accioacuten lexicograacutefica la justificacioacuten es sencilla el bloque de declaraciones se traslada al principio del coacutedigo del analizador lexicograacutefico generado y las ac-ciones lexicograacuteficas tambieacuten se trasladan a ese analizador

loz Declaraciones lexicograacuteficas predefinidasDe lo expuesto hasta ahora se aprecia que en una accioacuten lexicograacutefica asociada a una pieza sintaacutectica re-sultan accesibles

- las declaraciones de su propio bloque (si tiene declaraciones)- los componentes del bloque de declaraciones compartidas (si estaacute incluido en la especificacioacuten)

Ademaacutes de esto se tienen otras declaraciones pertenecientes a la clase TokenManager que constituye el analizador lexicograacutefico que tambieacuten son utilizables desde cualquiera de las acciones lexico-graacuteficas estas declaraciones pueden considerarse como ldquopredefinidasrdquo ya que son incorporadas automaacuteti-camente por el generador Entre estas declaraciones accesibles se encuentran las siguientes

[ static ] StringBuffer imagecontiene la cadena de caracteres representativa de la pieza sintaacutectica actual que se acaba de recono-cer en le entrada se trata de un campo de la clase y se declara como static cuando el analizador

27

se ha generado con la opcioacuten (por defecto) Static = true si se generase con la opcioacuten Sta-tic = false no quedariacutea declarada como static

Token matchedToken

variable de clase Token que representa la pieza sintaacutectica actual la que se acaba de encontrar en la entrada se trata de una variable local del meacutetodo al que se ha trasladado el coacutedigo de la accioacuten (y por ello accesible desde ese coacutedigo)

El campo image puede usarse tanto si la accioacuten lexicograacutefica estaacute asociada a una pieza declarada con TOKEN como si lo estaacute a una secuencia declarada con SKIP Sin embargo la variable matchedToken no puede usarse en una accioacuten asociada a un patroacuten especificado como SKIP el motivo es que lo declara-do como SKIP no constituye propiamente una pieza sintaacutectica ya que no se va a comunicar al analizador sintaacutectico

∆ Ejemplo 6

Se propone aquiacute una nueva solucioacuten para el ejemplo nordm 4 expuesto anteriormente

Esta nueva solucioacuten tal y como se expone aquiacute admite algunas mejoras ya que su pretensioacuten soacutelo es ser-vir de ilustracioacuten para algunos aspectos que se acaban de citar (maacutes adelante se propone otra solucioacuten al-ternativa)

En la especificacioacuten de la solucioacuten es posible apreciar esto- las acciones lexicograacuteficas pueden llevar sus propias declaraciones las variables nLineaVar nColumnaVar nLineaCte y nColumnaCte se declaran dentro de las acciones

- la variable matchedToken a la que se hace referencia en el coacutedigo de la accioacuten lexicograacutefica aso-ciada a una variable contiene la informacioacuten sobre la pieza sintaacutectica que se acaba de detectar una va-riable de la expresioacuten analizada anaacutelogamente con la referencia relativa a una constante- el nombre image que aparece en las acciones lexicograacuteficas se refiere al campo de la clase Expr-MinTokenManager (analizador lexicograacutefico) noacutetese que en efecto se comunica como un paraacuteme-tro de clase StringBuffer

- la variable numero se emplea para numerar las constantes y variables encontradas

- los nombres beginLine y beginColumn son campos (puacuteblicos) de la clase Token por ello se pueden aplicar a un objeto de esa clase

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

void unaExpresion()

28

expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n StringBuffer lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

TOKEN lt variable [a-z] gt

int nLineaVar nColumnaVar numero++ nLineaVar = matchedTokenbeginLine nColumnaVar = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaVar nColumnaVar)

TOKEN lt constante ( [0-9] ) + gt

int nLineaCte nColumnaCte

29

numero++ nLineaCte = matchedTokenbeginLine nColumnaCte = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaCte nColumnaCte)

SKIP | n | t | r

La especificacioacuten precedente se puede escribir de otra manera maacutes escueta como se indica a continua-cioacuten (soacutelo se muestran las modificaciones) sobre esta nueva especificacioacuten puede mencionarse- se han eliminado por innecesarias las variables declaradas dentro de las acciones lexicograacuteficas- el nombre image que aparece en el bloque de las declaraciones compartidas se refiere al campo de la clase Token noacutetese que en efecto se aplica a un objeto de esa clase y se comunica como un paraacute -metro de tipo String

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

TOKEN lt variable [a-z] gt

numero++ grabarDatosPieza(numero matchedToken)

TOKEN lt constante ( [0-9] ) + gt

numero++ grabarDatosPieza(numero matchedToken)

loz Accioacuten lexicograacutefica comuacutenSi hubiera que realizar la misma tarea tras la deteccioacuten de todas y cada una de las piezas sintaacutecticas una posibilidad incoacutemoda seriacutea repetir el mismo coacutedigo en todas las acciones lexicograacuteficas para evitar tanta reiteracioacuten se tiene la opcioacuten Common_Token_Action su valor por defecto es false pero si se le asigna el valor true es posible definir una ldquoaccioacuten lexicograacutefica comuacutenrdquo que se escribe una uacutenica vez y se aplica a todas las piezas sintaacutecticas

30

Cuando la opcioacuten Common_Token_Action se establece con el valor true es obligado incluir el blo-que de declaraciones lexicograacuteficas compartidas (TOKEN_MGR_DECLS) Ademaacutes en ese bloque se preci-sa la definicioacuten de un meacutetodo cuyo encabezamiento sea

[ static ] void CommonTokenAction(Token t)

el coacutedigo de este meacutetodo constituye la accioacuten lexicograacutefica comuacuten que se ejecutaraacute despueacutes de la deteccioacuten de cualquier pieza sintaacutectica el meacutetodo tiene un paraacutemetro de tipo Token mediante el que se puede co-municar la informacioacuten correspondiente a la pieza que se acaba de encontrar en la entrada analizada

Si una pieza sintaacutectica tiene asociada una accioacuten lexicograacutefica propia la accioacuten lexicograacutefica comuacuten (si es-taacute definida) se ejecutaraacute inmediatamente despueacutes de realizar la accioacuten propia

La accioacuten lexicograacutefica comuacuten se aplica a todas las piezas sintaacutecticas definidas como TOKEN bien sea no-minalmente (en las reglas lexicograacuteficas) o bien anoacutenimamente (incorporadas dentro de las reglas sintaacutecti-cas) sin embargo no se aplica cuando la secuencia acoplada se corresponde con una expresioacuten regular es-pecificada como SKIP (noacutetese que por el contrario una especificacioacuten SKIP siacute puede tener asociada una accioacuten lexicograacutefica propia)

El coacutedigo del meacutetodo CommonTokenAction se traslada como un componente maacutes del bloque de decla-raciones compartidas (TOKEN_MGR_DECLS) al principio de la clase del analizador lexicograacutefico

∆ Ejemplo 7

Se pretende ahora ampliar el ejemplo de presentacioacuten con la obtencioacuten de una relacioacuten numerada de los lexemas de las piezas sintaacutecticas encontradas en la entrada analizada en el caso de las variables y de las constantes antes del lexema se mostraraacute un indicativo (Var Const) por ejemplo si la expresioacuten de la entrada es

( a + 987 ) b

se grabaraacute la salida

( 1Var a 2+ 3Const 987 4) 5 6Var b 7 8

En la solucioacuten que se propone a continuacioacuten se muestran la definicioacuten de las opciones y las reglas sin-taacutecticas y lexicograacuteficas (en el resto no hay modificaciones) puede resaltarse lo siguiente- las llamadas al meacutetodo CommonTokenAction se incorporan automaacuteticamente no hay que incluir-las en la especificacioacuten como paraacutemetro de llamada a este meacutetodo siempre se emplea el objeto que re -presenta la pieza que se acaba de detectar- la accioacuten lexicograacutefica comuacuten realiza la actualizacioacuten del contador de piezas y la grabacioacuten del lexe-ma de la pieza detectada con su nuacutemero de orden- la accioacuten lexicograacutefica comuacuten tambieacuten se aplica a las piezas sintaacutecticas anoacutenimas (pareacutentesis y ope-radores) y a la pieza especial que representa el final del fichero (EOF)

- las piezas sintaacutecticas ltconstantegt y ltvariablegt tienen asociada una accioacuten lexicograacutefica propia que se ejecuta antes de la accioacuten lexicograacutefica comuacuten- los caracteres que se saltan (SKIP) no estaacuten afectados por la accioacuten lexicograacutefica comuacuten

options Ignore_Case = true Common_Token_Action = true

31

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarIndicativo(String indicativo) Systemoutprint(indicativo + )

static void grabarLexema (int num String lexema) Systemoutprint(lexema + + num + n)

static void CommonTokenAction(Token pieza) numero++ grabarLexema(numero piezaimage)

TOKEN lt variable [a-z] gt

grabarIndicativo(Var)

32

TOKEN lt constante ( [0-9] ) + gt

grabarIndicativo(Const)

SKIP | n | t | r

A modo de ejemplo de aplicacioacuten de la accioacuten lexicograacutefica comuacuten en lo que sigue se ofrece una solucioacuten para la grabacioacuten (en el fichero predefinido de salida) del texto analizado con las liacuteneas numeradas

∆ Ejemplo 8 [ Listado del texto analizado ]Se desea completar el ejemplo de presentacioacuten con la grabacioacuten de un listado con las liacuteneas numeradas del texto analizado (noacutetese que una expresioacuten puede escribirse ocupando varias liacuteneas) No se muestra la especificacioacuten sintaacutectica debido a que no hay ninguna modificacioacuten en ella Los aspectos resentildeables de la solucioacuten que se propone son- se usa la variable numLin como contador de liacuteneas

- en la variable linea se van yuxtaponiendo los lexemas de las piezas sintaacutecticas de una liacutenea esta yuxtaposicioacuten se realiza mediante la accioacuten lexicograacutefica comuacuten- las secuencias reconocidas con los patrones especificados como SKIP no implican la ejecucioacuten de la accioacuten lexicograacutefica comuacuten por ello su lexema (almacenado en la variable image) ha de yuxtaponer-se expliacutecitamente- hay que separar el patroacuten correspondiente al fin de liacutenea para detectar el momento en que ha de gra-barse la liacutenea completa cuyo final se ha alcanzado

options Ignore_Case = true Common_Token_Action = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(nnAnalisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

TOKEN_MGR_DECLS static int numLin = 0 static String linea =

33

static void CommonTokenAction(Token pieza) linea = linea + piezaimage

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP lt | t | r gt linea = linea + image

SKIP n numLin++ Systemoutprintln(numLin + + linea) linea =

A modo de recapitulacioacutenↄ Accioacuten lexicograacutefica Accioacuten sintaacutectica

bull accioacuten lexicograacutefica bloque de coacutedigo Java asociado a una pieza sintaacutectica nominal (TOKEN) o a una secuencia de caracteres (SKIP) se ejecuta cuando se detecta en la entrada la pieza o la secuencia corres-pondiente no se puede aplicar a piezas sintaacutecticas anoacutenimas (autodefinidas con comillas)bull accioacuten sintaacutectica bloque de coacutedigo Java asociado a un punto de la estructura sintaacutectica (el punto indica-do por el sitio de la produccioacuten donde se inserta el bloque) se ejecuta cuando el anaacutelisis de la entrada pa-sa por ese punto de la estructura

ↄ Valor de una pieza sintaacutectica comunicadaEl analizador lexicograacutefico comunica al analizador sintaacutectico una pieza a traveacutes de un valor de la clase Token (fichero Tokenjava) un objeto de la clase Token representa las caracteriacutesticas de la pieza comunicada En la clase Token entre otros se tienen los campos

String imagelexema de la pieza

int beginLine beginColumn endLine endColumnposicioacuten del lexema en el fichero analizado (indicada por los nuacutemeros de fila y de columna de su comienzo y de su terminacioacuten)

ↄ Valor asociado a un siacutembolo

34

En las producciones que especifican la sintaxis se pueden incluir asignaciones que sirven para dejar ano-tado el valor asociado a un siacutembolo

bull siacutembolo no terminal valor = nombreSimbolo()el meacutetodo de anaacutelisis asociado a nombreSimbolo se ha declarado de un cierto tipo (distinto del tipo void) valor ha de ser una variable del mismo tipo que el meacutetodo

bull siacutembolo terminal dato = lt nombrePieza gtel valor asignado es el valor de la clase Token representativo de la pieza sintaacutectica (nominal) dato ha de ser una variable de tipo Token

ↄ Miradas sobre la pieza sintaacutectica comunicadaLa pieza sintaacutectica comunicada puede mirarse desde cada uno de los dos analizadores

bull analizador sintaacutecticola pieza comunicada (desde el analizador lexicograacutefico) se tiene anotada en un campo de la clase del analizador sintaacutectico declarado como

(static) public Token token

este valor es accesible desde el coacutedigo de todos los meacutetodos de anaacutelisis sintaacutectico el contenido del campo variacutea cuando se recibe una nueva pieza sintaacutectica

bull analizador lexicograacuteficola pieza dispuesta para ser comunicada (al analizador sintaacutectico) se tiene anotada en una variable local declarada como

Token matchedToken

este valor es accesible para el coacutedigo de todas las acciones lexicograacuteficas

ↄ El lexema de la pieza comunicadaEl lexema de la pieza sintaacutectica comunicada se encuentra disponible en dos sitios distintos que compar-ten el mismo nombre pero que son de distinto tipo y se usan de distinta manera

bull String image

es un campo de la clase TOKEN puede consultarse en relacioacuten con un objeto de esa clase que esteacute disponible

bull StringBuffer image

es un campo de la clase del analizador lexicograacutefico ( ∙ ∙ ∙ ∙ TokenManagerjava) es accesible desde el coacutedigo de la clase y por lo tanto desde todas las acciones lexicograacuteficas (bloques de coacute-digo que estaacuten incorporados al analizador lexicograacutefico)

se trata de un valor disponible tanto para las piezas sintaacutecticas nominales (TOKEN) como para las secuencias que se saltan (SKIP)

ↄ Resumen relativo a la lexicografiacutea

TOKEN pieza sintaacutectica nominal∙ ∙ ∙ pieza sintaacutectica anoacutenimaSKIP secuencia de caracteres que no forman pieza

35

Precisiones sobre el analizador sintaacutectico generadoPuede decirse que en su funcionamiento maacutes sencillo cuando no se emplean las posibilidades de examen por adelantado (ldquolookaheadrdquo) JavaCC comprueba que las reglas sintaacutecticas cumplen la condicioacuten LL(1) y generan un analizador descendente-predictivo-recursivo Pero esta apreciacioacuten hay que matizarla ya que el analizador sintaacutectico generado no se ajusta en sentido estricto al modelo de analizador LL(1)

En el modelo de anaacutelisis LL(1) el orden del examen de las distintas alternativas de expansioacuten de un siacutembo-lo no terminal es indiferente ya que se conocen los siacutembolos directores de cada una de ellas (que resultan ser conjuntos disjuntos) en la implementacioacuten del subprograma de anaacutelisis asociado al siacutembolo no terminal ltNombreSimbgt cuyas producciones son

ltNombreSimbgt = α1

| α2

∙ ∙ ∙

| αn

se podriacutea preguntar en cualquier orden si la pieza actual pertenece al conjunto de siacutembolos directores de las distintas reglas Sin embargo en el analizador sintaacutectico generado por JavaCC la implementacioacuten del meacute-todo asociado al siacutembolo no terminal ltNombreSimbgt no sigue esa pauta

Si las anteriores reglas sintaacutecticas se transcriben en JavaCC de la forma

void nombreSimb () α1-jcc

| α2-jcc

∙ ∙ ∙

| αn-jcc

1

accioacutenlexicograacutefica

comuacuten

Commom -TokenAction2

accioacutenlexicograacutefica

propia

(accioacuten ligadaa una pieza)3

bloque dedeclaracioneslexicograacuteficas

TOKEN_MGR_DECLS4

declaracioneslexicograacuteficaspredefinidas

(disponibilidad

de uso)TOKENsisisisi∙ ∙ ∙sino--SKIPnosisisi

Las indicaciones si de las columnas 3 y 4 son consecuencia de las indicaciones de la columna 2

La accioacuten lexicograacutefica comuacuten se ejecuta despueacutes de la accioacuten lexicograacutefica propia

36

el meacutetodo de anaacutelisis generado procede de la siguiente manera

se van considerando las alternativas sucesivamente en el mismo orden en el que estaacuten especifica-das primero la de α1 (α1-jcc) despueacutes la de α2 (α2-jcc) etc

para cada alternativa seleccionada se comprueba si la pieza por adelantado coincide con alguno de los siacutembolos iniciales de la parte derecha y si hay coincidencia se prosigue el anaacutelisis con ella si no hay coincidencia se pasa a la alternativa siguiente

siguiendo con este orden si se llega al final de las alternativas sin haberse encontrado coincidencia alguna se produce un error sintaacutectico

En el analizador generado por JavaCC hay una peculiaridad que no se tiene en los analizadores LL(1) es-trictamente considerados la palabra vaciacutea siacute se considera como posible siacutembolo inicial de la parte derecha una regla (esto ocurre siempre que la parte derecha es anulable) De esta peculiaridad se deriva una conse-cuencia que ha de tenerse en cuenta al escribir la especificacioacuten dado que siempre es posible considerar la palabra vaciacutea como la siguiente pieza por adelantado si se llega a examinar la alternativa correspondiente a una produccioacuten anulable el analizador generado siempre seleccionaraacute esa alternativa con independencia de la siguiente pieza que esteacute presente en la entrada que queda por analizar

Asiacute pues si alguna de las producciones que definen un siacutembolo no terminal es anulable es importante el or-den en que se escriben las alternativas en una especificacioacuten sintaacutectica JavaCC el funcionamiento del ana-lizador generado siacute depende de la colocacioacuten elegida para las reglas

Para ilustrar estas peculiaridades del analizador sintaacutectico generado por JavaCC a continuacioacuten se expone un ejemplo

∆ Ejemplo 9

Se quiere obtener un analizador leacutexico-sintaacutectico de textos que se ajustan al siguiente formato una se-cuencia de uno o maacutes nombres seguida del siacutembolo igual seguido de un nuacutemero entero si la secuencia tiene maacutes de un nombre han de estar separados bien por una coma en todos los casos o bien por el siacutem-bolo dos puntos en todos los casos esto es son correctas las secuencias

uno dos tres = 123exclusivo = 0Abajo Arriba = 57

y son incorrectas las secuenciasuno dos tres = 123

exclusivo inclusive = 0

fuera dentro = 56

Una gramaacutetica que define las secuencias con este formato es

ltListagt = nombre ltOtrosNombresgt = numero

ltOtrosNombresgt = nombre nombre | nombre nombre | ε

se trata de una gramaacutetica que cumple la condicioacuten LL(1)- el siacutembolo director para la alternativa vaciacutea es el siacutembolo igual es evidente pues que las tres reglas de ltOtrosNombresgt tienen siacutembolos directores distintos

37

- la decisioacuten de abandonar la repeticioacuten del anaacutelisis de un nuevo nombre (en las dos primeras alterna-tivas de ltOtrosNombresgt) se puede tomar ante la presencia del siacutembolo igual

En la implementacioacuten de un analizador sintaacutectico LL(1) en sentido estricto la seleccioacuten de la alternativa vaciacutea para expandir el siacutembolo ltOtrosNombresgt se hariacutea tras comprobar expliacutecitamente que la pie-za sintaacutectica por adelantado es el siacutembolo igual

El funcionamiento del analizador generado por JavaCC depende del orden de colocacioacuten de las reglas A continuacioacuten se exponen varias posibilidades de especificacioacuten y se comenta el analizador generado en cada caso

bull Especificacioacuten primeraSe transcriben literalmente las reglas tal y como se han escrito antes en la notacioacuten BNF noacutetese que para poder codificar la parte derecha de la regla vaciacutea ha de incluirse una accioacuten sintaacutectica vaciacutea

PARSER_BEGIN (Igualdad)

public class Igualdad

public static void main (String[] argum) throws ParseException Igualdad analisis = new Igualdad(Systemin) analisissecuencia() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (Igualdad)

void secuencia() lista() ltEOFgt

void lista() lt nombre gt otrosNombres() = lt numero gt

void otrosNombres() lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt ) |

TOKEN lt nombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN lt numero ( [ 0 - 9 ] )+ gt

SKIP | r | n

38

El analizador generado funciona correctamente puesto que en el meacutetodo otrosNombres se selec-ciona la alternativa vaciacutea despueacutes de haber comprobado que la pieza por adelantado no es siacutembolo ini-cial de ninguna de las dos alternativas precedentes (no es una coma ni un siacutembolo dos puntos)

bull Especificacioacuten segundaSe considera la misma especificacioacuten anterior pero cambiando el orden de colocacioacuten de las reglas ahora la produccioacuten vaciacutea se coloca como primera alternativa (soacutelo se muestran las partes modifica-das)

void otrosNombres() | lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt )

Con la entrada de esta especificacioacuten el generador JavaCC produce un mensaje en el que se avisa de que las dos alternativas que estaacuten colocadas detraacutes de la alternativa vaciacutea nunca podraacuten seleccionarse aun asiacute se genera un analizador Pero es un analizador que funciona incorrectamente asiacute por ejemplo la entrada

mxa nyz = 57

se considera incorrecta despueacutes de visto el primer nombre se intenta aplicar la produccioacuten vaciacutea (se espera encontrar el siacutembolo igual) pero habriacutea que aplicar la regla cuyo siacutembolo director es la coma

bull Especificacioacuten terceraSe utilizan las posibilidades de la notacioacuten BNF-Ampliada para prescindir de la especificacioacuten expliacuteci-ta de la palabra vaciacutea se aprovecha que JavaCC admite el operador + para indicar repeticiones de una o maacutes veces la alternativa vaciacutea estaacute impliacutecitamente contemplada en la posibilidad de cero repeti-ciones que contempla el operador (soacutelo se muestran las partes modificadas en la especificacioacuten)

void otrosNombres() ( lt nombre gt )+ | ( lt nombre gt )

El meacutetodo otrosNombres() generado hace las comprobaciones en el siguiente orden

- se comprueba si la pieza por adelantado es una coma (siacutembolo inicial de la primera alternativa) y en este caso se selecciona la primera regla- si no es una coma se comprueba si es un siacutembolo dos puntos (siacutembolo inicial de la segunda alter-nativa) y en este caso se selecciona la segunda regla- si no es un siacutembolo dos puntos se considera la palabra vaciacutea como siacutembolo inicial (siempre posi-ble impliacutecitamente) para la segunda alternativa en este caso para que no haya error la pieza por adelantado ha de ser un siacutembolo igual

Se trata pues de un analizador que funciona correctamente

bull Especificacioacuten cuarta

39

Se pone la misma especificacioacuten anterior pero cambiando el orden de consideracioacuten de la regla vaciacutea ahora se incluye impliacutecitamente en la primera alternativa (soacutelo se muestran las partes modificadas)

void otrosNombres() ( lt nombre gt ) | ( lt nombre gt )+

Para esta especificacioacuten el generador JavaCC emite un aviso en el que se indica que la segunda alter -nativa nunca se seleccionaraacute no obstante se genera un analizador que no funciona como se supone que deberiacutea hacerlo por ejemplo el anaacutelisis de la entrada

nyz mxa = 1

produce un error despueacutes de tratado el primer nombre se intenta aplicar la produccioacuten vaciacutea (corres-pondiente a la repeticioacuten de cero veces contemplada en la primera regla) por lo que no se encuentra el esperado siacutembolo igual

40

Page 12: JavaCC

| [ β ]

Como ejemplo sea la especificacioacuten sintaacutectica de una llamada a una funcioacuten que puede tener cero o maacutes paraacutemetros separados por comas aunque no haya paraacutemetros los pareacutentesis estaacuten presentes Las siguien-tes reglas definen esta estructura

ltLlamadagt = id ( ltParametrosgt )

ltParametrosgt = ltExpresiongt ltExpresiongt | ε

En JavaCC la definicioacuten del siacutembolo ltParametrosgt se puede escribir como sigue (noacutetese que aquiacute los pareacutentesis empleados para aplicar el operador son metasiacutembolos)

void parametros() [ expresion() ( expresion() ) ]

loz Seccioacuten de lexicografiacuteaEn esta seccioacuten se indica la lexicografiacutea del lenguaje para el que se va a generar el analizador la notacioacuten de JavaCC que representa la forma de cada una de las piezas sintaacutecticas es una variante de las bien conoci-das expresiones regulares

bull El nombre de las piezas sintaacutecticas nominalesPara asociar un nombre a una pieza sintaacutectica y para al mismo tiempo definir la forma de la pieza se em-plea la notacioacuten

TOKEN lt nombreTerminal Expresioacuten-Regular gt

- TOKEN es una palabra reservada detraacutes de ella se pone el separador dos puntos

- nombreTerminal representa el nombre que se quiere asociar a la pieza sintaacutectica

- Expresioacuten-Regular indica una expresioacuten regular que especifica la forma de la pieza

- el nombre y la expresioacuten se separan mediante el caraacutecter dos puntos y conjuntamente se deli -mitan mediante los caracteres lt y gt a su vez toda la asociacioacuten se delimita con llaves

bull Expresiones regulares en JavaCCLas expresiones regulares en una especificacioacuten JavaCC se escriben de manera parecida a la forma utiliza-da en otras conocidas notaciones aunque con ciertas peculiaridades A continuacioacuten se mencionan los as-pectos maacutes significativos de las expresiones regulares JavaCC y algunos ejemplos

Los operadores de las expresiones son

| unioacuten

repeticioacuten cero o maacutes veces

+ repeticioacuten una o maacutes veces

opcionalidad

11

Los pareacutentesis permiten fijar el orden de aplicacioacuten de los operadores Una peculiaridad de las expresiones regulares JavaCC es que los operadores de repeticioacuten y de opcionalidad requieren que el componente al que se aplican esteacute en todo caso delimitado por pareacutentesis

Se pueden citar las siguientes caracteriacutesticas baacutesicas de las expresiones regulares en JavaCC

Se tiene la posibilidad de definir conjuntos de caracteres poniendo entre corchetes la relacioacuten de carac-teres separados por comas y cada caraacutecter delimitado por comillas En un conjunto se puede fijar un ran-go de caracteres poniendo el primero y el uacuteltimo separados por el siacutembolo guioacuten (-) A veces resulta maacutes coacutemodo indicar los caracteres que no pertenecen al conjunto para ello se puede definir el comple -mentario de un conjunto poniendo el siacutembolo ~ delante del corchete inicial

Para indicar que una secuencia de caracteres colocados consecutivamente constituye por siacute misma una expresioacuten regular (o un componente de una expresioacuten) se escribe la secuencia delimitada por comillas El siacutembolo se emplea como metacaraacutecter para indicar que el siacutembolo que estaacute inmediatamente a su derecha se considere como caraacutecter del alfabeto no como posible metacaraacutecter (operador delimitador)

En una expresioacuten regular se pueden incluir como si fueran caracteres las secuencias de escape con la misma notacioacuten y el mismo significado que en Java o en C

Noacutetese que en una produccioacuten sintaacutectica la opcionalidad se puede especificar de dos formas con corchetes o con el siacutembolo de interrogacioacuten sin embargo en una expresioacuten regular la opcionalidad soacutelo se puede es-pecificar mediante el siacutembolo de interrogacioacuten (los corchetes en las expresiones regulares son para definir conjuntos de caracteres)

A continuacioacuten se muestran ejemplos de expresiones regulares escritas seguacuten la notacioacuten JavaCC

ENDsecuencia de las tres letras mayuacutesculas que conforman la palabra END

whilesecuencia de las cinco letras minuacutesculas que conforman la palabra while

ncaraacutecter representativo del fin de liacutenea (secuencia de escape)

rcaraacutecter representativo del retorno de carro (secuencia de escape)

caraacutecter dos puntos

=secuencia de dos caracteres (siacutembolo de asignacioacuten en Pascal)

gtcaraacutecter representativo del operador de relacioacuten mayor

(i | I)(f | F)palabra ldquoifrdquo escrita con letras minuacutesculas o mayuacutesculas (cuatro combinaciones)

if | IFpalabra ldquoifrdquo escrita soacutelo con minuacutesculas o soacutelo con mayuacutesculas (dos combinaciones)

12

u | o | i | e | auna cualquiera de las vocales minuacutesculas (pero soacutelo una de ellas)

[ A E I O U ]una cualquiera de las vocales mayuacutesculas (pero soacutelo una de ellas)

[ a - z ntilde ]una cualquiera de las letras minuacutesculas incluyendo la letra ntilde

[ a - z A - Z 0 - 9 ]una cualquiera de las letras o de las cifras decimales

([ 0 - 9 ])+constante entera (nuacutemero de una o maacutes cifras)

noacutetese que es incorrecto poner [ 0 - 9 ]+

( + | - )([ 0 - 9 ])+constante entera precedida opcionalmente por un signo

noacutetese que la primera cifra ha de seguir inmediatamente al signo (si existe)

([+ -])([ 0 - 9 ])+representa lo mismo que la expresioacuten anterior

[ a - z ] ([ a - z 0 - 9 ])una letra minuacutescula seguida de cero o maacutes letras minuacutesculas o cifras decimales

~[a - z ntilde A - Z Ntilde]cualquier caraacutecter que no sea una letra

noacutetese que la expresioacuten representa un uacutenico caraacutecter

~[n r]cualquier caraacutecter (un uacutenico caraacutecter) excepto el fin de liacutenea y el retorno de carro

~[ ]cualquier caraacutecter del alfabeto (un uacutenico caraacutecter)

([ 0 - 9 ])+ ( ([ 0 - 9 ])+)constante aritmeacutetica con parte decimal opcional

Los modernossecuencia de catorce caracteres Los modernos

noacutetese el uso de para imponer la condicioacuten de caraacutecter del siacutembolo comillas

nsecuencia de cuatro caracteres n

noacutetese que aquiacute no se representa la secuencia de escape

bull Piezas sintaacutecticas internas (ldquoprivaterdquo)

13

Es posible definir piezas sintaacutecticas auxiliares llamadas ldquointernasrdquo porque no se comunican al analizador sintaacutectico sino que soacutelo sirven para facilitar la escritura de la forma de las piezas sintaacutecticas que siacute se co-munican con el uso de las piezas internas es posible evitar repeticiones de escritura y hacer maacutes legible la especificacioacuten de las caracteriacutesticas lexicograacuteficas Las piezas internas permiten dar nombre a subexpre-siones regulares que a su vez pueden emplearse como componentes para escribir expresiones maacutes com-plejas que incorporan esos nombres Para indicar que una pieza sintaacutectica se considere como interna se pone el siacutembolo delante del nombre asociado a la pieza

A continuacioacuten se muestra un ejemplo tiacutepico de utilizacioacuten de las piezas internas en una especificacioacuten le-xicograacutefica en JavaCC Se quieren definir tres piezas sintaacutecticas que representan un nombre una constante entera y una constante decimal una posible especificacioacuten es

TOKEN ltnombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN ltcteEntera ( [ 0 - 9 ] )+ gt

TOKEN ltcteDecimal ( [ 0 - 9 ] )+ ( [ 0 - 9 ] )+ gt

Con la inclusioacuten de tres piezas sintaacutecticas internas auxiliares esta misma especificacioacuten podriacutea escribirse de la siguiente manera

TOKEN lt letra [ a - z A - Z ntilde Ntilde ] gt

TOKEN lt cifra [ 0 - 9 ] gt

TOKEN lt numero ( ltcifragt )+ gt

TOKEN lt nombre ( ltletragt )+ gt

TOKEN lt cteEntera ltnumerogt gt

TOKEN lt cteDecimal ltnumerogt ltnumerogt gt

Los nombres letra cifra y numero son exclusivos (internos) del analizador lexicograacutefico y no se pueden emplear como nombres de piezas en la especificacioacuten de las reglas sintaacutecticas cuando esos nom-bres se usan en la definicioacuten de otras piezas han de escribirse delimitados por los siacutembolos lt y gt

bull Distincioacuten restringida entre minuacutesculas y mayuacutesculasSe tiene la opcioacuten Ignore_Case para determinar si en el anaacutelisis del texto de entrada se distingue entre las letras minuacutesculas y las mayuacutesculas el valor por defecto false indica que siacute se diferencian si se asig-na el valor true a esta opcioacuten entonces no se diferencian en ninguacuten caso con independencia de que en los patrones que se definan para las piezas sintaacutecticas se pongan minuacutesculas o mayuacutesculas

Pero a veces conviene que la diferenciacioacuten entre minuacutesculas y mayuacutesculas no se aplique de manera ge-neral a toda la entrada sino en particular a las secuencias de caracteres que se acoplen a ciertos patrones Para este fin existe la posibilidad de establecer que en la expresioacuten regular que define el patroacuten de una determinada pieza sintaacutectica no se diferencie entre minuacutesculas y mayuacutesculas ello se consigue poniendo

14

[ IGNORE_CASE ]

inmediatamente detraacutes de la palabra TOKEN con la que empieza la especificacioacuten de la pieza sintaacutectica en la que se pretende la no distincioacuten entre minuacutesculas y mayuacutesculas

Noacutetese que ignore_case tiene dos significados

- es el nombre de un paraacutemetro de la seccioacuten de opciones (puede ponerse en minuacutesculas o mayuacutesculas)- es una palabra reservada que puede aplicarse (escribieacutendola entre corchetes y en mayuacutesculas) a la de -finicioacuten de una pieza sintaacutectica

Por ejemplo sea un lenguaje que en general distingue entre minuacutesculas y mayuacutesculas pero en la codifi -cacioacuten del exponente de una constante real exponencial puede ponerse la letra e en minuacutescula o en ma-yuacutescula En la especificacioacuten JavaCC para ese lenguaje la opcioacuten Ignore_Case ha de tomar el valor false en la definicioacuten de una constante exponencial son posibles las siguientes formas equivalentes

1) sin hacer uso de IGNORE_CASE

TOKEN lt cifras ( [ 0 - 9 ] )+ gt

TOKEN lt signo [+ -] gt

TOKEN lt constante

ltcifrasgt ( ltcifrasgt) [e E] (ltsignogt) ltcifrasgt gt

2) aplicando IGNORE_CASE

TOKEN lt cifras ( [ 0 - 9 ] )+ gt

TOKEN lt signo [+ -] gt

TOKEN [IGNORE_CASE] lt constante

ltcifrasgt ( ltcifrasgt) e (ltsignogt) ltcifrasgt gt

bull Secuencias de caracteres sin efecto en la estructura sintaacutecticaEn los lenguajes de programacioacuten suele haber secuencias de caracteres que no forman parte de la estructu-ra sintaacutectica de un programa esas secuencias son detectadas por el analizador lexicograacutefico pero no son comunicadas al sintaacutectico no han de considerarse como piezas sino que han de saltarse En JavaCC se pueden definir estas secuencias mediante la palabra reservada SKIP la notacioacuten de una especificacioacuten con la palabra SKIP de una secuencia que ha de saltarse es la misma que la notacioacuten usada para las espe-cificaciones con la palabra TOKEN de las piezas que han de comunicarse

Los dos casos maacutes habituales de secuencias que no se comunican al analizador sintaacutectico son

a) los caracteres separadores que no tienen significado en el texto analizadoen los lenguajes de codificacioacuten en formato libre hay caracteres del texto fuente que han de ignorarse en la mayoriacutea de los casos estos caracteres son el espacio en blanco el tabulador el retorno de carro y el fin de liacutenea esta caracteriacutestica se especifica

SKIP |t | r |n

b) los comentarios

15

en el anaacutelisis sintaacutectico de un programa los comentarios han de ignorarse una manera de especificar esta circunstancia es poniendo en una definicioacuten SKIP la expresioacuten regular que denota la forma de los comentarios del lenguaje por ejemplo para los comentarios de una liacutenea de Java se pondriacutea

SKIP

lt (~ [n r]) gt

Tareas asociadas a la estructura sintaacutecticaJavaCC genera un analizador sintaacutectico descendente recursivo constituido por un conjunto de meacutetodos ca-da siacutembolo no terminal de la gramaacutetica sintaacutectica tiene asociado un meacutetodo de anaacutelisis (el meacutetodo tiene el mismo nombre que el siacutembolo correspondiente) Un meacutetodo de anaacutelisis ademaacutes de la realizacioacuten de la parte del anaacutelisis sintaacutectico que le corresponde puede llevar a cabo otras tareas complementarias que sirvan para efectuar un anaacutelisis maacutes complejo del texto de entrada

loz Bloque para un siacutemboloAl describir la forma de las reglas sintaacutecticas de un siacutembolo no terminal en una especificacioacuten JavaCC ya se ha visto que detraacutes de la definicioacuten del nombre del siacutembolo y antes de empezar con las diferentes alter-nativas que tiene asociadas se incluye un bloque de coacutedigo Java (en el ejemplo de presentacioacuten este bloque estaacute vaciacuteo en todos los siacutembolos no terminales de la gramaacutetica) Cada bloque situado en esa posicioacuten puede considerarse ligado al siacutembolo no terminal en cuya descripcioacuten estaacute incluido el coacutedigo Java del bloque se traslada literalmente al principio del meacutetodo generado correspondiente al siacutembolo no terminal y por lo tan-to es lo primero que se considera (se ejecuta) cuando en el transcurso del proceso de anaacutelisis se realiza una llamada al meacutetodo Asiacute pues cada siacutembolo no terminal siempre tiene asociado un uacutenico bloque de coacute-digo Java ndashbloque de presencia obligada aunque pueda estar vaciacuteo- en este bloque se pueden poner de-claraciones y sentencias

loz Acciones sintaacutecticasAdemaacutes del bloque asociado a cada uno de los siacutembolos no terminales de la gramaacutetica se pueden incluir otros bloques de coacutedigo Java intercalados en cualquiera de las partes derechas de las reglas sintaacutecticas Los bloques incluidos en las partes derechas se denominan ldquoacciones sintaacutecticasrdquo (parser actions) ya que son trozos de coacutedigo que pueden considerarse asociados a los puntos de la estructura sintaacutectica donde estaacuten co-locados Una accioacuten sintaacutectica se puede poner en cualquier posicioacuten al principio al final o en el medio de la parte derecha de una regla precedida o seguida de siacutembolos terminales o no terminales el coacutedigo Java de una accioacuten sintaacutectica se traslada literalmente al meacutetodo asociado al siacutembolo no terminal de la parte izquierda de la regla de manera que se ejecuta cada vez que el analizador transcurre por el punto de la estructura sintaacutectica donde se ha incorporado la accioacuten (esto es las acciones sintaacutecticas se incorporan al coacutedigo del analizador sintaacutectico) No obstante su nombre en los bloques de las acciones sintaacutecticas suele ponerse coacutedigo dedicado a la realizacioacuten de tareas semaacutenticas

Si en el bloque asociado a un siacutembolo no terminal se ponen declaraciones los nombres ahiacute definidos son accesibles en todas las acciones sintaacutecticas intercaladas en las partes derechas de las reglas de ese siacutembolo la justificacioacuten es sencilla el coacutedigo del bloque asociado al siacutembolo se incluye al principio de un meacutetodo y el coacutedigo de las acciones pertenece a ese mismo meacutetodo

∆ Ejemplo 1

Se considera de nuevo el ejemplo de presentacioacuten ahora se pretende completar el anaacutelisis leacutexico-sintaacutec-tico con la realizacioacuten de las siguientes tareas

- cada vez que se analiza una expresioacuten un teacutermino o un factor se graba el nombre del siacutembolo no terminal correspondiente y

16

- cuando se analiza un teacutermino en el que hay operadores multiplicativos se cuenta su nuacutemero y se graba el resultado (noacutetese que se cuentan los operadores de cada teacutermino pero no se acumula la cuenta de todos los operadores de la expresioacuten)

Para la realizacioacuten de estas tareas se antildeaden bloques de coacutedigo Java En la especificacioacuten que se describe a continuacioacuten puede apreciarse- para los siacutembolos no terminales expresion termino y factor se ha incluido en los blo-ques correspondientes la operacioacuten de grabacioacuten de su nombre que se ejecutaraacute cada vez que se efec-tuacutee una llamada al meacutetodo- en el bloque asociado a termino se tiene la declaracioacuten del contador de operadores (que seraacute una variable local del meacutetodo)- en la parte derecha de la regla que define la estructura de un teacutermino se han incluido dos acciones sintaacutecticas una para contar la cantidad de operadores (situada detraacutes de la pieza sintaacutectica del opera-dor) y otra para grabar la cantidad de operadores del teacutermino (situada al final de la parte derecha)- se ha definido el meacutetodo grabar para facilitar la escritura del coacutedigo Java

options Ignore_Case = true

PARSER_BEGIN (ExprMin) public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

private static void grabar(String nombre) Systemoutprintln ( -gt + nombre + n)

PARSER_END (ExprMin)

void unaExpresion()

expresion() ltEOFgt

void expresion() grabar(Expresion)

termino() ( + termino() )

void termino() int nAst = 0 grabar(Termino)

17

factor() ( nAst++ factor() ) if (nAst gt 0) Systemoutprintln (n Asteriscos + nAst + n)

void factor() grabar(Factor)

ltconstantegt | ltvariablegt | ( expresion() )

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP | t | n | r

Cuando se incluyen acciones sintaacutecticas la legibilidad del aspecto sintaacutectico de la especificacioacuten queda oscurecida para paliar ese inconveniente puede antildeadirse un comentario que muestre la regla sintaacutectica escueta sin las acciones por ejemplo podriacutea escribirse

void termino() int nAst = 0 grabar(Termino)

factor() ( factor() )

factor()

( nAst++ factor() )

if (nAst gt 0) Systemoutprintln (n Asteriscos + nAst + n)

loz Valor comunicado por un meacutetodoEn todo lo visto hasta ahora los meacutetodos de anaacutelisis sintaacutectico generados por JavaCC no devuelven valor alguno al terminar su tarea de anaacutelisis ya que en la especificacioacuten siempre se ha declarado el nombre de un meacutetodo precedido de void (son meacutetodos de tipo void) Pero en general un meacutetodo de anaacutelisis sintaacutec-tico puede ser de cualquier tipo (primitivo o no) por ello un meacutetodo puede devolver un valor de cualquier tipo (de cualquier clase) al meacutetodo desde el que se efectuoacute la llamada

Esta posibilidad de devolucioacuten de valores tiene una utilidad fundamental en la implementacioacuten de analiza-dores de lenguajes permite la comunicacioacuten de datos (atributos de las entidades analizadas) entre los dife -rentes meacutetodos que colaboran en el anaacutelisis del programa completo

En una especificacioacuten JavaCC la llamada a un meacutetodo ocurre cuando en la parte derecha de una regla estaacute el nombre del siacutembolo no terminal asociado al meacutetodo Cuando un meacutetodo devuelve un valor (tiene un tipo distinto de void) es habitual la recepcioacuten del valor devuelto en una variable en la propia especificacioacuten

18

se puede indicar el nombre de la variable a la que se asignaraacute el valor devuelto tras la llamada para ello se emplea una notacioacuten ideacutentica a la forma que tienen las sentencias de asignacioacuten en el lenguaje Java

dato = nombreSimb()

donde dato es el nombre de la variable a la que se asigna el valor devuelto por el meacutetodo llamado esa va -riable ha de estar oportunamente declarada del mismo tipo que dicho meacutetodo

∆ Ejemplo 2

En el mismo ejemplo de presentacioacuten se desea ademaacutes del anaacutelisis leacutexico-sintaacutectico de la entrada contar la cantidad de operadores que hay en la expresioacuten A continuacioacuten se expone una especificacioacuten JavaCC que es una posible solucioacuten soacutelo se muestran las reglas sintaacutecticas (el resto de la especificacioacuten es igual que en el ejemplo de presentacioacuten)

En la especificacioacuten que se propone puede apreciarse- los siacutembolos no terminales expresion termino y factor se han declarado de tipo int los meacutetodos correspondientes devuelven un valor de tipo entero- el valor devuelto por los meacutetodos sirve para propagar a traveacutes de la estructura sintaacutectica la cuenta de los operadores que hay en el texto analizado- por tratarse de meacutetodos que devuelven un valor es preciso incluir las correspondientes sentencias de devolucioacuten return

void unaExpresion() int nOper nOper = expresion() ltEOFgt Systemoutprintln (Cantidad de operadores = + nOper)

int expresion() int n i n = termino() ( + n++ i = termino() n = n + i )

return n

int termino() int n i n = factor() ( n++ i = factor() n = n + i )

return n

int factor() int n ltconstantegt return 0 | ltvariablegt return 0 | ( n = expresion() ) return n

La especificacioacuten de un factor tambieacuten podriacutea escribirse de esta otra manera

19

int factor() int n = 0

( ltconstantegt | ltvariablegt | ( n = expresion() ) )

return n

A continuacioacuten se expone un nuevo ejemplo para ilustrar la utilidad que proporciona el valor devuelto por un meacutetodo de anaacutelisis sintaacutectico para la propagacioacuten (ascendente) de atributos (propiedades de las entida-des analizadas)

∆ Ejemplo 3

Se pretende ampliar el analizador del ejemplo de presentacioacuten para que se emita un mensaje indicativo cuando la expresioacuten analizada sea una expresioacuten en la que soacutelo aparecen constantes en la solucioacuten que se muestra soacutelo se incluyen las reglas sintaacutecticas (en el resto de la especificacioacuten no hay modificacio-nes)

void unaExpresion() boolean esCte

expresion() ltEOFgt

esCte = expresion() ltEOFgt

if (esCte) Systemoutprintln(nExpresion constanten)

boolean expresion() boolean esCte esTambienCte

termino() ( + termino() )

esCte = termino() ( + esTambienCte = termino() esCte = esCte ampamp esTambienCte )

return esCte

boolean termino() boolean esCte esTambienCte

factor() ( factor() )

esCte = factor() ( esTambienCte = factor() esCte = esCte ampamp esTambienCte )

return esCte

20

boolean factor() boolean esCte

ltconstantegt return true | ltvariablegt return false | ( esCte = expresion() ) return esCte

bull Reconsideracioacuten sobre las producciones vaciacuteasAnteriormente se ha comentado que para representar una produccioacuten vaciacutea en una especificacioacuten JavaCC hay que hacer uso de los metasiacutembolos de opcionalidad para ilustrar esta idea se ha incluido como ejem-plo la especificacioacuten de una lista de paraacutemetros compuesta por cero o maacutes expresiones separadas entre siacute por el caraacutecter coma

void parametros() [ expresion() ( expresion() ) ]

Pero si se aprovecha la posibilidad de incorporar acciones sintaacutecticas se tiene una manera de especificar directamente producciones vaciacuteas para conseguirlo basta considerar una produccioacuten vaciacutea (no tiene nada en su parte derecha) acompantildeada de una accioacuten sintaacutectica cuyo bloque de coacutedigo esteacute vaciacuteo Seguacuten esta posibilidad la especificacioacuten anterior podriacutea tambieacuten escribirse asiacute

void parametros() expresion() ( expresion() ) |

En general la accioacuten sintaacutectica asociada a una produccioacuten vaciacutea no tiene por queacute ser un bloque vaciacuteo de coacutedigo por ejemplo si se quiere que el meacutetodo que analiza los paraacutemetros de una llamada devuelva la cantidad de paraacutemetros encontrados se podriacutea escribir la siguiente especificacioacuten

int parametros()

int cantidad

( expresion() cantidad = 1

( expresion() cantidad++ )

| cantidad = 0 )

return cantidad

y si no quisiera ponerse expliacutecitamente la alternativa de la produccioacuten vaciacutea podriacutea escribirse

int parametros() int cantidad = 0

21

[ expresion() cantidad++ ( expresion() cantidad++ ) ]

return cantidad

Comunicacioacuten entre los analizadores leacutexico y sintaacutecticoCada vez que el analizador sintaacutectico realiza una llamada al analizador lexicograacutefico recibe como resultado de la llamada una representacioacuten de la pieza sintaacutectica encontrada en el texto analizado En los analizadores generados por JavaCC la comunicacioacuten de la pieza se efectuacutea mediante un valor de clase Token Esta cla-se estaacute definida en el fichero Tokenjava que se obtiene en todo caso siempre con el mismo contenido cualquiera que sea la especificacioacuten proporcionada como entrada al generador

Entre los campos de la clase Token se encuentran los siguientes

public int kind

nuacutemero entero que sirve de representacioacuten interna de la pieza sintaacutectica estos nuacutemeros asociados a las piezas son asignados automaacuteticamente por JavaCC los valores considerados pueden consultar-se en el fichero generado de nombre Constantsjava

public String image

cadena que contiene de la secuencia de caracteres (lexema) que constituyen la pieza sintaacutectica co-municada

public int beginLine beginColumn endLine endColumn

posiciones ocupadas (nuacutemeros de liacutenea y columna en el fichero de entrada) por el comienzo y el fi-nal del lexema de la pieza sintaacutectica comunicada

La clase Token tiene otros campos y meacutetodos en el fichero Tokenjava se tienen comentarios des-criptivos sobre todos los campos y meacutetodos que forman parte de la clase todos los campos de la clase se declaran como public

Ya se ha comentado anteriormente que cuando un meacutetodo de anaacutelisis sintaacutectico (asociado a un siacutembolo no terminal) tiene tipo el valor devuelto tras una llamada se puede almacenar en una variable por si se desea utilizar en alguna accioacuten sintaacutectica para ello se escribe

valor = nombreSimbolo()

(siendo valor el nombre de la variable y nombreSimbolo el nombre del siacutembolo no terminal) De manera anaacuteloga el objeto de la clase Token representativo de la pieza sintaacutectica encontrada en la entrada por el analizador lexicograacutefico puede asignarse a una variable del mismo tipo para tener accesibles las ca -racteriacutesticas de la pieza por si se precisaran en alguna accioacuten sintaacutectica para ello en la parte de la especifi -cacioacuten sintaacutectica donde aparezca la pieza sintaacutectica se escribe

dato = lt nombrePieza gt

donde nombrePieza es el nombre de la pieza sintaacutectica y dato es el nombre de una variable de tipo Token en la que se anota el valor indicativo de las propiedades de la pieza sintaacutectica comunicada

Tambieacuten existe otra manera de acceder a las caracteriacutesticas de la pieza sintaacutectica que el analizador lexico -graacutefico comunica al analizador sintaacutectico en la clase del analizador sintaacutectico se tiene declarado el campo

[ static ] public Token token

22

cada vez que el analizador sintaacutectico recibe una pieza sintaacutectica comunicada por el analizador lexicograacutefico se deja anotada en el campo token y ese contenido no se altera hasta que sea proporcionada la siguiente pieza de la entrada

∆ Ejemplo 4

Se considera una ampliacioacuten del ejemplo de presentacioacuten ademaacutes del anaacutelisis de la entrada se pretende obtener una relacioacuten numerada de las variables y las constantes indicando el nuacutemero de liacutenea y el nuacuteme-ro de columna donde estaacuten situadas por ejemplo si en la entrada se tiene la expresioacuten (grabada en dos liacuteneas)

x (y + 11) + 22

en la salida se tendraacute

1- x linea 1 columna 1

2- y linea 1 columna 6

3- 11 linea 1 columna 10

4- 22 linea 2 columna 5

En la especificacioacuten que se propone como solucioacuten puede apreciarse- en la clase del analizador sintaacutectico se han incluido meacutetodos auxiliares en los que se usan los nom-bres image beginLine beginColumn que son campos de un objeto de clase Token

- en la especificacioacuten sintaacutectica de factor se emplea la variable pieza de tipo Token para reco-ger las caracteriacutesticas de las piezas sintaacutecticas detectadas- la variable numero hace de contador para numerar las constantes y variables

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin

static int numero = 0

private static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

private static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

private static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

23

PARSER_END (ExprMin)

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() Token pieza pieza = ltconstantegt numero++ grabarDatosPieza(numero pieza) | pieza = ltvariablegt numero++ grabarDatosPieza(numero pieza) | ( expresion() )

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP | n | t | r

Si se agrupan las dos acciones sintaacutecticas comunes la especificacioacuten de factor tambieacuten puede escri-birse de esta forma

void factor() Token pieza ( pieza = ltconstantegt | pieza = ltvariablegt

) numero++ grabarDatosPieza(numero pieza)

| ( expresion() )

24

Si se accede al campo token de la clase del analizador sintaacutectico se puede resolver este ejemplo pres-cindiendo de la variable pieza

void factor() ( ltconstantegt | ltvariablegt

) numero++ grabarDatosPieza(numero token)

| ( expresion() )

Tareas asociadas a las piezas sintaacutecticasYa se ha visto que es posible asociar bloques de coacutedigo Java a puntos de la estructura sintaacutectica para que se realicen ciertas operaciones cuando el analizador sintaacutectico pase por esa zona de la estructura Tambieacuten es posible asociar coacutedigo Java a las piezas sintaacutecticas para que el analizador lexicograacutefico efectuacutee determina-das tareas cuando detecte en la entrada analizada piezas que tengan asociado coacutedigo

loz Accioacuten ligada a una pieza sintaacutecticaSi se asocia un bloque de coacutedigo a una pieza sintaacutectica se consigue que cada vez que se detecta en la en-trada analizada la presencia de esa pieza se ejecute en ese preciso momento el coacutedigo del bloque asociado a la pieza Estas acciones se denominan ldquoacciones lexicograacuteficasrdquo (lexical actions) el coacutedigo del bloque se traslada literalmente al coacutedigo del analizador lexicograacutefico generado para que se ejecute oportunamente cuando se detecte la pieza ligada al bloque de coacutedigo La accioacuten lexicograacutefica se ejecuta inmediatamente antes que la pieza sea comunicada al analizador sintaacutectico

Se puede asociar una accioacuten lexicograacutefica a una pieza sintaacutectica declarada con TOKEN y tambieacuten a una se-cuencia de caracteres declarada con SKIP ademaacutes de eacutestas hay otras dos formas de declaraciones lexico-graacuteficas (no mencionadas en esta introduccioacuten) que tambieacuten pueden llevar una accioacuten asociada

En una especificacioacuten JavaCC la forma de una pieza sintaacutectica (o bien de una secuencia de caracteres) se define poniendo su descripcioacuten delimitada entre llaves detraacutes de la indicacioacuten TOKEN (o bien SKIP) si se quiere asociar una accioacuten lexicograacutefica a la pieza (o a la secuencia) hay que colocar el bloque de coacutedigo de la accioacuten a continuacioacuten de la descripcioacuten de la forma de la pieza (o de la secuencia) el bloque (delimi-tado por llaves) queda situado entre la descripcioacuten y la llave que cierra la especificacioacuten

La pieza sintaacutectica especial que representa el final del fichero (EOF) tambieacuten puede llevar asociada una ac-cioacuten lexicograacutefica para ello hay que poner

ltgt TOKEN lt EOF gt coacutedigo de la accioacuten asociada

para justificar la presencia de los caracteres ltgt delante de la palabra TOKEN hay que basarse en el con-cepto de ldquoestado lexicograacuteficordquo (cuya explicacioacuten queda fuera del alcance de esta introduccioacuten)

∆ Ejemplo 5

Se considera nuevamente el ejemplo de presentacioacuten como resultado del anaacutelisis de una expresioacuten se pretende obtener una relacioacuten de los pareacutentesis contenidos en la expresioacuten en el mismo orden en que se encuentran ademaacutes por cada salto de liacutenea del texto de entrada se grabaraacute en la salida el caraacutecter y

25

se pasaraacute a una nueva liacutenea tambieacuten se anotaraacute la frase Final del trabajo cuando se alcance el final del fichero de entrada Asiacute por ejemplo si la entrada analizada es

(a + b) (( x )) (y + 7)

los resultados obtenidos seraacuten

() (())()

Final del trabajo

En la especificacioacuten JavaCC que se propone como solucioacuten soacutelo se muestran las reglas sintaacutecticas y le -xicograacuteficas (en el resto no hay cambios) en esta solucioacuten puede apreciarse- las piezas sintaacutecticas correspondientes a los pareacutentesis se han definido de manera nominal para po-der asociarles una accioacuten lexicograacutefica- los caracteres incluidos en la pieza de tipo SKIP se han descompuesto en dos grupos ya que la ac-cioacuten lexicograacutefica incorporada soacutelo afecta a uno de ellos

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ltpAbrirgt expresion() ltpCerrargt

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

TOKEN

26

lt pAbrir ( gt Systemoutprint(()

TOKEN lt pCerrar ) gt Systemoutprint())

ltgtTOKEN lt EOF gt Systemoutprintln ( Final del trabajo )

SKIP n Systemoutprint( n)

SKIP | t | r

loz Bloque de declaraciones lexicograacuteficasCada definicioacuten de una pieza sintaacutectica puede llevar asociada una accioacuten lexicograacutefica que se ejecuta in-mediatamente despueacutes de detectar en la entrada un lexema que se ajusta a la definicioacuten de la pieza cada accioacuten lexicograacutefica es un bloque de coacutedigo Java que puede contener sus propias declaraciones Pero a ve-ces interesa tener declaraciones que sean compartidas por todas las acciones lexicograacuteficas con indepen-dencia de la pieza a la que esteacute asociada cada una de ellas

Para incluir declaraciones que puedan usarse desde el coacutedigo asociado a cualquiera de las piezas sintaacutecti -cas se dispone de una construccioacuten con el siguiente formato

TOKEN_MGR_DECLS

que consta de una palabra reservada seguida del siacutembolo dos puntos seguido de un bloque de coacutedigo Java (delimitado por llaves) esta declaracioacuten se puede colocar en cualquier parte de una especificacioacuten JavaCC si se incluye soacutelo puede ponerse una vez

Todas las declaraciones de variables o de meacutetodos puestas en el bloque de declaraciones lexicograacuteficas compartidas son accesibles desde el coacutedigo de cualquier accioacuten lexicograacutefica la justificacioacuten es sencilla el bloque de declaraciones se traslada al principio del coacutedigo del analizador lexicograacutefico generado y las ac-ciones lexicograacuteficas tambieacuten se trasladan a ese analizador

loz Declaraciones lexicograacuteficas predefinidasDe lo expuesto hasta ahora se aprecia que en una accioacuten lexicograacutefica asociada a una pieza sintaacutectica re-sultan accesibles

- las declaraciones de su propio bloque (si tiene declaraciones)- los componentes del bloque de declaraciones compartidas (si estaacute incluido en la especificacioacuten)

Ademaacutes de esto se tienen otras declaraciones pertenecientes a la clase TokenManager que constituye el analizador lexicograacutefico que tambieacuten son utilizables desde cualquiera de las acciones lexico-graacuteficas estas declaraciones pueden considerarse como ldquopredefinidasrdquo ya que son incorporadas automaacuteti-camente por el generador Entre estas declaraciones accesibles se encuentran las siguientes

[ static ] StringBuffer imagecontiene la cadena de caracteres representativa de la pieza sintaacutectica actual que se acaba de recono-cer en le entrada se trata de un campo de la clase y se declara como static cuando el analizador

27

se ha generado con la opcioacuten (por defecto) Static = true si se generase con la opcioacuten Sta-tic = false no quedariacutea declarada como static

Token matchedToken

variable de clase Token que representa la pieza sintaacutectica actual la que se acaba de encontrar en la entrada se trata de una variable local del meacutetodo al que se ha trasladado el coacutedigo de la accioacuten (y por ello accesible desde ese coacutedigo)

El campo image puede usarse tanto si la accioacuten lexicograacutefica estaacute asociada a una pieza declarada con TOKEN como si lo estaacute a una secuencia declarada con SKIP Sin embargo la variable matchedToken no puede usarse en una accioacuten asociada a un patroacuten especificado como SKIP el motivo es que lo declara-do como SKIP no constituye propiamente una pieza sintaacutectica ya que no se va a comunicar al analizador sintaacutectico

∆ Ejemplo 6

Se propone aquiacute una nueva solucioacuten para el ejemplo nordm 4 expuesto anteriormente

Esta nueva solucioacuten tal y como se expone aquiacute admite algunas mejoras ya que su pretensioacuten soacutelo es ser-vir de ilustracioacuten para algunos aspectos que se acaban de citar (maacutes adelante se propone otra solucioacuten al-ternativa)

En la especificacioacuten de la solucioacuten es posible apreciar esto- las acciones lexicograacuteficas pueden llevar sus propias declaraciones las variables nLineaVar nColumnaVar nLineaCte y nColumnaCte se declaran dentro de las acciones

- la variable matchedToken a la que se hace referencia en el coacutedigo de la accioacuten lexicograacutefica aso-ciada a una variable contiene la informacioacuten sobre la pieza sintaacutectica que se acaba de detectar una va-riable de la expresioacuten analizada anaacutelogamente con la referencia relativa a una constante- el nombre image que aparece en las acciones lexicograacuteficas se refiere al campo de la clase Expr-MinTokenManager (analizador lexicograacutefico) noacutetese que en efecto se comunica como un paraacuteme-tro de clase StringBuffer

- la variable numero se emplea para numerar las constantes y variables encontradas

- los nombres beginLine y beginColumn son campos (puacuteblicos) de la clase Token por ello se pueden aplicar a un objeto de esa clase

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

void unaExpresion()

28

expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n StringBuffer lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

TOKEN lt variable [a-z] gt

int nLineaVar nColumnaVar numero++ nLineaVar = matchedTokenbeginLine nColumnaVar = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaVar nColumnaVar)

TOKEN lt constante ( [0-9] ) + gt

int nLineaCte nColumnaCte

29

numero++ nLineaCte = matchedTokenbeginLine nColumnaCte = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaCte nColumnaCte)

SKIP | n | t | r

La especificacioacuten precedente se puede escribir de otra manera maacutes escueta como se indica a continua-cioacuten (soacutelo se muestran las modificaciones) sobre esta nueva especificacioacuten puede mencionarse- se han eliminado por innecesarias las variables declaradas dentro de las acciones lexicograacuteficas- el nombre image que aparece en el bloque de las declaraciones compartidas se refiere al campo de la clase Token noacutetese que en efecto se aplica a un objeto de esa clase y se comunica como un paraacute -metro de tipo String

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

TOKEN lt variable [a-z] gt

numero++ grabarDatosPieza(numero matchedToken)

TOKEN lt constante ( [0-9] ) + gt

numero++ grabarDatosPieza(numero matchedToken)

loz Accioacuten lexicograacutefica comuacutenSi hubiera que realizar la misma tarea tras la deteccioacuten de todas y cada una de las piezas sintaacutecticas una posibilidad incoacutemoda seriacutea repetir el mismo coacutedigo en todas las acciones lexicograacuteficas para evitar tanta reiteracioacuten se tiene la opcioacuten Common_Token_Action su valor por defecto es false pero si se le asigna el valor true es posible definir una ldquoaccioacuten lexicograacutefica comuacutenrdquo que se escribe una uacutenica vez y se aplica a todas las piezas sintaacutecticas

30

Cuando la opcioacuten Common_Token_Action se establece con el valor true es obligado incluir el blo-que de declaraciones lexicograacuteficas compartidas (TOKEN_MGR_DECLS) Ademaacutes en ese bloque se preci-sa la definicioacuten de un meacutetodo cuyo encabezamiento sea

[ static ] void CommonTokenAction(Token t)

el coacutedigo de este meacutetodo constituye la accioacuten lexicograacutefica comuacuten que se ejecutaraacute despueacutes de la deteccioacuten de cualquier pieza sintaacutectica el meacutetodo tiene un paraacutemetro de tipo Token mediante el que se puede co-municar la informacioacuten correspondiente a la pieza que se acaba de encontrar en la entrada analizada

Si una pieza sintaacutectica tiene asociada una accioacuten lexicograacutefica propia la accioacuten lexicograacutefica comuacuten (si es-taacute definida) se ejecutaraacute inmediatamente despueacutes de realizar la accioacuten propia

La accioacuten lexicograacutefica comuacuten se aplica a todas las piezas sintaacutecticas definidas como TOKEN bien sea no-minalmente (en las reglas lexicograacuteficas) o bien anoacutenimamente (incorporadas dentro de las reglas sintaacutecti-cas) sin embargo no se aplica cuando la secuencia acoplada se corresponde con una expresioacuten regular es-pecificada como SKIP (noacutetese que por el contrario una especificacioacuten SKIP siacute puede tener asociada una accioacuten lexicograacutefica propia)

El coacutedigo del meacutetodo CommonTokenAction se traslada como un componente maacutes del bloque de decla-raciones compartidas (TOKEN_MGR_DECLS) al principio de la clase del analizador lexicograacutefico

∆ Ejemplo 7

Se pretende ahora ampliar el ejemplo de presentacioacuten con la obtencioacuten de una relacioacuten numerada de los lexemas de las piezas sintaacutecticas encontradas en la entrada analizada en el caso de las variables y de las constantes antes del lexema se mostraraacute un indicativo (Var Const) por ejemplo si la expresioacuten de la entrada es

( a + 987 ) b

se grabaraacute la salida

( 1Var a 2+ 3Const 987 4) 5 6Var b 7 8

En la solucioacuten que se propone a continuacioacuten se muestran la definicioacuten de las opciones y las reglas sin-taacutecticas y lexicograacuteficas (en el resto no hay modificaciones) puede resaltarse lo siguiente- las llamadas al meacutetodo CommonTokenAction se incorporan automaacuteticamente no hay que incluir-las en la especificacioacuten como paraacutemetro de llamada a este meacutetodo siempre se emplea el objeto que re -presenta la pieza que se acaba de detectar- la accioacuten lexicograacutefica comuacuten realiza la actualizacioacuten del contador de piezas y la grabacioacuten del lexe-ma de la pieza detectada con su nuacutemero de orden- la accioacuten lexicograacutefica comuacuten tambieacuten se aplica a las piezas sintaacutecticas anoacutenimas (pareacutentesis y ope-radores) y a la pieza especial que representa el final del fichero (EOF)

- las piezas sintaacutecticas ltconstantegt y ltvariablegt tienen asociada una accioacuten lexicograacutefica propia que se ejecuta antes de la accioacuten lexicograacutefica comuacuten- los caracteres que se saltan (SKIP) no estaacuten afectados por la accioacuten lexicograacutefica comuacuten

options Ignore_Case = true Common_Token_Action = true

31

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarIndicativo(String indicativo) Systemoutprint(indicativo + )

static void grabarLexema (int num String lexema) Systemoutprint(lexema + + num + n)

static void CommonTokenAction(Token pieza) numero++ grabarLexema(numero piezaimage)

TOKEN lt variable [a-z] gt

grabarIndicativo(Var)

32

TOKEN lt constante ( [0-9] ) + gt

grabarIndicativo(Const)

SKIP | n | t | r

A modo de ejemplo de aplicacioacuten de la accioacuten lexicograacutefica comuacuten en lo que sigue se ofrece una solucioacuten para la grabacioacuten (en el fichero predefinido de salida) del texto analizado con las liacuteneas numeradas

∆ Ejemplo 8 [ Listado del texto analizado ]Se desea completar el ejemplo de presentacioacuten con la grabacioacuten de un listado con las liacuteneas numeradas del texto analizado (noacutetese que una expresioacuten puede escribirse ocupando varias liacuteneas) No se muestra la especificacioacuten sintaacutectica debido a que no hay ninguna modificacioacuten en ella Los aspectos resentildeables de la solucioacuten que se propone son- se usa la variable numLin como contador de liacuteneas

- en la variable linea se van yuxtaponiendo los lexemas de las piezas sintaacutecticas de una liacutenea esta yuxtaposicioacuten se realiza mediante la accioacuten lexicograacutefica comuacuten- las secuencias reconocidas con los patrones especificados como SKIP no implican la ejecucioacuten de la accioacuten lexicograacutefica comuacuten por ello su lexema (almacenado en la variable image) ha de yuxtaponer-se expliacutecitamente- hay que separar el patroacuten correspondiente al fin de liacutenea para detectar el momento en que ha de gra-barse la liacutenea completa cuyo final se ha alcanzado

options Ignore_Case = true Common_Token_Action = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(nnAnalisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

TOKEN_MGR_DECLS static int numLin = 0 static String linea =

33

static void CommonTokenAction(Token pieza) linea = linea + piezaimage

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP lt | t | r gt linea = linea + image

SKIP n numLin++ Systemoutprintln(numLin + + linea) linea =

A modo de recapitulacioacutenↄ Accioacuten lexicograacutefica Accioacuten sintaacutectica

bull accioacuten lexicograacutefica bloque de coacutedigo Java asociado a una pieza sintaacutectica nominal (TOKEN) o a una secuencia de caracteres (SKIP) se ejecuta cuando se detecta en la entrada la pieza o la secuencia corres-pondiente no se puede aplicar a piezas sintaacutecticas anoacutenimas (autodefinidas con comillas)bull accioacuten sintaacutectica bloque de coacutedigo Java asociado a un punto de la estructura sintaacutectica (el punto indica-do por el sitio de la produccioacuten donde se inserta el bloque) se ejecuta cuando el anaacutelisis de la entrada pa-sa por ese punto de la estructura

ↄ Valor de una pieza sintaacutectica comunicadaEl analizador lexicograacutefico comunica al analizador sintaacutectico una pieza a traveacutes de un valor de la clase Token (fichero Tokenjava) un objeto de la clase Token representa las caracteriacutesticas de la pieza comunicada En la clase Token entre otros se tienen los campos

String imagelexema de la pieza

int beginLine beginColumn endLine endColumnposicioacuten del lexema en el fichero analizado (indicada por los nuacutemeros de fila y de columna de su comienzo y de su terminacioacuten)

ↄ Valor asociado a un siacutembolo

34

En las producciones que especifican la sintaxis se pueden incluir asignaciones que sirven para dejar ano-tado el valor asociado a un siacutembolo

bull siacutembolo no terminal valor = nombreSimbolo()el meacutetodo de anaacutelisis asociado a nombreSimbolo se ha declarado de un cierto tipo (distinto del tipo void) valor ha de ser una variable del mismo tipo que el meacutetodo

bull siacutembolo terminal dato = lt nombrePieza gtel valor asignado es el valor de la clase Token representativo de la pieza sintaacutectica (nominal) dato ha de ser una variable de tipo Token

ↄ Miradas sobre la pieza sintaacutectica comunicadaLa pieza sintaacutectica comunicada puede mirarse desde cada uno de los dos analizadores

bull analizador sintaacutecticola pieza comunicada (desde el analizador lexicograacutefico) se tiene anotada en un campo de la clase del analizador sintaacutectico declarado como

(static) public Token token

este valor es accesible desde el coacutedigo de todos los meacutetodos de anaacutelisis sintaacutectico el contenido del campo variacutea cuando se recibe una nueva pieza sintaacutectica

bull analizador lexicograacuteficola pieza dispuesta para ser comunicada (al analizador sintaacutectico) se tiene anotada en una variable local declarada como

Token matchedToken

este valor es accesible para el coacutedigo de todas las acciones lexicograacuteficas

ↄ El lexema de la pieza comunicadaEl lexema de la pieza sintaacutectica comunicada se encuentra disponible en dos sitios distintos que compar-ten el mismo nombre pero que son de distinto tipo y se usan de distinta manera

bull String image

es un campo de la clase TOKEN puede consultarse en relacioacuten con un objeto de esa clase que esteacute disponible

bull StringBuffer image

es un campo de la clase del analizador lexicograacutefico ( ∙ ∙ ∙ ∙ TokenManagerjava) es accesible desde el coacutedigo de la clase y por lo tanto desde todas las acciones lexicograacuteficas (bloques de coacute-digo que estaacuten incorporados al analizador lexicograacutefico)

se trata de un valor disponible tanto para las piezas sintaacutecticas nominales (TOKEN) como para las secuencias que se saltan (SKIP)

ↄ Resumen relativo a la lexicografiacutea

TOKEN pieza sintaacutectica nominal∙ ∙ ∙ pieza sintaacutectica anoacutenimaSKIP secuencia de caracteres que no forman pieza

35

Precisiones sobre el analizador sintaacutectico generadoPuede decirse que en su funcionamiento maacutes sencillo cuando no se emplean las posibilidades de examen por adelantado (ldquolookaheadrdquo) JavaCC comprueba que las reglas sintaacutecticas cumplen la condicioacuten LL(1) y generan un analizador descendente-predictivo-recursivo Pero esta apreciacioacuten hay que matizarla ya que el analizador sintaacutectico generado no se ajusta en sentido estricto al modelo de analizador LL(1)

En el modelo de anaacutelisis LL(1) el orden del examen de las distintas alternativas de expansioacuten de un siacutembo-lo no terminal es indiferente ya que se conocen los siacutembolos directores de cada una de ellas (que resultan ser conjuntos disjuntos) en la implementacioacuten del subprograma de anaacutelisis asociado al siacutembolo no terminal ltNombreSimbgt cuyas producciones son

ltNombreSimbgt = α1

| α2

∙ ∙ ∙

| αn

se podriacutea preguntar en cualquier orden si la pieza actual pertenece al conjunto de siacutembolos directores de las distintas reglas Sin embargo en el analizador sintaacutectico generado por JavaCC la implementacioacuten del meacute-todo asociado al siacutembolo no terminal ltNombreSimbgt no sigue esa pauta

Si las anteriores reglas sintaacutecticas se transcriben en JavaCC de la forma

void nombreSimb () α1-jcc

| α2-jcc

∙ ∙ ∙

| αn-jcc

1

accioacutenlexicograacutefica

comuacuten

Commom -TokenAction2

accioacutenlexicograacutefica

propia

(accioacuten ligadaa una pieza)3

bloque dedeclaracioneslexicograacuteficas

TOKEN_MGR_DECLS4

declaracioneslexicograacuteficaspredefinidas

(disponibilidad

de uso)TOKENsisisisi∙ ∙ ∙sino--SKIPnosisisi

Las indicaciones si de las columnas 3 y 4 son consecuencia de las indicaciones de la columna 2

La accioacuten lexicograacutefica comuacuten se ejecuta despueacutes de la accioacuten lexicograacutefica propia

36

el meacutetodo de anaacutelisis generado procede de la siguiente manera

se van considerando las alternativas sucesivamente en el mismo orden en el que estaacuten especifica-das primero la de α1 (α1-jcc) despueacutes la de α2 (α2-jcc) etc

para cada alternativa seleccionada se comprueba si la pieza por adelantado coincide con alguno de los siacutembolos iniciales de la parte derecha y si hay coincidencia se prosigue el anaacutelisis con ella si no hay coincidencia se pasa a la alternativa siguiente

siguiendo con este orden si se llega al final de las alternativas sin haberse encontrado coincidencia alguna se produce un error sintaacutectico

En el analizador generado por JavaCC hay una peculiaridad que no se tiene en los analizadores LL(1) es-trictamente considerados la palabra vaciacutea siacute se considera como posible siacutembolo inicial de la parte derecha una regla (esto ocurre siempre que la parte derecha es anulable) De esta peculiaridad se deriva una conse-cuencia que ha de tenerse en cuenta al escribir la especificacioacuten dado que siempre es posible considerar la palabra vaciacutea como la siguiente pieza por adelantado si se llega a examinar la alternativa correspondiente a una produccioacuten anulable el analizador generado siempre seleccionaraacute esa alternativa con independencia de la siguiente pieza que esteacute presente en la entrada que queda por analizar

Asiacute pues si alguna de las producciones que definen un siacutembolo no terminal es anulable es importante el or-den en que se escriben las alternativas en una especificacioacuten sintaacutectica JavaCC el funcionamiento del ana-lizador generado siacute depende de la colocacioacuten elegida para las reglas

Para ilustrar estas peculiaridades del analizador sintaacutectico generado por JavaCC a continuacioacuten se expone un ejemplo

∆ Ejemplo 9

Se quiere obtener un analizador leacutexico-sintaacutectico de textos que se ajustan al siguiente formato una se-cuencia de uno o maacutes nombres seguida del siacutembolo igual seguido de un nuacutemero entero si la secuencia tiene maacutes de un nombre han de estar separados bien por una coma en todos los casos o bien por el siacutem-bolo dos puntos en todos los casos esto es son correctas las secuencias

uno dos tres = 123exclusivo = 0Abajo Arriba = 57

y son incorrectas las secuenciasuno dos tres = 123

exclusivo inclusive = 0

fuera dentro = 56

Una gramaacutetica que define las secuencias con este formato es

ltListagt = nombre ltOtrosNombresgt = numero

ltOtrosNombresgt = nombre nombre | nombre nombre | ε

se trata de una gramaacutetica que cumple la condicioacuten LL(1)- el siacutembolo director para la alternativa vaciacutea es el siacutembolo igual es evidente pues que las tres reglas de ltOtrosNombresgt tienen siacutembolos directores distintos

37

- la decisioacuten de abandonar la repeticioacuten del anaacutelisis de un nuevo nombre (en las dos primeras alterna-tivas de ltOtrosNombresgt) se puede tomar ante la presencia del siacutembolo igual

En la implementacioacuten de un analizador sintaacutectico LL(1) en sentido estricto la seleccioacuten de la alternativa vaciacutea para expandir el siacutembolo ltOtrosNombresgt se hariacutea tras comprobar expliacutecitamente que la pie-za sintaacutectica por adelantado es el siacutembolo igual

El funcionamiento del analizador generado por JavaCC depende del orden de colocacioacuten de las reglas A continuacioacuten se exponen varias posibilidades de especificacioacuten y se comenta el analizador generado en cada caso

bull Especificacioacuten primeraSe transcriben literalmente las reglas tal y como se han escrito antes en la notacioacuten BNF noacutetese que para poder codificar la parte derecha de la regla vaciacutea ha de incluirse una accioacuten sintaacutectica vaciacutea

PARSER_BEGIN (Igualdad)

public class Igualdad

public static void main (String[] argum) throws ParseException Igualdad analisis = new Igualdad(Systemin) analisissecuencia() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (Igualdad)

void secuencia() lista() ltEOFgt

void lista() lt nombre gt otrosNombres() = lt numero gt

void otrosNombres() lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt ) |

TOKEN lt nombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN lt numero ( [ 0 - 9 ] )+ gt

SKIP | r | n

38

El analizador generado funciona correctamente puesto que en el meacutetodo otrosNombres se selec-ciona la alternativa vaciacutea despueacutes de haber comprobado que la pieza por adelantado no es siacutembolo ini-cial de ninguna de las dos alternativas precedentes (no es una coma ni un siacutembolo dos puntos)

bull Especificacioacuten segundaSe considera la misma especificacioacuten anterior pero cambiando el orden de colocacioacuten de las reglas ahora la produccioacuten vaciacutea se coloca como primera alternativa (soacutelo se muestran las partes modifica-das)

void otrosNombres() | lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt )

Con la entrada de esta especificacioacuten el generador JavaCC produce un mensaje en el que se avisa de que las dos alternativas que estaacuten colocadas detraacutes de la alternativa vaciacutea nunca podraacuten seleccionarse aun asiacute se genera un analizador Pero es un analizador que funciona incorrectamente asiacute por ejemplo la entrada

mxa nyz = 57

se considera incorrecta despueacutes de visto el primer nombre se intenta aplicar la produccioacuten vaciacutea (se espera encontrar el siacutembolo igual) pero habriacutea que aplicar la regla cuyo siacutembolo director es la coma

bull Especificacioacuten terceraSe utilizan las posibilidades de la notacioacuten BNF-Ampliada para prescindir de la especificacioacuten expliacuteci-ta de la palabra vaciacutea se aprovecha que JavaCC admite el operador + para indicar repeticiones de una o maacutes veces la alternativa vaciacutea estaacute impliacutecitamente contemplada en la posibilidad de cero repeti-ciones que contempla el operador (soacutelo se muestran las partes modificadas en la especificacioacuten)

void otrosNombres() ( lt nombre gt )+ | ( lt nombre gt )

El meacutetodo otrosNombres() generado hace las comprobaciones en el siguiente orden

- se comprueba si la pieza por adelantado es una coma (siacutembolo inicial de la primera alternativa) y en este caso se selecciona la primera regla- si no es una coma se comprueba si es un siacutembolo dos puntos (siacutembolo inicial de la segunda alter-nativa) y en este caso se selecciona la segunda regla- si no es un siacutembolo dos puntos se considera la palabra vaciacutea como siacutembolo inicial (siempre posi-ble impliacutecitamente) para la segunda alternativa en este caso para que no haya error la pieza por adelantado ha de ser un siacutembolo igual

Se trata pues de un analizador que funciona correctamente

bull Especificacioacuten cuarta

39

Se pone la misma especificacioacuten anterior pero cambiando el orden de consideracioacuten de la regla vaciacutea ahora se incluye impliacutecitamente en la primera alternativa (soacutelo se muestran las partes modificadas)

void otrosNombres() ( lt nombre gt ) | ( lt nombre gt )+

Para esta especificacioacuten el generador JavaCC emite un aviso en el que se indica que la segunda alter -nativa nunca se seleccionaraacute no obstante se genera un analizador que no funciona como se supone que deberiacutea hacerlo por ejemplo el anaacutelisis de la entrada

nyz mxa = 1

produce un error despueacutes de tratado el primer nombre se intenta aplicar la produccioacuten vaciacutea (corres-pondiente a la repeticioacuten de cero veces contemplada en la primera regla) por lo que no se encuentra el esperado siacutembolo igual

40

Page 13: JavaCC

Los pareacutentesis permiten fijar el orden de aplicacioacuten de los operadores Una peculiaridad de las expresiones regulares JavaCC es que los operadores de repeticioacuten y de opcionalidad requieren que el componente al que se aplican esteacute en todo caso delimitado por pareacutentesis

Se pueden citar las siguientes caracteriacutesticas baacutesicas de las expresiones regulares en JavaCC

Se tiene la posibilidad de definir conjuntos de caracteres poniendo entre corchetes la relacioacuten de carac-teres separados por comas y cada caraacutecter delimitado por comillas En un conjunto se puede fijar un ran-go de caracteres poniendo el primero y el uacuteltimo separados por el siacutembolo guioacuten (-) A veces resulta maacutes coacutemodo indicar los caracteres que no pertenecen al conjunto para ello se puede definir el comple -mentario de un conjunto poniendo el siacutembolo ~ delante del corchete inicial

Para indicar que una secuencia de caracteres colocados consecutivamente constituye por siacute misma una expresioacuten regular (o un componente de una expresioacuten) se escribe la secuencia delimitada por comillas El siacutembolo se emplea como metacaraacutecter para indicar que el siacutembolo que estaacute inmediatamente a su derecha se considere como caraacutecter del alfabeto no como posible metacaraacutecter (operador delimitador)

En una expresioacuten regular se pueden incluir como si fueran caracteres las secuencias de escape con la misma notacioacuten y el mismo significado que en Java o en C

Noacutetese que en una produccioacuten sintaacutectica la opcionalidad se puede especificar de dos formas con corchetes o con el siacutembolo de interrogacioacuten sin embargo en una expresioacuten regular la opcionalidad soacutelo se puede es-pecificar mediante el siacutembolo de interrogacioacuten (los corchetes en las expresiones regulares son para definir conjuntos de caracteres)

A continuacioacuten se muestran ejemplos de expresiones regulares escritas seguacuten la notacioacuten JavaCC

ENDsecuencia de las tres letras mayuacutesculas que conforman la palabra END

whilesecuencia de las cinco letras minuacutesculas que conforman la palabra while

ncaraacutecter representativo del fin de liacutenea (secuencia de escape)

rcaraacutecter representativo del retorno de carro (secuencia de escape)

caraacutecter dos puntos

=secuencia de dos caracteres (siacutembolo de asignacioacuten en Pascal)

gtcaraacutecter representativo del operador de relacioacuten mayor

(i | I)(f | F)palabra ldquoifrdquo escrita con letras minuacutesculas o mayuacutesculas (cuatro combinaciones)

if | IFpalabra ldquoifrdquo escrita soacutelo con minuacutesculas o soacutelo con mayuacutesculas (dos combinaciones)

12

u | o | i | e | auna cualquiera de las vocales minuacutesculas (pero soacutelo una de ellas)

[ A E I O U ]una cualquiera de las vocales mayuacutesculas (pero soacutelo una de ellas)

[ a - z ntilde ]una cualquiera de las letras minuacutesculas incluyendo la letra ntilde

[ a - z A - Z 0 - 9 ]una cualquiera de las letras o de las cifras decimales

([ 0 - 9 ])+constante entera (nuacutemero de una o maacutes cifras)

noacutetese que es incorrecto poner [ 0 - 9 ]+

( + | - )([ 0 - 9 ])+constante entera precedida opcionalmente por un signo

noacutetese que la primera cifra ha de seguir inmediatamente al signo (si existe)

([+ -])([ 0 - 9 ])+representa lo mismo que la expresioacuten anterior

[ a - z ] ([ a - z 0 - 9 ])una letra minuacutescula seguida de cero o maacutes letras minuacutesculas o cifras decimales

~[a - z ntilde A - Z Ntilde]cualquier caraacutecter que no sea una letra

noacutetese que la expresioacuten representa un uacutenico caraacutecter

~[n r]cualquier caraacutecter (un uacutenico caraacutecter) excepto el fin de liacutenea y el retorno de carro

~[ ]cualquier caraacutecter del alfabeto (un uacutenico caraacutecter)

([ 0 - 9 ])+ ( ([ 0 - 9 ])+)constante aritmeacutetica con parte decimal opcional

Los modernossecuencia de catorce caracteres Los modernos

noacutetese el uso de para imponer la condicioacuten de caraacutecter del siacutembolo comillas

nsecuencia de cuatro caracteres n

noacutetese que aquiacute no se representa la secuencia de escape

bull Piezas sintaacutecticas internas (ldquoprivaterdquo)

13

Es posible definir piezas sintaacutecticas auxiliares llamadas ldquointernasrdquo porque no se comunican al analizador sintaacutectico sino que soacutelo sirven para facilitar la escritura de la forma de las piezas sintaacutecticas que siacute se co-munican con el uso de las piezas internas es posible evitar repeticiones de escritura y hacer maacutes legible la especificacioacuten de las caracteriacutesticas lexicograacuteficas Las piezas internas permiten dar nombre a subexpre-siones regulares que a su vez pueden emplearse como componentes para escribir expresiones maacutes com-plejas que incorporan esos nombres Para indicar que una pieza sintaacutectica se considere como interna se pone el siacutembolo delante del nombre asociado a la pieza

A continuacioacuten se muestra un ejemplo tiacutepico de utilizacioacuten de las piezas internas en una especificacioacuten le-xicograacutefica en JavaCC Se quieren definir tres piezas sintaacutecticas que representan un nombre una constante entera y una constante decimal una posible especificacioacuten es

TOKEN ltnombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN ltcteEntera ( [ 0 - 9 ] )+ gt

TOKEN ltcteDecimal ( [ 0 - 9 ] )+ ( [ 0 - 9 ] )+ gt

Con la inclusioacuten de tres piezas sintaacutecticas internas auxiliares esta misma especificacioacuten podriacutea escribirse de la siguiente manera

TOKEN lt letra [ a - z A - Z ntilde Ntilde ] gt

TOKEN lt cifra [ 0 - 9 ] gt

TOKEN lt numero ( ltcifragt )+ gt

TOKEN lt nombre ( ltletragt )+ gt

TOKEN lt cteEntera ltnumerogt gt

TOKEN lt cteDecimal ltnumerogt ltnumerogt gt

Los nombres letra cifra y numero son exclusivos (internos) del analizador lexicograacutefico y no se pueden emplear como nombres de piezas en la especificacioacuten de las reglas sintaacutecticas cuando esos nom-bres se usan en la definicioacuten de otras piezas han de escribirse delimitados por los siacutembolos lt y gt

bull Distincioacuten restringida entre minuacutesculas y mayuacutesculasSe tiene la opcioacuten Ignore_Case para determinar si en el anaacutelisis del texto de entrada se distingue entre las letras minuacutesculas y las mayuacutesculas el valor por defecto false indica que siacute se diferencian si se asig-na el valor true a esta opcioacuten entonces no se diferencian en ninguacuten caso con independencia de que en los patrones que se definan para las piezas sintaacutecticas se pongan minuacutesculas o mayuacutesculas

Pero a veces conviene que la diferenciacioacuten entre minuacutesculas y mayuacutesculas no se aplique de manera ge-neral a toda la entrada sino en particular a las secuencias de caracteres que se acoplen a ciertos patrones Para este fin existe la posibilidad de establecer que en la expresioacuten regular que define el patroacuten de una determinada pieza sintaacutectica no se diferencie entre minuacutesculas y mayuacutesculas ello se consigue poniendo

14

[ IGNORE_CASE ]

inmediatamente detraacutes de la palabra TOKEN con la que empieza la especificacioacuten de la pieza sintaacutectica en la que se pretende la no distincioacuten entre minuacutesculas y mayuacutesculas

Noacutetese que ignore_case tiene dos significados

- es el nombre de un paraacutemetro de la seccioacuten de opciones (puede ponerse en minuacutesculas o mayuacutesculas)- es una palabra reservada que puede aplicarse (escribieacutendola entre corchetes y en mayuacutesculas) a la de -finicioacuten de una pieza sintaacutectica

Por ejemplo sea un lenguaje que en general distingue entre minuacutesculas y mayuacutesculas pero en la codifi -cacioacuten del exponente de una constante real exponencial puede ponerse la letra e en minuacutescula o en ma-yuacutescula En la especificacioacuten JavaCC para ese lenguaje la opcioacuten Ignore_Case ha de tomar el valor false en la definicioacuten de una constante exponencial son posibles las siguientes formas equivalentes

1) sin hacer uso de IGNORE_CASE

TOKEN lt cifras ( [ 0 - 9 ] )+ gt

TOKEN lt signo [+ -] gt

TOKEN lt constante

ltcifrasgt ( ltcifrasgt) [e E] (ltsignogt) ltcifrasgt gt

2) aplicando IGNORE_CASE

TOKEN lt cifras ( [ 0 - 9 ] )+ gt

TOKEN lt signo [+ -] gt

TOKEN [IGNORE_CASE] lt constante

ltcifrasgt ( ltcifrasgt) e (ltsignogt) ltcifrasgt gt

bull Secuencias de caracteres sin efecto en la estructura sintaacutecticaEn los lenguajes de programacioacuten suele haber secuencias de caracteres que no forman parte de la estructu-ra sintaacutectica de un programa esas secuencias son detectadas por el analizador lexicograacutefico pero no son comunicadas al sintaacutectico no han de considerarse como piezas sino que han de saltarse En JavaCC se pueden definir estas secuencias mediante la palabra reservada SKIP la notacioacuten de una especificacioacuten con la palabra SKIP de una secuencia que ha de saltarse es la misma que la notacioacuten usada para las espe-cificaciones con la palabra TOKEN de las piezas que han de comunicarse

Los dos casos maacutes habituales de secuencias que no se comunican al analizador sintaacutectico son

a) los caracteres separadores que no tienen significado en el texto analizadoen los lenguajes de codificacioacuten en formato libre hay caracteres del texto fuente que han de ignorarse en la mayoriacutea de los casos estos caracteres son el espacio en blanco el tabulador el retorno de carro y el fin de liacutenea esta caracteriacutestica se especifica

SKIP |t | r |n

b) los comentarios

15

en el anaacutelisis sintaacutectico de un programa los comentarios han de ignorarse una manera de especificar esta circunstancia es poniendo en una definicioacuten SKIP la expresioacuten regular que denota la forma de los comentarios del lenguaje por ejemplo para los comentarios de una liacutenea de Java se pondriacutea

SKIP

lt (~ [n r]) gt

Tareas asociadas a la estructura sintaacutecticaJavaCC genera un analizador sintaacutectico descendente recursivo constituido por un conjunto de meacutetodos ca-da siacutembolo no terminal de la gramaacutetica sintaacutectica tiene asociado un meacutetodo de anaacutelisis (el meacutetodo tiene el mismo nombre que el siacutembolo correspondiente) Un meacutetodo de anaacutelisis ademaacutes de la realizacioacuten de la parte del anaacutelisis sintaacutectico que le corresponde puede llevar a cabo otras tareas complementarias que sirvan para efectuar un anaacutelisis maacutes complejo del texto de entrada

loz Bloque para un siacutemboloAl describir la forma de las reglas sintaacutecticas de un siacutembolo no terminal en una especificacioacuten JavaCC ya se ha visto que detraacutes de la definicioacuten del nombre del siacutembolo y antes de empezar con las diferentes alter-nativas que tiene asociadas se incluye un bloque de coacutedigo Java (en el ejemplo de presentacioacuten este bloque estaacute vaciacuteo en todos los siacutembolos no terminales de la gramaacutetica) Cada bloque situado en esa posicioacuten puede considerarse ligado al siacutembolo no terminal en cuya descripcioacuten estaacute incluido el coacutedigo Java del bloque se traslada literalmente al principio del meacutetodo generado correspondiente al siacutembolo no terminal y por lo tan-to es lo primero que se considera (se ejecuta) cuando en el transcurso del proceso de anaacutelisis se realiza una llamada al meacutetodo Asiacute pues cada siacutembolo no terminal siempre tiene asociado un uacutenico bloque de coacute-digo Java ndashbloque de presencia obligada aunque pueda estar vaciacuteo- en este bloque se pueden poner de-claraciones y sentencias

loz Acciones sintaacutecticasAdemaacutes del bloque asociado a cada uno de los siacutembolos no terminales de la gramaacutetica se pueden incluir otros bloques de coacutedigo Java intercalados en cualquiera de las partes derechas de las reglas sintaacutecticas Los bloques incluidos en las partes derechas se denominan ldquoacciones sintaacutecticasrdquo (parser actions) ya que son trozos de coacutedigo que pueden considerarse asociados a los puntos de la estructura sintaacutectica donde estaacuten co-locados Una accioacuten sintaacutectica se puede poner en cualquier posicioacuten al principio al final o en el medio de la parte derecha de una regla precedida o seguida de siacutembolos terminales o no terminales el coacutedigo Java de una accioacuten sintaacutectica se traslada literalmente al meacutetodo asociado al siacutembolo no terminal de la parte izquierda de la regla de manera que se ejecuta cada vez que el analizador transcurre por el punto de la estructura sintaacutectica donde se ha incorporado la accioacuten (esto es las acciones sintaacutecticas se incorporan al coacutedigo del analizador sintaacutectico) No obstante su nombre en los bloques de las acciones sintaacutecticas suele ponerse coacutedigo dedicado a la realizacioacuten de tareas semaacutenticas

Si en el bloque asociado a un siacutembolo no terminal se ponen declaraciones los nombres ahiacute definidos son accesibles en todas las acciones sintaacutecticas intercaladas en las partes derechas de las reglas de ese siacutembolo la justificacioacuten es sencilla el coacutedigo del bloque asociado al siacutembolo se incluye al principio de un meacutetodo y el coacutedigo de las acciones pertenece a ese mismo meacutetodo

∆ Ejemplo 1

Se considera de nuevo el ejemplo de presentacioacuten ahora se pretende completar el anaacutelisis leacutexico-sintaacutec-tico con la realizacioacuten de las siguientes tareas

- cada vez que se analiza una expresioacuten un teacutermino o un factor se graba el nombre del siacutembolo no terminal correspondiente y

16

- cuando se analiza un teacutermino en el que hay operadores multiplicativos se cuenta su nuacutemero y se graba el resultado (noacutetese que se cuentan los operadores de cada teacutermino pero no se acumula la cuenta de todos los operadores de la expresioacuten)

Para la realizacioacuten de estas tareas se antildeaden bloques de coacutedigo Java En la especificacioacuten que se describe a continuacioacuten puede apreciarse- para los siacutembolos no terminales expresion termino y factor se ha incluido en los blo-ques correspondientes la operacioacuten de grabacioacuten de su nombre que se ejecutaraacute cada vez que se efec-tuacutee una llamada al meacutetodo- en el bloque asociado a termino se tiene la declaracioacuten del contador de operadores (que seraacute una variable local del meacutetodo)- en la parte derecha de la regla que define la estructura de un teacutermino se han incluido dos acciones sintaacutecticas una para contar la cantidad de operadores (situada detraacutes de la pieza sintaacutectica del opera-dor) y otra para grabar la cantidad de operadores del teacutermino (situada al final de la parte derecha)- se ha definido el meacutetodo grabar para facilitar la escritura del coacutedigo Java

options Ignore_Case = true

PARSER_BEGIN (ExprMin) public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

private static void grabar(String nombre) Systemoutprintln ( -gt + nombre + n)

PARSER_END (ExprMin)

void unaExpresion()

expresion() ltEOFgt

void expresion() grabar(Expresion)

termino() ( + termino() )

void termino() int nAst = 0 grabar(Termino)

17

factor() ( nAst++ factor() ) if (nAst gt 0) Systemoutprintln (n Asteriscos + nAst + n)

void factor() grabar(Factor)

ltconstantegt | ltvariablegt | ( expresion() )

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP | t | n | r

Cuando se incluyen acciones sintaacutecticas la legibilidad del aspecto sintaacutectico de la especificacioacuten queda oscurecida para paliar ese inconveniente puede antildeadirse un comentario que muestre la regla sintaacutectica escueta sin las acciones por ejemplo podriacutea escribirse

void termino() int nAst = 0 grabar(Termino)

factor() ( factor() )

factor()

( nAst++ factor() )

if (nAst gt 0) Systemoutprintln (n Asteriscos + nAst + n)

loz Valor comunicado por un meacutetodoEn todo lo visto hasta ahora los meacutetodos de anaacutelisis sintaacutectico generados por JavaCC no devuelven valor alguno al terminar su tarea de anaacutelisis ya que en la especificacioacuten siempre se ha declarado el nombre de un meacutetodo precedido de void (son meacutetodos de tipo void) Pero en general un meacutetodo de anaacutelisis sintaacutec-tico puede ser de cualquier tipo (primitivo o no) por ello un meacutetodo puede devolver un valor de cualquier tipo (de cualquier clase) al meacutetodo desde el que se efectuoacute la llamada

Esta posibilidad de devolucioacuten de valores tiene una utilidad fundamental en la implementacioacuten de analiza-dores de lenguajes permite la comunicacioacuten de datos (atributos de las entidades analizadas) entre los dife -rentes meacutetodos que colaboran en el anaacutelisis del programa completo

En una especificacioacuten JavaCC la llamada a un meacutetodo ocurre cuando en la parte derecha de una regla estaacute el nombre del siacutembolo no terminal asociado al meacutetodo Cuando un meacutetodo devuelve un valor (tiene un tipo distinto de void) es habitual la recepcioacuten del valor devuelto en una variable en la propia especificacioacuten

18

se puede indicar el nombre de la variable a la que se asignaraacute el valor devuelto tras la llamada para ello se emplea una notacioacuten ideacutentica a la forma que tienen las sentencias de asignacioacuten en el lenguaje Java

dato = nombreSimb()

donde dato es el nombre de la variable a la que se asigna el valor devuelto por el meacutetodo llamado esa va -riable ha de estar oportunamente declarada del mismo tipo que dicho meacutetodo

∆ Ejemplo 2

En el mismo ejemplo de presentacioacuten se desea ademaacutes del anaacutelisis leacutexico-sintaacutectico de la entrada contar la cantidad de operadores que hay en la expresioacuten A continuacioacuten se expone una especificacioacuten JavaCC que es una posible solucioacuten soacutelo se muestran las reglas sintaacutecticas (el resto de la especificacioacuten es igual que en el ejemplo de presentacioacuten)

En la especificacioacuten que se propone puede apreciarse- los siacutembolos no terminales expresion termino y factor se han declarado de tipo int los meacutetodos correspondientes devuelven un valor de tipo entero- el valor devuelto por los meacutetodos sirve para propagar a traveacutes de la estructura sintaacutectica la cuenta de los operadores que hay en el texto analizado- por tratarse de meacutetodos que devuelven un valor es preciso incluir las correspondientes sentencias de devolucioacuten return

void unaExpresion() int nOper nOper = expresion() ltEOFgt Systemoutprintln (Cantidad de operadores = + nOper)

int expresion() int n i n = termino() ( + n++ i = termino() n = n + i )

return n

int termino() int n i n = factor() ( n++ i = factor() n = n + i )

return n

int factor() int n ltconstantegt return 0 | ltvariablegt return 0 | ( n = expresion() ) return n

La especificacioacuten de un factor tambieacuten podriacutea escribirse de esta otra manera

19

int factor() int n = 0

( ltconstantegt | ltvariablegt | ( n = expresion() ) )

return n

A continuacioacuten se expone un nuevo ejemplo para ilustrar la utilidad que proporciona el valor devuelto por un meacutetodo de anaacutelisis sintaacutectico para la propagacioacuten (ascendente) de atributos (propiedades de las entida-des analizadas)

∆ Ejemplo 3

Se pretende ampliar el analizador del ejemplo de presentacioacuten para que se emita un mensaje indicativo cuando la expresioacuten analizada sea una expresioacuten en la que soacutelo aparecen constantes en la solucioacuten que se muestra soacutelo se incluyen las reglas sintaacutecticas (en el resto de la especificacioacuten no hay modificacio-nes)

void unaExpresion() boolean esCte

expresion() ltEOFgt

esCte = expresion() ltEOFgt

if (esCte) Systemoutprintln(nExpresion constanten)

boolean expresion() boolean esCte esTambienCte

termino() ( + termino() )

esCte = termino() ( + esTambienCte = termino() esCte = esCte ampamp esTambienCte )

return esCte

boolean termino() boolean esCte esTambienCte

factor() ( factor() )

esCte = factor() ( esTambienCte = factor() esCte = esCte ampamp esTambienCte )

return esCte

20

boolean factor() boolean esCte

ltconstantegt return true | ltvariablegt return false | ( esCte = expresion() ) return esCte

bull Reconsideracioacuten sobre las producciones vaciacuteasAnteriormente se ha comentado que para representar una produccioacuten vaciacutea en una especificacioacuten JavaCC hay que hacer uso de los metasiacutembolos de opcionalidad para ilustrar esta idea se ha incluido como ejem-plo la especificacioacuten de una lista de paraacutemetros compuesta por cero o maacutes expresiones separadas entre siacute por el caraacutecter coma

void parametros() [ expresion() ( expresion() ) ]

Pero si se aprovecha la posibilidad de incorporar acciones sintaacutecticas se tiene una manera de especificar directamente producciones vaciacuteas para conseguirlo basta considerar una produccioacuten vaciacutea (no tiene nada en su parte derecha) acompantildeada de una accioacuten sintaacutectica cuyo bloque de coacutedigo esteacute vaciacuteo Seguacuten esta posibilidad la especificacioacuten anterior podriacutea tambieacuten escribirse asiacute

void parametros() expresion() ( expresion() ) |

En general la accioacuten sintaacutectica asociada a una produccioacuten vaciacutea no tiene por queacute ser un bloque vaciacuteo de coacutedigo por ejemplo si se quiere que el meacutetodo que analiza los paraacutemetros de una llamada devuelva la cantidad de paraacutemetros encontrados se podriacutea escribir la siguiente especificacioacuten

int parametros()

int cantidad

( expresion() cantidad = 1

( expresion() cantidad++ )

| cantidad = 0 )

return cantidad

y si no quisiera ponerse expliacutecitamente la alternativa de la produccioacuten vaciacutea podriacutea escribirse

int parametros() int cantidad = 0

21

[ expresion() cantidad++ ( expresion() cantidad++ ) ]

return cantidad

Comunicacioacuten entre los analizadores leacutexico y sintaacutecticoCada vez que el analizador sintaacutectico realiza una llamada al analizador lexicograacutefico recibe como resultado de la llamada una representacioacuten de la pieza sintaacutectica encontrada en el texto analizado En los analizadores generados por JavaCC la comunicacioacuten de la pieza se efectuacutea mediante un valor de clase Token Esta cla-se estaacute definida en el fichero Tokenjava que se obtiene en todo caso siempre con el mismo contenido cualquiera que sea la especificacioacuten proporcionada como entrada al generador

Entre los campos de la clase Token se encuentran los siguientes

public int kind

nuacutemero entero que sirve de representacioacuten interna de la pieza sintaacutectica estos nuacutemeros asociados a las piezas son asignados automaacuteticamente por JavaCC los valores considerados pueden consultar-se en el fichero generado de nombre Constantsjava

public String image

cadena que contiene de la secuencia de caracteres (lexema) que constituyen la pieza sintaacutectica co-municada

public int beginLine beginColumn endLine endColumn

posiciones ocupadas (nuacutemeros de liacutenea y columna en el fichero de entrada) por el comienzo y el fi-nal del lexema de la pieza sintaacutectica comunicada

La clase Token tiene otros campos y meacutetodos en el fichero Tokenjava se tienen comentarios des-criptivos sobre todos los campos y meacutetodos que forman parte de la clase todos los campos de la clase se declaran como public

Ya se ha comentado anteriormente que cuando un meacutetodo de anaacutelisis sintaacutectico (asociado a un siacutembolo no terminal) tiene tipo el valor devuelto tras una llamada se puede almacenar en una variable por si se desea utilizar en alguna accioacuten sintaacutectica para ello se escribe

valor = nombreSimbolo()

(siendo valor el nombre de la variable y nombreSimbolo el nombre del siacutembolo no terminal) De manera anaacuteloga el objeto de la clase Token representativo de la pieza sintaacutectica encontrada en la entrada por el analizador lexicograacutefico puede asignarse a una variable del mismo tipo para tener accesibles las ca -racteriacutesticas de la pieza por si se precisaran en alguna accioacuten sintaacutectica para ello en la parte de la especifi -cacioacuten sintaacutectica donde aparezca la pieza sintaacutectica se escribe

dato = lt nombrePieza gt

donde nombrePieza es el nombre de la pieza sintaacutectica y dato es el nombre de una variable de tipo Token en la que se anota el valor indicativo de las propiedades de la pieza sintaacutectica comunicada

Tambieacuten existe otra manera de acceder a las caracteriacutesticas de la pieza sintaacutectica que el analizador lexico -graacutefico comunica al analizador sintaacutectico en la clase del analizador sintaacutectico se tiene declarado el campo

[ static ] public Token token

22

cada vez que el analizador sintaacutectico recibe una pieza sintaacutectica comunicada por el analizador lexicograacutefico se deja anotada en el campo token y ese contenido no se altera hasta que sea proporcionada la siguiente pieza de la entrada

∆ Ejemplo 4

Se considera una ampliacioacuten del ejemplo de presentacioacuten ademaacutes del anaacutelisis de la entrada se pretende obtener una relacioacuten numerada de las variables y las constantes indicando el nuacutemero de liacutenea y el nuacuteme-ro de columna donde estaacuten situadas por ejemplo si en la entrada se tiene la expresioacuten (grabada en dos liacuteneas)

x (y + 11) + 22

en la salida se tendraacute

1- x linea 1 columna 1

2- y linea 1 columna 6

3- 11 linea 1 columna 10

4- 22 linea 2 columna 5

En la especificacioacuten que se propone como solucioacuten puede apreciarse- en la clase del analizador sintaacutectico se han incluido meacutetodos auxiliares en los que se usan los nom-bres image beginLine beginColumn que son campos de un objeto de clase Token

- en la especificacioacuten sintaacutectica de factor se emplea la variable pieza de tipo Token para reco-ger las caracteriacutesticas de las piezas sintaacutecticas detectadas- la variable numero hace de contador para numerar las constantes y variables

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin

static int numero = 0

private static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

private static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

private static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

23

PARSER_END (ExprMin)

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() Token pieza pieza = ltconstantegt numero++ grabarDatosPieza(numero pieza) | pieza = ltvariablegt numero++ grabarDatosPieza(numero pieza) | ( expresion() )

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP | n | t | r

Si se agrupan las dos acciones sintaacutecticas comunes la especificacioacuten de factor tambieacuten puede escri-birse de esta forma

void factor() Token pieza ( pieza = ltconstantegt | pieza = ltvariablegt

) numero++ grabarDatosPieza(numero pieza)

| ( expresion() )

24

Si se accede al campo token de la clase del analizador sintaacutectico se puede resolver este ejemplo pres-cindiendo de la variable pieza

void factor() ( ltconstantegt | ltvariablegt

) numero++ grabarDatosPieza(numero token)

| ( expresion() )

Tareas asociadas a las piezas sintaacutecticasYa se ha visto que es posible asociar bloques de coacutedigo Java a puntos de la estructura sintaacutectica para que se realicen ciertas operaciones cuando el analizador sintaacutectico pase por esa zona de la estructura Tambieacuten es posible asociar coacutedigo Java a las piezas sintaacutecticas para que el analizador lexicograacutefico efectuacutee determina-das tareas cuando detecte en la entrada analizada piezas que tengan asociado coacutedigo

loz Accioacuten ligada a una pieza sintaacutecticaSi se asocia un bloque de coacutedigo a una pieza sintaacutectica se consigue que cada vez que se detecta en la en-trada analizada la presencia de esa pieza se ejecute en ese preciso momento el coacutedigo del bloque asociado a la pieza Estas acciones se denominan ldquoacciones lexicograacuteficasrdquo (lexical actions) el coacutedigo del bloque se traslada literalmente al coacutedigo del analizador lexicograacutefico generado para que se ejecute oportunamente cuando se detecte la pieza ligada al bloque de coacutedigo La accioacuten lexicograacutefica se ejecuta inmediatamente antes que la pieza sea comunicada al analizador sintaacutectico

Se puede asociar una accioacuten lexicograacutefica a una pieza sintaacutectica declarada con TOKEN y tambieacuten a una se-cuencia de caracteres declarada con SKIP ademaacutes de eacutestas hay otras dos formas de declaraciones lexico-graacuteficas (no mencionadas en esta introduccioacuten) que tambieacuten pueden llevar una accioacuten asociada

En una especificacioacuten JavaCC la forma de una pieza sintaacutectica (o bien de una secuencia de caracteres) se define poniendo su descripcioacuten delimitada entre llaves detraacutes de la indicacioacuten TOKEN (o bien SKIP) si se quiere asociar una accioacuten lexicograacutefica a la pieza (o a la secuencia) hay que colocar el bloque de coacutedigo de la accioacuten a continuacioacuten de la descripcioacuten de la forma de la pieza (o de la secuencia) el bloque (delimi-tado por llaves) queda situado entre la descripcioacuten y la llave que cierra la especificacioacuten

La pieza sintaacutectica especial que representa el final del fichero (EOF) tambieacuten puede llevar asociada una ac-cioacuten lexicograacutefica para ello hay que poner

ltgt TOKEN lt EOF gt coacutedigo de la accioacuten asociada

para justificar la presencia de los caracteres ltgt delante de la palabra TOKEN hay que basarse en el con-cepto de ldquoestado lexicograacuteficordquo (cuya explicacioacuten queda fuera del alcance de esta introduccioacuten)

∆ Ejemplo 5

Se considera nuevamente el ejemplo de presentacioacuten como resultado del anaacutelisis de una expresioacuten se pretende obtener una relacioacuten de los pareacutentesis contenidos en la expresioacuten en el mismo orden en que se encuentran ademaacutes por cada salto de liacutenea del texto de entrada se grabaraacute en la salida el caraacutecter y

25

se pasaraacute a una nueva liacutenea tambieacuten se anotaraacute la frase Final del trabajo cuando se alcance el final del fichero de entrada Asiacute por ejemplo si la entrada analizada es

(a + b) (( x )) (y + 7)

los resultados obtenidos seraacuten

() (())()

Final del trabajo

En la especificacioacuten JavaCC que se propone como solucioacuten soacutelo se muestran las reglas sintaacutecticas y le -xicograacuteficas (en el resto no hay cambios) en esta solucioacuten puede apreciarse- las piezas sintaacutecticas correspondientes a los pareacutentesis se han definido de manera nominal para po-der asociarles una accioacuten lexicograacutefica- los caracteres incluidos en la pieza de tipo SKIP se han descompuesto en dos grupos ya que la ac-cioacuten lexicograacutefica incorporada soacutelo afecta a uno de ellos

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ltpAbrirgt expresion() ltpCerrargt

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

TOKEN

26

lt pAbrir ( gt Systemoutprint(()

TOKEN lt pCerrar ) gt Systemoutprint())

ltgtTOKEN lt EOF gt Systemoutprintln ( Final del trabajo )

SKIP n Systemoutprint( n)

SKIP | t | r

loz Bloque de declaraciones lexicograacuteficasCada definicioacuten de una pieza sintaacutectica puede llevar asociada una accioacuten lexicograacutefica que se ejecuta in-mediatamente despueacutes de detectar en la entrada un lexema que se ajusta a la definicioacuten de la pieza cada accioacuten lexicograacutefica es un bloque de coacutedigo Java que puede contener sus propias declaraciones Pero a ve-ces interesa tener declaraciones que sean compartidas por todas las acciones lexicograacuteficas con indepen-dencia de la pieza a la que esteacute asociada cada una de ellas

Para incluir declaraciones que puedan usarse desde el coacutedigo asociado a cualquiera de las piezas sintaacutecti -cas se dispone de una construccioacuten con el siguiente formato

TOKEN_MGR_DECLS

que consta de una palabra reservada seguida del siacutembolo dos puntos seguido de un bloque de coacutedigo Java (delimitado por llaves) esta declaracioacuten se puede colocar en cualquier parte de una especificacioacuten JavaCC si se incluye soacutelo puede ponerse una vez

Todas las declaraciones de variables o de meacutetodos puestas en el bloque de declaraciones lexicograacuteficas compartidas son accesibles desde el coacutedigo de cualquier accioacuten lexicograacutefica la justificacioacuten es sencilla el bloque de declaraciones se traslada al principio del coacutedigo del analizador lexicograacutefico generado y las ac-ciones lexicograacuteficas tambieacuten se trasladan a ese analizador

loz Declaraciones lexicograacuteficas predefinidasDe lo expuesto hasta ahora se aprecia que en una accioacuten lexicograacutefica asociada a una pieza sintaacutectica re-sultan accesibles

- las declaraciones de su propio bloque (si tiene declaraciones)- los componentes del bloque de declaraciones compartidas (si estaacute incluido en la especificacioacuten)

Ademaacutes de esto se tienen otras declaraciones pertenecientes a la clase TokenManager que constituye el analizador lexicograacutefico que tambieacuten son utilizables desde cualquiera de las acciones lexico-graacuteficas estas declaraciones pueden considerarse como ldquopredefinidasrdquo ya que son incorporadas automaacuteti-camente por el generador Entre estas declaraciones accesibles se encuentran las siguientes

[ static ] StringBuffer imagecontiene la cadena de caracteres representativa de la pieza sintaacutectica actual que se acaba de recono-cer en le entrada se trata de un campo de la clase y se declara como static cuando el analizador

27

se ha generado con la opcioacuten (por defecto) Static = true si se generase con la opcioacuten Sta-tic = false no quedariacutea declarada como static

Token matchedToken

variable de clase Token que representa la pieza sintaacutectica actual la que se acaba de encontrar en la entrada se trata de una variable local del meacutetodo al que se ha trasladado el coacutedigo de la accioacuten (y por ello accesible desde ese coacutedigo)

El campo image puede usarse tanto si la accioacuten lexicograacutefica estaacute asociada a una pieza declarada con TOKEN como si lo estaacute a una secuencia declarada con SKIP Sin embargo la variable matchedToken no puede usarse en una accioacuten asociada a un patroacuten especificado como SKIP el motivo es que lo declara-do como SKIP no constituye propiamente una pieza sintaacutectica ya que no se va a comunicar al analizador sintaacutectico

∆ Ejemplo 6

Se propone aquiacute una nueva solucioacuten para el ejemplo nordm 4 expuesto anteriormente

Esta nueva solucioacuten tal y como se expone aquiacute admite algunas mejoras ya que su pretensioacuten soacutelo es ser-vir de ilustracioacuten para algunos aspectos que se acaban de citar (maacutes adelante se propone otra solucioacuten al-ternativa)

En la especificacioacuten de la solucioacuten es posible apreciar esto- las acciones lexicograacuteficas pueden llevar sus propias declaraciones las variables nLineaVar nColumnaVar nLineaCte y nColumnaCte se declaran dentro de las acciones

- la variable matchedToken a la que se hace referencia en el coacutedigo de la accioacuten lexicograacutefica aso-ciada a una variable contiene la informacioacuten sobre la pieza sintaacutectica que se acaba de detectar una va-riable de la expresioacuten analizada anaacutelogamente con la referencia relativa a una constante- el nombre image que aparece en las acciones lexicograacuteficas se refiere al campo de la clase Expr-MinTokenManager (analizador lexicograacutefico) noacutetese que en efecto se comunica como un paraacuteme-tro de clase StringBuffer

- la variable numero se emplea para numerar las constantes y variables encontradas

- los nombres beginLine y beginColumn son campos (puacuteblicos) de la clase Token por ello se pueden aplicar a un objeto de esa clase

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

void unaExpresion()

28

expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n StringBuffer lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

TOKEN lt variable [a-z] gt

int nLineaVar nColumnaVar numero++ nLineaVar = matchedTokenbeginLine nColumnaVar = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaVar nColumnaVar)

TOKEN lt constante ( [0-9] ) + gt

int nLineaCte nColumnaCte

29

numero++ nLineaCte = matchedTokenbeginLine nColumnaCte = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaCte nColumnaCte)

SKIP | n | t | r

La especificacioacuten precedente se puede escribir de otra manera maacutes escueta como se indica a continua-cioacuten (soacutelo se muestran las modificaciones) sobre esta nueva especificacioacuten puede mencionarse- se han eliminado por innecesarias las variables declaradas dentro de las acciones lexicograacuteficas- el nombre image que aparece en el bloque de las declaraciones compartidas se refiere al campo de la clase Token noacutetese que en efecto se aplica a un objeto de esa clase y se comunica como un paraacute -metro de tipo String

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

TOKEN lt variable [a-z] gt

numero++ grabarDatosPieza(numero matchedToken)

TOKEN lt constante ( [0-9] ) + gt

numero++ grabarDatosPieza(numero matchedToken)

loz Accioacuten lexicograacutefica comuacutenSi hubiera que realizar la misma tarea tras la deteccioacuten de todas y cada una de las piezas sintaacutecticas una posibilidad incoacutemoda seriacutea repetir el mismo coacutedigo en todas las acciones lexicograacuteficas para evitar tanta reiteracioacuten se tiene la opcioacuten Common_Token_Action su valor por defecto es false pero si se le asigna el valor true es posible definir una ldquoaccioacuten lexicograacutefica comuacutenrdquo que se escribe una uacutenica vez y se aplica a todas las piezas sintaacutecticas

30

Cuando la opcioacuten Common_Token_Action se establece con el valor true es obligado incluir el blo-que de declaraciones lexicograacuteficas compartidas (TOKEN_MGR_DECLS) Ademaacutes en ese bloque se preci-sa la definicioacuten de un meacutetodo cuyo encabezamiento sea

[ static ] void CommonTokenAction(Token t)

el coacutedigo de este meacutetodo constituye la accioacuten lexicograacutefica comuacuten que se ejecutaraacute despueacutes de la deteccioacuten de cualquier pieza sintaacutectica el meacutetodo tiene un paraacutemetro de tipo Token mediante el que se puede co-municar la informacioacuten correspondiente a la pieza que se acaba de encontrar en la entrada analizada

Si una pieza sintaacutectica tiene asociada una accioacuten lexicograacutefica propia la accioacuten lexicograacutefica comuacuten (si es-taacute definida) se ejecutaraacute inmediatamente despueacutes de realizar la accioacuten propia

La accioacuten lexicograacutefica comuacuten se aplica a todas las piezas sintaacutecticas definidas como TOKEN bien sea no-minalmente (en las reglas lexicograacuteficas) o bien anoacutenimamente (incorporadas dentro de las reglas sintaacutecti-cas) sin embargo no se aplica cuando la secuencia acoplada se corresponde con una expresioacuten regular es-pecificada como SKIP (noacutetese que por el contrario una especificacioacuten SKIP siacute puede tener asociada una accioacuten lexicograacutefica propia)

El coacutedigo del meacutetodo CommonTokenAction se traslada como un componente maacutes del bloque de decla-raciones compartidas (TOKEN_MGR_DECLS) al principio de la clase del analizador lexicograacutefico

∆ Ejemplo 7

Se pretende ahora ampliar el ejemplo de presentacioacuten con la obtencioacuten de una relacioacuten numerada de los lexemas de las piezas sintaacutecticas encontradas en la entrada analizada en el caso de las variables y de las constantes antes del lexema se mostraraacute un indicativo (Var Const) por ejemplo si la expresioacuten de la entrada es

( a + 987 ) b

se grabaraacute la salida

( 1Var a 2+ 3Const 987 4) 5 6Var b 7 8

En la solucioacuten que se propone a continuacioacuten se muestran la definicioacuten de las opciones y las reglas sin-taacutecticas y lexicograacuteficas (en el resto no hay modificaciones) puede resaltarse lo siguiente- las llamadas al meacutetodo CommonTokenAction se incorporan automaacuteticamente no hay que incluir-las en la especificacioacuten como paraacutemetro de llamada a este meacutetodo siempre se emplea el objeto que re -presenta la pieza que se acaba de detectar- la accioacuten lexicograacutefica comuacuten realiza la actualizacioacuten del contador de piezas y la grabacioacuten del lexe-ma de la pieza detectada con su nuacutemero de orden- la accioacuten lexicograacutefica comuacuten tambieacuten se aplica a las piezas sintaacutecticas anoacutenimas (pareacutentesis y ope-radores) y a la pieza especial que representa el final del fichero (EOF)

- las piezas sintaacutecticas ltconstantegt y ltvariablegt tienen asociada una accioacuten lexicograacutefica propia que se ejecuta antes de la accioacuten lexicograacutefica comuacuten- los caracteres que se saltan (SKIP) no estaacuten afectados por la accioacuten lexicograacutefica comuacuten

options Ignore_Case = true Common_Token_Action = true

31

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarIndicativo(String indicativo) Systemoutprint(indicativo + )

static void grabarLexema (int num String lexema) Systemoutprint(lexema + + num + n)

static void CommonTokenAction(Token pieza) numero++ grabarLexema(numero piezaimage)

TOKEN lt variable [a-z] gt

grabarIndicativo(Var)

32

TOKEN lt constante ( [0-9] ) + gt

grabarIndicativo(Const)

SKIP | n | t | r

A modo de ejemplo de aplicacioacuten de la accioacuten lexicograacutefica comuacuten en lo que sigue se ofrece una solucioacuten para la grabacioacuten (en el fichero predefinido de salida) del texto analizado con las liacuteneas numeradas

∆ Ejemplo 8 [ Listado del texto analizado ]Se desea completar el ejemplo de presentacioacuten con la grabacioacuten de un listado con las liacuteneas numeradas del texto analizado (noacutetese que una expresioacuten puede escribirse ocupando varias liacuteneas) No se muestra la especificacioacuten sintaacutectica debido a que no hay ninguna modificacioacuten en ella Los aspectos resentildeables de la solucioacuten que se propone son- se usa la variable numLin como contador de liacuteneas

- en la variable linea se van yuxtaponiendo los lexemas de las piezas sintaacutecticas de una liacutenea esta yuxtaposicioacuten se realiza mediante la accioacuten lexicograacutefica comuacuten- las secuencias reconocidas con los patrones especificados como SKIP no implican la ejecucioacuten de la accioacuten lexicograacutefica comuacuten por ello su lexema (almacenado en la variable image) ha de yuxtaponer-se expliacutecitamente- hay que separar el patroacuten correspondiente al fin de liacutenea para detectar el momento en que ha de gra-barse la liacutenea completa cuyo final se ha alcanzado

options Ignore_Case = true Common_Token_Action = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(nnAnalisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

TOKEN_MGR_DECLS static int numLin = 0 static String linea =

33

static void CommonTokenAction(Token pieza) linea = linea + piezaimage

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP lt | t | r gt linea = linea + image

SKIP n numLin++ Systemoutprintln(numLin + + linea) linea =

A modo de recapitulacioacutenↄ Accioacuten lexicograacutefica Accioacuten sintaacutectica

bull accioacuten lexicograacutefica bloque de coacutedigo Java asociado a una pieza sintaacutectica nominal (TOKEN) o a una secuencia de caracteres (SKIP) se ejecuta cuando se detecta en la entrada la pieza o la secuencia corres-pondiente no se puede aplicar a piezas sintaacutecticas anoacutenimas (autodefinidas con comillas)bull accioacuten sintaacutectica bloque de coacutedigo Java asociado a un punto de la estructura sintaacutectica (el punto indica-do por el sitio de la produccioacuten donde se inserta el bloque) se ejecuta cuando el anaacutelisis de la entrada pa-sa por ese punto de la estructura

ↄ Valor de una pieza sintaacutectica comunicadaEl analizador lexicograacutefico comunica al analizador sintaacutectico una pieza a traveacutes de un valor de la clase Token (fichero Tokenjava) un objeto de la clase Token representa las caracteriacutesticas de la pieza comunicada En la clase Token entre otros se tienen los campos

String imagelexema de la pieza

int beginLine beginColumn endLine endColumnposicioacuten del lexema en el fichero analizado (indicada por los nuacutemeros de fila y de columna de su comienzo y de su terminacioacuten)

ↄ Valor asociado a un siacutembolo

34

En las producciones que especifican la sintaxis se pueden incluir asignaciones que sirven para dejar ano-tado el valor asociado a un siacutembolo

bull siacutembolo no terminal valor = nombreSimbolo()el meacutetodo de anaacutelisis asociado a nombreSimbolo se ha declarado de un cierto tipo (distinto del tipo void) valor ha de ser una variable del mismo tipo que el meacutetodo

bull siacutembolo terminal dato = lt nombrePieza gtel valor asignado es el valor de la clase Token representativo de la pieza sintaacutectica (nominal) dato ha de ser una variable de tipo Token

ↄ Miradas sobre la pieza sintaacutectica comunicadaLa pieza sintaacutectica comunicada puede mirarse desde cada uno de los dos analizadores

bull analizador sintaacutecticola pieza comunicada (desde el analizador lexicograacutefico) se tiene anotada en un campo de la clase del analizador sintaacutectico declarado como

(static) public Token token

este valor es accesible desde el coacutedigo de todos los meacutetodos de anaacutelisis sintaacutectico el contenido del campo variacutea cuando se recibe una nueva pieza sintaacutectica

bull analizador lexicograacuteficola pieza dispuesta para ser comunicada (al analizador sintaacutectico) se tiene anotada en una variable local declarada como

Token matchedToken

este valor es accesible para el coacutedigo de todas las acciones lexicograacuteficas

ↄ El lexema de la pieza comunicadaEl lexema de la pieza sintaacutectica comunicada se encuentra disponible en dos sitios distintos que compar-ten el mismo nombre pero que son de distinto tipo y se usan de distinta manera

bull String image

es un campo de la clase TOKEN puede consultarse en relacioacuten con un objeto de esa clase que esteacute disponible

bull StringBuffer image

es un campo de la clase del analizador lexicograacutefico ( ∙ ∙ ∙ ∙ TokenManagerjava) es accesible desde el coacutedigo de la clase y por lo tanto desde todas las acciones lexicograacuteficas (bloques de coacute-digo que estaacuten incorporados al analizador lexicograacutefico)

se trata de un valor disponible tanto para las piezas sintaacutecticas nominales (TOKEN) como para las secuencias que se saltan (SKIP)

ↄ Resumen relativo a la lexicografiacutea

TOKEN pieza sintaacutectica nominal∙ ∙ ∙ pieza sintaacutectica anoacutenimaSKIP secuencia de caracteres que no forman pieza

35

Precisiones sobre el analizador sintaacutectico generadoPuede decirse que en su funcionamiento maacutes sencillo cuando no se emplean las posibilidades de examen por adelantado (ldquolookaheadrdquo) JavaCC comprueba que las reglas sintaacutecticas cumplen la condicioacuten LL(1) y generan un analizador descendente-predictivo-recursivo Pero esta apreciacioacuten hay que matizarla ya que el analizador sintaacutectico generado no se ajusta en sentido estricto al modelo de analizador LL(1)

En el modelo de anaacutelisis LL(1) el orden del examen de las distintas alternativas de expansioacuten de un siacutembo-lo no terminal es indiferente ya que se conocen los siacutembolos directores de cada una de ellas (que resultan ser conjuntos disjuntos) en la implementacioacuten del subprograma de anaacutelisis asociado al siacutembolo no terminal ltNombreSimbgt cuyas producciones son

ltNombreSimbgt = α1

| α2

∙ ∙ ∙

| αn

se podriacutea preguntar en cualquier orden si la pieza actual pertenece al conjunto de siacutembolos directores de las distintas reglas Sin embargo en el analizador sintaacutectico generado por JavaCC la implementacioacuten del meacute-todo asociado al siacutembolo no terminal ltNombreSimbgt no sigue esa pauta

Si las anteriores reglas sintaacutecticas se transcriben en JavaCC de la forma

void nombreSimb () α1-jcc

| α2-jcc

∙ ∙ ∙

| αn-jcc

1

accioacutenlexicograacutefica

comuacuten

Commom -TokenAction2

accioacutenlexicograacutefica

propia

(accioacuten ligadaa una pieza)3

bloque dedeclaracioneslexicograacuteficas

TOKEN_MGR_DECLS4

declaracioneslexicograacuteficaspredefinidas

(disponibilidad

de uso)TOKENsisisisi∙ ∙ ∙sino--SKIPnosisisi

Las indicaciones si de las columnas 3 y 4 son consecuencia de las indicaciones de la columna 2

La accioacuten lexicograacutefica comuacuten se ejecuta despueacutes de la accioacuten lexicograacutefica propia

36

el meacutetodo de anaacutelisis generado procede de la siguiente manera

se van considerando las alternativas sucesivamente en el mismo orden en el que estaacuten especifica-das primero la de α1 (α1-jcc) despueacutes la de α2 (α2-jcc) etc

para cada alternativa seleccionada se comprueba si la pieza por adelantado coincide con alguno de los siacutembolos iniciales de la parte derecha y si hay coincidencia se prosigue el anaacutelisis con ella si no hay coincidencia se pasa a la alternativa siguiente

siguiendo con este orden si se llega al final de las alternativas sin haberse encontrado coincidencia alguna se produce un error sintaacutectico

En el analizador generado por JavaCC hay una peculiaridad que no se tiene en los analizadores LL(1) es-trictamente considerados la palabra vaciacutea siacute se considera como posible siacutembolo inicial de la parte derecha una regla (esto ocurre siempre que la parte derecha es anulable) De esta peculiaridad se deriva una conse-cuencia que ha de tenerse en cuenta al escribir la especificacioacuten dado que siempre es posible considerar la palabra vaciacutea como la siguiente pieza por adelantado si se llega a examinar la alternativa correspondiente a una produccioacuten anulable el analizador generado siempre seleccionaraacute esa alternativa con independencia de la siguiente pieza que esteacute presente en la entrada que queda por analizar

Asiacute pues si alguna de las producciones que definen un siacutembolo no terminal es anulable es importante el or-den en que se escriben las alternativas en una especificacioacuten sintaacutectica JavaCC el funcionamiento del ana-lizador generado siacute depende de la colocacioacuten elegida para las reglas

Para ilustrar estas peculiaridades del analizador sintaacutectico generado por JavaCC a continuacioacuten se expone un ejemplo

∆ Ejemplo 9

Se quiere obtener un analizador leacutexico-sintaacutectico de textos que se ajustan al siguiente formato una se-cuencia de uno o maacutes nombres seguida del siacutembolo igual seguido de un nuacutemero entero si la secuencia tiene maacutes de un nombre han de estar separados bien por una coma en todos los casos o bien por el siacutem-bolo dos puntos en todos los casos esto es son correctas las secuencias

uno dos tres = 123exclusivo = 0Abajo Arriba = 57

y son incorrectas las secuenciasuno dos tres = 123

exclusivo inclusive = 0

fuera dentro = 56

Una gramaacutetica que define las secuencias con este formato es

ltListagt = nombre ltOtrosNombresgt = numero

ltOtrosNombresgt = nombre nombre | nombre nombre | ε

se trata de una gramaacutetica que cumple la condicioacuten LL(1)- el siacutembolo director para la alternativa vaciacutea es el siacutembolo igual es evidente pues que las tres reglas de ltOtrosNombresgt tienen siacutembolos directores distintos

37

- la decisioacuten de abandonar la repeticioacuten del anaacutelisis de un nuevo nombre (en las dos primeras alterna-tivas de ltOtrosNombresgt) se puede tomar ante la presencia del siacutembolo igual

En la implementacioacuten de un analizador sintaacutectico LL(1) en sentido estricto la seleccioacuten de la alternativa vaciacutea para expandir el siacutembolo ltOtrosNombresgt se hariacutea tras comprobar expliacutecitamente que la pie-za sintaacutectica por adelantado es el siacutembolo igual

El funcionamiento del analizador generado por JavaCC depende del orden de colocacioacuten de las reglas A continuacioacuten se exponen varias posibilidades de especificacioacuten y se comenta el analizador generado en cada caso

bull Especificacioacuten primeraSe transcriben literalmente las reglas tal y como se han escrito antes en la notacioacuten BNF noacutetese que para poder codificar la parte derecha de la regla vaciacutea ha de incluirse una accioacuten sintaacutectica vaciacutea

PARSER_BEGIN (Igualdad)

public class Igualdad

public static void main (String[] argum) throws ParseException Igualdad analisis = new Igualdad(Systemin) analisissecuencia() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (Igualdad)

void secuencia() lista() ltEOFgt

void lista() lt nombre gt otrosNombres() = lt numero gt

void otrosNombres() lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt ) |

TOKEN lt nombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN lt numero ( [ 0 - 9 ] )+ gt

SKIP | r | n

38

El analizador generado funciona correctamente puesto que en el meacutetodo otrosNombres se selec-ciona la alternativa vaciacutea despueacutes de haber comprobado que la pieza por adelantado no es siacutembolo ini-cial de ninguna de las dos alternativas precedentes (no es una coma ni un siacutembolo dos puntos)

bull Especificacioacuten segundaSe considera la misma especificacioacuten anterior pero cambiando el orden de colocacioacuten de las reglas ahora la produccioacuten vaciacutea se coloca como primera alternativa (soacutelo se muestran las partes modifica-das)

void otrosNombres() | lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt )

Con la entrada de esta especificacioacuten el generador JavaCC produce un mensaje en el que se avisa de que las dos alternativas que estaacuten colocadas detraacutes de la alternativa vaciacutea nunca podraacuten seleccionarse aun asiacute se genera un analizador Pero es un analizador que funciona incorrectamente asiacute por ejemplo la entrada

mxa nyz = 57

se considera incorrecta despueacutes de visto el primer nombre se intenta aplicar la produccioacuten vaciacutea (se espera encontrar el siacutembolo igual) pero habriacutea que aplicar la regla cuyo siacutembolo director es la coma

bull Especificacioacuten terceraSe utilizan las posibilidades de la notacioacuten BNF-Ampliada para prescindir de la especificacioacuten expliacuteci-ta de la palabra vaciacutea se aprovecha que JavaCC admite el operador + para indicar repeticiones de una o maacutes veces la alternativa vaciacutea estaacute impliacutecitamente contemplada en la posibilidad de cero repeti-ciones que contempla el operador (soacutelo se muestran las partes modificadas en la especificacioacuten)

void otrosNombres() ( lt nombre gt )+ | ( lt nombre gt )

El meacutetodo otrosNombres() generado hace las comprobaciones en el siguiente orden

- se comprueba si la pieza por adelantado es una coma (siacutembolo inicial de la primera alternativa) y en este caso se selecciona la primera regla- si no es una coma se comprueba si es un siacutembolo dos puntos (siacutembolo inicial de la segunda alter-nativa) y en este caso se selecciona la segunda regla- si no es un siacutembolo dos puntos se considera la palabra vaciacutea como siacutembolo inicial (siempre posi-ble impliacutecitamente) para la segunda alternativa en este caso para que no haya error la pieza por adelantado ha de ser un siacutembolo igual

Se trata pues de un analizador que funciona correctamente

bull Especificacioacuten cuarta

39

Se pone la misma especificacioacuten anterior pero cambiando el orden de consideracioacuten de la regla vaciacutea ahora se incluye impliacutecitamente en la primera alternativa (soacutelo se muestran las partes modificadas)

void otrosNombres() ( lt nombre gt ) | ( lt nombre gt )+

Para esta especificacioacuten el generador JavaCC emite un aviso en el que se indica que la segunda alter -nativa nunca se seleccionaraacute no obstante se genera un analizador que no funciona como se supone que deberiacutea hacerlo por ejemplo el anaacutelisis de la entrada

nyz mxa = 1

produce un error despueacutes de tratado el primer nombre se intenta aplicar la produccioacuten vaciacutea (corres-pondiente a la repeticioacuten de cero veces contemplada en la primera regla) por lo que no se encuentra el esperado siacutembolo igual

40

Page 14: JavaCC

u | o | i | e | auna cualquiera de las vocales minuacutesculas (pero soacutelo una de ellas)

[ A E I O U ]una cualquiera de las vocales mayuacutesculas (pero soacutelo una de ellas)

[ a - z ntilde ]una cualquiera de las letras minuacutesculas incluyendo la letra ntilde

[ a - z A - Z 0 - 9 ]una cualquiera de las letras o de las cifras decimales

([ 0 - 9 ])+constante entera (nuacutemero de una o maacutes cifras)

noacutetese que es incorrecto poner [ 0 - 9 ]+

( + | - )([ 0 - 9 ])+constante entera precedida opcionalmente por un signo

noacutetese que la primera cifra ha de seguir inmediatamente al signo (si existe)

([+ -])([ 0 - 9 ])+representa lo mismo que la expresioacuten anterior

[ a - z ] ([ a - z 0 - 9 ])una letra minuacutescula seguida de cero o maacutes letras minuacutesculas o cifras decimales

~[a - z ntilde A - Z Ntilde]cualquier caraacutecter que no sea una letra

noacutetese que la expresioacuten representa un uacutenico caraacutecter

~[n r]cualquier caraacutecter (un uacutenico caraacutecter) excepto el fin de liacutenea y el retorno de carro

~[ ]cualquier caraacutecter del alfabeto (un uacutenico caraacutecter)

([ 0 - 9 ])+ ( ([ 0 - 9 ])+)constante aritmeacutetica con parte decimal opcional

Los modernossecuencia de catorce caracteres Los modernos

noacutetese el uso de para imponer la condicioacuten de caraacutecter del siacutembolo comillas

nsecuencia de cuatro caracteres n

noacutetese que aquiacute no se representa la secuencia de escape

bull Piezas sintaacutecticas internas (ldquoprivaterdquo)

13

Es posible definir piezas sintaacutecticas auxiliares llamadas ldquointernasrdquo porque no se comunican al analizador sintaacutectico sino que soacutelo sirven para facilitar la escritura de la forma de las piezas sintaacutecticas que siacute se co-munican con el uso de las piezas internas es posible evitar repeticiones de escritura y hacer maacutes legible la especificacioacuten de las caracteriacutesticas lexicograacuteficas Las piezas internas permiten dar nombre a subexpre-siones regulares que a su vez pueden emplearse como componentes para escribir expresiones maacutes com-plejas que incorporan esos nombres Para indicar que una pieza sintaacutectica se considere como interna se pone el siacutembolo delante del nombre asociado a la pieza

A continuacioacuten se muestra un ejemplo tiacutepico de utilizacioacuten de las piezas internas en una especificacioacuten le-xicograacutefica en JavaCC Se quieren definir tres piezas sintaacutecticas que representan un nombre una constante entera y una constante decimal una posible especificacioacuten es

TOKEN ltnombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN ltcteEntera ( [ 0 - 9 ] )+ gt

TOKEN ltcteDecimal ( [ 0 - 9 ] )+ ( [ 0 - 9 ] )+ gt

Con la inclusioacuten de tres piezas sintaacutecticas internas auxiliares esta misma especificacioacuten podriacutea escribirse de la siguiente manera

TOKEN lt letra [ a - z A - Z ntilde Ntilde ] gt

TOKEN lt cifra [ 0 - 9 ] gt

TOKEN lt numero ( ltcifragt )+ gt

TOKEN lt nombre ( ltletragt )+ gt

TOKEN lt cteEntera ltnumerogt gt

TOKEN lt cteDecimal ltnumerogt ltnumerogt gt

Los nombres letra cifra y numero son exclusivos (internos) del analizador lexicograacutefico y no se pueden emplear como nombres de piezas en la especificacioacuten de las reglas sintaacutecticas cuando esos nom-bres se usan en la definicioacuten de otras piezas han de escribirse delimitados por los siacutembolos lt y gt

bull Distincioacuten restringida entre minuacutesculas y mayuacutesculasSe tiene la opcioacuten Ignore_Case para determinar si en el anaacutelisis del texto de entrada se distingue entre las letras minuacutesculas y las mayuacutesculas el valor por defecto false indica que siacute se diferencian si se asig-na el valor true a esta opcioacuten entonces no se diferencian en ninguacuten caso con independencia de que en los patrones que se definan para las piezas sintaacutecticas se pongan minuacutesculas o mayuacutesculas

Pero a veces conviene que la diferenciacioacuten entre minuacutesculas y mayuacutesculas no se aplique de manera ge-neral a toda la entrada sino en particular a las secuencias de caracteres que se acoplen a ciertos patrones Para este fin existe la posibilidad de establecer que en la expresioacuten regular que define el patroacuten de una determinada pieza sintaacutectica no se diferencie entre minuacutesculas y mayuacutesculas ello se consigue poniendo

14

[ IGNORE_CASE ]

inmediatamente detraacutes de la palabra TOKEN con la que empieza la especificacioacuten de la pieza sintaacutectica en la que se pretende la no distincioacuten entre minuacutesculas y mayuacutesculas

Noacutetese que ignore_case tiene dos significados

- es el nombre de un paraacutemetro de la seccioacuten de opciones (puede ponerse en minuacutesculas o mayuacutesculas)- es una palabra reservada que puede aplicarse (escribieacutendola entre corchetes y en mayuacutesculas) a la de -finicioacuten de una pieza sintaacutectica

Por ejemplo sea un lenguaje que en general distingue entre minuacutesculas y mayuacutesculas pero en la codifi -cacioacuten del exponente de una constante real exponencial puede ponerse la letra e en minuacutescula o en ma-yuacutescula En la especificacioacuten JavaCC para ese lenguaje la opcioacuten Ignore_Case ha de tomar el valor false en la definicioacuten de una constante exponencial son posibles las siguientes formas equivalentes

1) sin hacer uso de IGNORE_CASE

TOKEN lt cifras ( [ 0 - 9 ] )+ gt

TOKEN lt signo [+ -] gt

TOKEN lt constante

ltcifrasgt ( ltcifrasgt) [e E] (ltsignogt) ltcifrasgt gt

2) aplicando IGNORE_CASE

TOKEN lt cifras ( [ 0 - 9 ] )+ gt

TOKEN lt signo [+ -] gt

TOKEN [IGNORE_CASE] lt constante

ltcifrasgt ( ltcifrasgt) e (ltsignogt) ltcifrasgt gt

bull Secuencias de caracteres sin efecto en la estructura sintaacutecticaEn los lenguajes de programacioacuten suele haber secuencias de caracteres que no forman parte de la estructu-ra sintaacutectica de un programa esas secuencias son detectadas por el analizador lexicograacutefico pero no son comunicadas al sintaacutectico no han de considerarse como piezas sino que han de saltarse En JavaCC se pueden definir estas secuencias mediante la palabra reservada SKIP la notacioacuten de una especificacioacuten con la palabra SKIP de una secuencia que ha de saltarse es la misma que la notacioacuten usada para las espe-cificaciones con la palabra TOKEN de las piezas que han de comunicarse

Los dos casos maacutes habituales de secuencias que no se comunican al analizador sintaacutectico son

a) los caracteres separadores que no tienen significado en el texto analizadoen los lenguajes de codificacioacuten en formato libre hay caracteres del texto fuente que han de ignorarse en la mayoriacutea de los casos estos caracteres son el espacio en blanco el tabulador el retorno de carro y el fin de liacutenea esta caracteriacutestica se especifica

SKIP |t | r |n

b) los comentarios

15

en el anaacutelisis sintaacutectico de un programa los comentarios han de ignorarse una manera de especificar esta circunstancia es poniendo en una definicioacuten SKIP la expresioacuten regular que denota la forma de los comentarios del lenguaje por ejemplo para los comentarios de una liacutenea de Java se pondriacutea

SKIP

lt (~ [n r]) gt

Tareas asociadas a la estructura sintaacutecticaJavaCC genera un analizador sintaacutectico descendente recursivo constituido por un conjunto de meacutetodos ca-da siacutembolo no terminal de la gramaacutetica sintaacutectica tiene asociado un meacutetodo de anaacutelisis (el meacutetodo tiene el mismo nombre que el siacutembolo correspondiente) Un meacutetodo de anaacutelisis ademaacutes de la realizacioacuten de la parte del anaacutelisis sintaacutectico que le corresponde puede llevar a cabo otras tareas complementarias que sirvan para efectuar un anaacutelisis maacutes complejo del texto de entrada

loz Bloque para un siacutemboloAl describir la forma de las reglas sintaacutecticas de un siacutembolo no terminal en una especificacioacuten JavaCC ya se ha visto que detraacutes de la definicioacuten del nombre del siacutembolo y antes de empezar con las diferentes alter-nativas que tiene asociadas se incluye un bloque de coacutedigo Java (en el ejemplo de presentacioacuten este bloque estaacute vaciacuteo en todos los siacutembolos no terminales de la gramaacutetica) Cada bloque situado en esa posicioacuten puede considerarse ligado al siacutembolo no terminal en cuya descripcioacuten estaacute incluido el coacutedigo Java del bloque se traslada literalmente al principio del meacutetodo generado correspondiente al siacutembolo no terminal y por lo tan-to es lo primero que se considera (se ejecuta) cuando en el transcurso del proceso de anaacutelisis se realiza una llamada al meacutetodo Asiacute pues cada siacutembolo no terminal siempre tiene asociado un uacutenico bloque de coacute-digo Java ndashbloque de presencia obligada aunque pueda estar vaciacuteo- en este bloque se pueden poner de-claraciones y sentencias

loz Acciones sintaacutecticasAdemaacutes del bloque asociado a cada uno de los siacutembolos no terminales de la gramaacutetica se pueden incluir otros bloques de coacutedigo Java intercalados en cualquiera de las partes derechas de las reglas sintaacutecticas Los bloques incluidos en las partes derechas se denominan ldquoacciones sintaacutecticasrdquo (parser actions) ya que son trozos de coacutedigo que pueden considerarse asociados a los puntos de la estructura sintaacutectica donde estaacuten co-locados Una accioacuten sintaacutectica se puede poner en cualquier posicioacuten al principio al final o en el medio de la parte derecha de una regla precedida o seguida de siacutembolos terminales o no terminales el coacutedigo Java de una accioacuten sintaacutectica se traslada literalmente al meacutetodo asociado al siacutembolo no terminal de la parte izquierda de la regla de manera que se ejecuta cada vez que el analizador transcurre por el punto de la estructura sintaacutectica donde se ha incorporado la accioacuten (esto es las acciones sintaacutecticas se incorporan al coacutedigo del analizador sintaacutectico) No obstante su nombre en los bloques de las acciones sintaacutecticas suele ponerse coacutedigo dedicado a la realizacioacuten de tareas semaacutenticas

Si en el bloque asociado a un siacutembolo no terminal se ponen declaraciones los nombres ahiacute definidos son accesibles en todas las acciones sintaacutecticas intercaladas en las partes derechas de las reglas de ese siacutembolo la justificacioacuten es sencilla el coacutedigo del bloque asociado al siacutembolo se incluye al principio de un meacutetodo y el coacutedigo de las acciones pertenece a ese mismo meacutetodo

∆ Ejemplo 1

Se considera de nuevo el ejemplo de presentacioacuten ahora se pretende completar el anaacutelisis leacutexico-sintaacutec-tico con la realizacioacuten de las siguientes tareas

- cada vez que se analiza una expresioacuten un teacutermino o un factor se graba el nombre del siacutembolo no terminal correspondiente y

16

- cuando se analiza un teacutermino en el que hay operadores multiplicativos se cuenta su nuacutemero y se graba el resultado (noacutetese que se cuentan los operadores de cada teacutermino pero no se acumula la cuenta de todos los operadores de la expresioacuten)

Para la realizacioacuten de estas tareas se antildeaden bloques de coacutedigo Java En la especificacioacuten que se describe a continuacioacuten puede apreciarse- para los siacutembolos no terminales expresion termino y factor se ha incluido en los blo-ques correspondientes la operacioacuten de grabacioacuten de su nombre que se ejecutaraacute cada vez que se efec-tuacutee una llamada al meacutetodo- en el bloque asociado a termino se tiene la declaracioacuten del contador de operadores (que seraacute una variable local del meacutetodo)- en la parte derecha de la regla que define la estructura de un teacutermino se han incluido dos acciones sintaacutecticas una para contar la cantidad de operadores (situada detraacutes de la pieza sintaacutectica del opera-dor) y otra para grabar la cantidad de operadores del teacutermino (situada al final de la parte derecha)- se ha definido el meacutetodo grabar para facilitar la escritura del coacutedigo Java

options Ignore_Case = true

PARSER_BEGIN (ExprMin) public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

private static void grabar(String nombre) Systemoutprintln ( -gt + nombre + n)

PARSER_END (ExprMin)

void unaExpresion()

expresion() ltEOFgt

void expresion() grabar(Expresion)

termino() ( + termino() )

void termino() int nAst = 0 grabar(Termino)

17

factor() ( nAst++ factor() ) if (nAst gt 0) Systemoutprintln (n Asteriscos + nAst + n)

void factor() grabar(Factor)

ltconstantegt | ltvariablegt | ( expresion() )

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP | t | n | r

Cuando se incluyen acciones sintaacutecticas la legibilidad del aspecto sintaacutectico de la especificacioacuten queda oscurecida para paliar ese inconveniente puede antildeadirse un comentario que muestre la regla sintaacutectica escueta sin las acciones por ejemplo podriacutea escribirse

void termino() int nAst = 0 grabar(Termino)

factor() ( factor() )

factor()

( nAst++ factor() )

if (nAst gt 0) Systemoutprintln (n Asteriscos + nAst + n)

loz Valor comunicado por un meacutetodoEn todo lo visto hasta ahora los meacutetodos de anaacutelisis sintaacutectico generados por JavaCC no devuelven valor alguno al terminar su tarea de anaacutelisis ya que en la especificacioacuten siempre se ha declarado el nombre de un meacutetodo precedido de void (son meacutetodos de tipo void) Pero en general un meacutetodo de anaacutelisis sintaacutec-tico puede ser de cualquier tipo (primitivo o no) por ello un meacutetodo puede devolver un valor de cualquier tipo (de cualquier clase) al meacutetodo desde el que se efectuoacute la llamada

Esta posibilidad de devolucioacuten de valores tiene una utilidad fundamental en la implementacioacuten de analiza-dores de lenguajes permite la comunicacioacuten de datos (atributos de las entidades analizadas) entre los dife -rentes meacutetodos que colaboran en el anaacutelisis del programa completo

En una especificacioacuten JavaCC la llamada a un meacutetodo ocurre cuando en la parte derecha de una regla estaacute el nombre del siacutembolo no terminal asociado al meacutetodo Cuando un meacutetodo devuelve un valor (tiene un tipo distinto de void) es habitual la recepcioacuten del valor devuelto en una variable en la propia especificacioacuten

18

se puede indicar el nombre de la variable a la que se asignaraacute el valor devuelto tras la llamada para ello se emplea una notacioacuten ideacutentica a la forma que tienen las sentencias de asignacioacuten en el lenguaje Java

dato = nombreSimb()

donde dato es el nombre de la variable a la que se asigna el valor devuelto por el meacutetodo llamado esa va -riable ha de estar oportunamente declarada del mismo tipo que dicho meacutetodo

∆ Ejemplo 2

En el mismo ejemplo de presentacioacuten se desea ademaacutes del anaacutelisis leacutexico-sintaacutectico de la entrada contar la cantidad de operadores que hay en la expresioacuten A continuacioacuten se expone una especificacioacuten JavaCC que es una posible solucioacuten soacutelo se muestran las reglas sintaacutecticas (el resto de la especificacioacuten es igual que en el ejemplo de presentacioacuten)

En la especificacioacuten que se propone puede apreciarse- los siacutembolos no terminales expresion termino y factor se han declarado de tipo int los meacutetodos correspondientes devuelven un valor de tipo entero- el valor devuelto por los meacutetodos sirve para propagar a traveacutes de la estructura sintaacutectica la cuenta de los operadores que hay en el texto analizado- por tratarse de meacutetodos que devuelven un valor es preciso incluir las correspondientes sentencias de devolucioacuten return

void unaExpresion() int nOper nOper = expresion() ltEOFgt Systemoutprintln (Cantidad de operadores = + nOper)

int expresion() int n i n = termino() ( + n++ i = termino() n = n + i )

return n

int termino() int n i n = factor() ( n++ i = factor() n = n + i )

return n

int factor() int n ltconstantegt return 0 | ltvariablegt return 0 | ( n = expresion() ) return n

La especificacioacuten de un factor tambieacuten podriacutea escribirse de esta otra manera

19

int factor() int n = 0

( ltconstantegt | ltvariablegt | ( n = expresion() ) )

return n

A continuacioacuten se expone un nuevo ejemplo para ilustrar la utilidad que proporciona el valor devuelto por un meacutetodo de anaacutelisis sintaacutectico para la propagacioacuten (ascendente) de atributos (propiedades de las entida-des analizadas)

∆ Ejemplo 3

Se pretende ampliar el analizador del ejemplo de presentacioacuten para que se emita un mensaje indicativo cuando la expresioacuten analizada sea una expresioacuten en la que soacutelo aparecen constantes en la solucioacuten que se muestra soacutelo se incluyen las reglas sintaacutecticas (en el resto de la especificacioacuten no hay modificacio-nes)

void unaExpresion() boolean esCte

expresion() ltEOFgt

esCte = expresion() ltEOFgt

if (esCte) Systemoutprintln(nExpresion constanten)

boolean expresion() boolean esCte esTambienCte

termino() ( + termino() )

esCte = termino() ( + esTambienCte = termino() esCte = esCte ampamp esTambienCte )

return esCte

boolean termino() boolean esCte esTambienCte

factor() ( factor() )

esCte = factor() ( esTambienCte = factor() esCte = esCte ampamp esTambienCte )

return esCte

20

boolean factor() boolean esCte

ltconstantegt return true | ltvariablegt return false | ( esCte = expresion() ) return esCte

bull Reconsideracioacuten sobre las producciones vaciacuteasAnteriormente se ha comentado que para representar una produccioacuten vaciacutea en una especificacioacuten JavaCC hay que hacer uso de los metasiacutembolos de opcionalidad para ilustrar esta idea se ha incluido como ejem-plo la especificacioacuten de una lista de paraacutemetros compuesta por cero o maacutes expresiones separadas entre siacute por el caraacutecter coma

void parametros() [ expresion() ( expresion() ) ]

Pero si se aprovecha la posibilidad de incorporar acciones sintaacutecticas se tiene una manera de especificar directamente producciones vaciacuteas para conseguirlo basta considerar una produccioacuten vaciacutea (no tiene nada en su parte derecha) acompantildeada de una accioacuten sintaacutectica cuyo bloque de coacutedigo esteacute vaciacuteo Seguacuten esta posibilidad la especificacioacuten anterior podriacutea tambieacuten escribirse asiacute

void parametros() expresion() ( expresion() ) |

En general la accioacuten sintaacutectica asociada a una produccioacuten vaciacutea no tiene por queacute ser un bloque vaciacuteo de coacutedigo por ejemplo si se quiere que el meacutetodo que analiza los paraacutemetros de una llamada devuelva la cantidad de paraacutemetros encontrados se podriacutea escribir la siguiente especificacioacuten

int parametros()

int cantidad

( expresion() cantidad = 1

( expresion() cantidad++ )

| cantidad = 0 )

return cantidad

y si no quisiera ponerse expliacutecitamente la alternativa de la produccioacuten vaciacutea podriacutea escribirse

int parametros() int cantidad = 0

21

[ expresion() cantidad++ ( expresion() cantidad++ ) ]

return cantidad

Comunicacioacuten entre los analizadores leacutexico y sintaacutecticoCada vez que el analizador sintaacutectico realiza una llamada al analizador lexicograacutefico recibe como resultado de la llamada una representacioacuten de la pieza sintaacutectica encontrada en el texto analizado En los analizadores generados por JavaCC la comunicacioacuten de la pieza se efectuacutea mediante un valor de clase Token Esta cla-se estaacute definida en el fichero Tokenjava que se obtiene en todo caso siempre con el mismo contenido cualquiera que sea la especificacioacuten proporcionada como entrada al generador

Entre los campos de la clase Token se encuentran los siguientes

public int kind

nuacutemero entero que sirve de representacioacuten interna de la pieza sintaacutectica estos nuacutemeros asociados a las piezas son asignados automaacuteticamente por JavaCC los valores considerados pueden consultar-se en el fichero generado de nombre Constantsjava

public String image

cadena que contiene de la secuencia de caracteres (lexema) que constituyen la pieza sintaacutectica co-municada

public int beginLine beginColumn endLine endColumn

posiciones ocupadas (nuacutemeros de liacutenea y columna en el fichero de entrada) por el comienzo y el fi-nal del lexema de la pieza sintaacutectica comunicada

La clase Token tiene otros campos y meacutetodos en el fichero Tokenjava se tienen comentarios des-criptivos sobre todos los campos y meacutetodos que forman parte de la clase todos los campos de la clase se declaran como public

Ya se ha comentado anteriormente que cuando un meacutetodo de anaacutelisis sintaacutectico (asociado a un siacutembolo no terminal) tiene tipo el valor devuelto tras una llamada se puede almacenar en una variable por si se desea utilizar en alguna accioacuten sintaacutectica para ello se escribe

valor = nombreSimbolo()

(siendo valor el nombre de la variable y nombreSimbolo el nombre del siacutembolo no terminal) De manera anaacuteloga el objeto de la clase Token representativo de la pieza sintaacutectica encontrada en la entrada por el analizador lexicograacutefico puede asignarse a una variable del mismo tipo para tener accesibles las ca -racteriacutesticas de la pieza por si se precisaran en alguna accioacuten sintaacutectica para ello en la parte de la especifi -cacioacuten sintaacutectica donde aparezca la pieza sintaacutectica se escribe

dato = lt nombrePieza gt

donde nombrePieza es el nombre de la pieza sintaacutectica y dato es el nombre de una variable de tipo Token en la que se anota el valor indicativo de las propiedades de la pieza sintaacutectica comunicada

Tambieacuten existe otra manera de acceder a las caracteriacutesticas de la pieza sintaacutectica que el analizador lexico -graacutefico comunica al analizador sintaacutectico en la clase del analizador sintaacutectico se tiene declarado el campo

[ static ] public Token token

22

cada vez que el analizador sintaacutectico recibe una pieza sintaacutectica comunicada por el analizador lexicograacutefico se deja anotada en el campo token y ese contenido no se altera hasta que sea proporcionada la siguiente pieza de la entrada

∆ Ejemplo 4

Se considera una ampliacioacuten del ejemplo de presentacioacuten ademaacutes del anaacutelisis de la entrada se pretende obtener una relacioacuten numerada de las variables y las constantes indicando el nuacutemero de liacutenea y el nuacuteme-ro de columna donde estaacuten situadas por ejemplo si en la entrada se tiene la expresioacuten (grabada en dos liacuteneas)

x (y + 11) + 22

en la salida se tendraacute

1- x linea 1 columna 1

2- y linea 1 columna 6

3- 11 linea 1 columna 10

4- 22 linea 2 columna 5

En la especificacioacuten que se propone como solucioacuten puede apreciarse- en la clase del analizador sintaacutectico se han incluido meacutetodos auxiliares en los que se usan los nom-bres image beginLine beginColumn que son campos de un objeto de clase Token

- en la especificacioacuten sintaacutectica de factor se emplea la variable pieza de tipo Token para reco-ger las caracteriacutesticas de las piezas sintaacutecticas detectadas- la variable numero hace de contador para numerar las constantes y variables

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin

static int numero = 0

private static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

private static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

private static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

23

PARSER_END (ExprMin)

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() Token pieza pieza = ltconstantegt numero++ grabarDatosPieza(numero pieza) | pieza = ltvariablegt numero++ grabarDatosPieza(numero pieza) | ( expresion() )

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP | n | t | r

Si se agrupan las dos acciones sintaacutecticas comunes la especificacioacuten de factor tambieacuten puede escri-birse de esta forma

void factor() Token pieza ( pieza = ltconstantegt | pieza = ltvariablegt

) numero++ grabarDatosPieza(numero pieza)

| ( expresion() )

24

Si se accede al campo token de la clase del analizador sintaacutectico se puede resolver este ejemplo pres-cindiendo de la variable pieza

void factor() ( ltconstantegt | ltvariablegt

) numero++ grabarDatosPieza(numero token)

| ( expresion() )

Tareas asociadas a las piezas sintaacutecticasYa se ha visto que es posible asociar bloques de coacutedigo Java a puntos de la estructura sintaacutectica para que se realicen ciertas operaciones cuando el analizador sintaacutectico pase por esa zona de la estructura Tambieacuten es posible asociar coacutedigo Java a las piezas sintaacutecticas para que el analizador lexicograacutefico efectuacutee determina-das tareas cuando detecte en la entrada analizada piezas que tengan asociado coacutedigo

loz Accioacuten ligada a una pieza sintaacutecticaSi se asocia un bloque de coacutedigo a una pieza sintaacutectica se consigue que cada vez que se detecta en la en-trada analizada la presencia de esa pieza se ejecute en ese preciso momento el coacutedigo del bloque asociado a la pieza Estas acciones se denominan ldquoacciones lexicograacuteficasrdquo (lexical actions) el coacutedigo del bloque se traslada literalmente al coacutedigo del analizador lexicograacutefico generado para que se ejecute oportunamente cuando se detecte la pieza ligada al bloque de coacutedigo La accioacuten lexicograacutefica se ejecuta inmediatamente antes que la pieza sea comunicada al analizador sintaacutectico

Se puede asociar una accioacuten lexicograacutefica a una pieza sintaacutectica declarada con TOKEN y tambieacuten a una se-cuencia de caracteres declarada con SKIP ademaacutes de eacutestas hay otras dos formas de declaraciones lexico-graacuteficas (no mencionadas en esta introduccioacuten) que tambieacuten pueden llevar una accioacuten asociada

En una especificacioacuten JavaCC la forma de una pieza sintaacutectica (o bien de una secuencia de caracteres) se define poniendo su descripcioacuten delimitada entre llaves detraacutes de la indicacioacuten TOKEN (o bien SKIP) si se quiere asociar una accioacuten lexicograacutefica a la pieza (o a la secuencia) hay que colocar el bloque de coacutedigo de la accioacuten a continuacioacuten de la descripcioacuten de la forma de la pieza (o de la secuencia) el bloque (delimi-tado por llaves) queda situado entre la descripcioacuten y la llave que cierra la especificacioacuten

La pieza sintaacutectica especial que representa el final del fichero (EOF) tambieacuten puede llevar asociada una ac-cioacuten lexicograacutefica para ello hay que poner

ltgt TOKEN lt EOF gt coacutedigo de la accioacuten asociada

para justificar la presencia de los caracteres ltgt delante de la palabra TOKEN hay que basarse en el con-cepto de ldquoestado lexicograacuteficordquo (cuya explicacioacuten queda fuera del alcance de esta introduccioacuten)

∆ Ejemplo 5

Se considera nuevamente el ejemplo de presentacioacuten como resultado del anaacutelisis de una expresioacuten se pretende obtener una relacioacuten de los pareacutentesis contenidos en la expresioacuten en el mismo orden en que se encuentran ademaacutes por cada salto de liacutenea del texto de entrada se grabaraacute en la salida el caraacutecter y

25

se pasaraacute a una nueva liacutenea tambieacuten se anotaraacute la frase Final del trabajo cuando se alcance el final del fichero de entrada Asiacute por ejemplo si la entrada analizada es

(a + b) (( x )) (y + 7)

los resultados obtenidos seraacuten

() (())()

Final del trabajo

En la especificacioacuten JavaCC que se propone como solucioacuten soacutelo se muestran las reglas sintaacutecticas y le -xicograacuteficas (en el resto no hay cambios) en esta solucioacuten puede apreciarse- las piezas sintaacutecticas correspondientes a los pareacutentesis se han definido de manera nominal para po-der asociarles una accioacuten lexicograacutefica- los caracteres incluidos en la pieza de tipo SKIP se han descompuesto en dos grupos ya que la ac-cioacuten lexicograacutefica incorporada soacutelo afecta a uno de ellos

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ltpAbrirgt expresion() ltpCerrargt

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

TOKEN

26

lt pAbrir ( gt Systemoutprint(()

TOKEN lt pCerrar ) gt Systemoutprint())

ltgtTOKEN lt EOF gt Systemoutprintln ( Final del trabajo )

SKIP n Systemoutprint( n)

SKIP | t | r

loz Bloque de declaraciones lexicograacuteficasCada definicioacuten de una pieza sintaacutectica puede llevar asociada una accioacuten lexicograacutefica que se ejecuta in-mediatamente despueacutes de detectar en la entrada un lexema que se ajusta a la definicioacuten de la pieza cada accioacuten lexicograacutefica es un bloque de coacutedigo Java que puede contener sus propias declaraciones Pero a ve-ces interesa tener declaraciones que sean compartidas por todas las acciones lexicograacuteficas con indepen-dencia de la pieza a la que esteacute asociada cada una de ellas

Para incluir declaraciones que puedan usarse desde el coacutedigo asociado a cualquiera de las piezas sintaacutecti -cas se dispone de una construccioacuten con el siguiente formato

TOKEN_MGR_DECLS

que consta de una palabra reservada seguida del siacutembolo dos puntos seguido de un bloque de coacutedigo Java (delimitado por llaves) esta declaracioacuten se puede colocar en cualquier parte de una especificacioacuten JavaCC si se incluye soacutelo puede ponerse una vez

Todas las declaraciones de variables o de meacutetodos puestas en el bloque de declaraciones lexicograacuteficas compartidas son accesibles desde el coacutedigo de cualquier accioacuten lexicograacutefica la justificacioacuten es sencilla el bloque de declaraciones se traslada al principio del coacutedigo del analizador lexicograacutefico generado y las ac-ciones lexicograacuteficas tambieacuten se trasladan a ese analizador

loz Declaraciones lexicograacuteficas predefinidasDe lo expuesto hasta ahora se aprecia que en una accioacuten lexicograacutefica asociada a una pieza sintaacutectica re-sultan accesibles

- las declaraciones de su propio bloque (si tiene declaraciones)- los componentes del bloque de declaraciones compartidas (si estaacute incluido en la especificacioacuten)

Ademaacutes de esto se tienen otras declaraciones pertenecientes a la clase TokenManager que constituye el analizador lexicograacutefico que tambieacuten son utilizables desde cualquiera de las acciones lexico-graacuteficas estas declaraciones pueden considerarse como ldquopredefinidasrdquo ya que son incorporadas automaacuteti-camente por el generador Entre estas declaraciones accesibles se encuentran las siguientes

[ static ] StringBuffer imagecontiene la cadena de caracteres representativa de la pieza sintaacutectica actual que se acaba de recono-cer en le entrada se trata de un campo de la clase y se declara como static cuando el analizador

27

se ha generado con la opcioacuten (por defecto) Static = true si se generase con la opcioacuten Sta-tic = false no quedariacutea declarada como static

Token matchedToken

variable de clase Token que representa la pieza sintaacutectica actual la que se acaba de encontrar en la entrada se trata de una variable local del meacutetodo al que se ha trasladado el coacutedigo de la accioacuten (y por ello accesible desde ese coacutedigo)

El campo image puede usarse tanto si la accioacuten lexicograacutefica estaacute asociada a una pieza declarada con TOKEN como si lo estaacute a una secuencia declarada con SKIP Sin embargo la variable matchedToken no puede usarse en una accioacuten asociada a un patroacuten especificado como SKIP el motivo es que lo declara-do como SKIP no constituye propiamente una pieza sintaacutectica ya que no se va a comunicar al analizador sintaacutectico

∆ Ejemplo 6

Se propone aquiacute una nueva solucioacuten para el ejemplo nordm 4 expuesto anteriormente

Esta nueva solucioacuten tal y como se expone aquiacute admite algunas mejoras ya que su pretensioacuten soacutelo es ser-vir de ilustracioacuten para algunos aspectos que se acaban de citar (maacutes adelante se propone otra solucioacuten al-ternativa)

En la especificacioacuten de la solucioacuten es posible apreciar esto- las acciones lexicograacuteficas pueden llevar sus propias declaraciones las variables nLineaVar nColumnaVar nLineaCte y nColumnaCte se declaran dentro de las acciones

- la variable matchedToken a la que se hace referencia en el coacutedigo de la accioacuten lexicograacutefica aso-ciada a una variable contiene la informacioacuten sobre la pieza sintaacutectica que se acaba de detectar una va-riable de la expresioacuten analizada anaacutelogamente con la referencia relativa a una constante- el nombre image que aparece en las acciones lexicograacuteficas se refiere al campo de la clase Expr-MinTokenManager (analizador lexicograacutefico) noacutetese que en efecto se comunica como un paraacuteme-tro de clase StringBuffer

- la variable numero se emplea para numerar las constantes y variables encontradas

- los nombres beginLine y beginColumn son campos (puacuteblicos) de la clase Token por ello se pueden aplicar a un objeto de esa clase

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

void unaExpresion()

28

expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n StringBuffer lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

TOKEN lt variable [a-z] gt

int nLineaVar nColumnaVar numero++ nLineaVar = matchedTokenbeginLine nColumnaVar = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaVar nColumnaVar)

TOKEN lt constante ( [0-9] ) + gt

int nLineaCte nColumnaCte

29

numero++ nLineaCte = matchedTokenbeginLine nColumnaCte = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaCte nColumnaCte)

SKIP | n | t | r

La especificacioacuten precedente se puede escribir de otra manera maacutes escueta como se indica a continua-cioacuten (soacutelo se muestran las modificaciones) sobre esta nueva especificacioacuten puede mencionarse- se han eliminado por innecesarias las variables declaradas dentro de las acciones lexicograacuteficas- el nombre image que aparece en el bloque de las declaraciones compartidas se refiere al campo de la clase Token noacutetese que en efecto se aplica a un objeto de esa clase y se comunica como un paraacute -metro de tipo String

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

TOKEN lt variable [a-z] gt

numero++ grabarDatosPieza(numero matchedToken)

TOKEN lt constante ( [0-9] ) + gt

numero++ grabarDatosPieza(numero matchedToken)

loz Accioacuten lexicograacutefica comuacutenSi hubiera que realizar la misma tarea tras la deteccioacuten de todas y cada una de las piezas sintaacutecticas una posibilidad incoacutemoda seriacutea repetir el mismo coacutedigo en todas las acciones lexicograacuteficas para evitar tanta reiteracioacuten se tiene la opcioacuten Common_Token_Action su valor por defecto es false pero si se le asigna el valor true es posible definir una ldquoaccioacuten lexicograacutefica comuacutenrdquo que se escribe una uacutenica vez y se aplica a todas las piezas sintaacutecticas

30

Cuando la opcioacuten Common_Token_Action se establece con el valor true es obligado incluir el blo-que de declaraciones lexicograacuteficas compartidas (TOKEN_MGR_DECLS) Ademaacutes en ese bloque se preci-sa la definicioacuten de un meacutetodo cuyo encabezamiento sea

[ static ] void CommonTokenAction(Token t)

el coacutedigo de este meacutetodo constituye la accioacuten lexicograacutefica comuacuten que se ejecutaraacute despueacutes de la deteccioacuten de cualquier pieza sintaacutectica el meacutetodo tiene un paraacutemetro de tipo Token mediante el que se puede co-municar la informacioacuten correspondiente a la pieza que se acaba de encontrar en la entrada analizada

Si una pieza sintaacutectica tiene asociada una accioacuten lexicograacutefica propia la accioacuten lexicograacutefica comuacuten (si es-taacute definida) se ejecutaraacute inmediatamente despueacutes de realizar la accioacuten propia

La accioacuten lexicograacutefica comuacuten se aplica a todas las piezas sintaacutecticas definidas como TOKEN bien sea no-minalmente (en las reglas lexicograacuteficas) o bien anoacutenimamente (incorporadas dentro de las reglas sintaacutecti-cas) sin embargo no se aplica cuando la secuencia acoplada se corresponde con una expresioacuten regular es-pecificada como SKIP (noacutetese que por el contrario una especificacioacuten SKIP siacute puede tener asociada una accioacuten lexicograacutefica propia)

El coacutedigo del meacutetodo CommonTokenAction se traslada como un componente maacutes del bloque de decla-raciones compartidas (TOKEN_MGR_DECLS) al principio de la clase del analizador lexicograacutefico

∆ Ejemplo 7

Se pretende ahora ampliar el ejemplo de presentacioacuten con la obtencioacuten de una relacioacuten numerada de los lexemas de las piezas sintaacutecticas encontradas en la entrada analizada en el caso de las variables y de las constantes antes del lexema se mostraraacute un indicativo (Var Const) por ejemplo si la expresioacuten de la entrada es

( a + 987 ) b

se grabaraacute la salida

( 1Var a 2+ 3Const 987 4) 5 6Var b 7 8

En la solucioacuten que se propone a continuacioacuten se muestran la definicioacuten de las opciones y las reglas sin-taacutecticas y lexicograacuteficas (en el resto no hay modificaciones) puede resaltarse lo siguiente- las llamadas al meacutetodo CommonTokenAction se incorporan automaacuteticamente no hay que incluir-las en la especificacioacuten como paraacutemetro de llamada a este meacutetodo siempre se emplea el objeto que re -presenta la pieza que se acaba de detectar- la accioacuten lexicograacutefica comuacuten realiza la actualizacioacuten del contador de piezas y la grabacioacuten del lexe-ma de la pieza detectada con su nuacutemero de orden- la accioacuten lexicograacutefica comuacuten tambieacuten se aplica a las piezas sintaacutecticas anoacutenimas (pareacutentesis y ope-radores) y a la pieza especial que representa el final del fichero (EOF)

- las piezas sintaacutecticas ltconstantegt y ltvariablegt tienen asociada una accioacuten lexicograacutefica propia que se ejecuta antes de la accioacuten lexicograacutefica comuacuten- los caracteres que se saltan (SKIP) no estaacuten afectados por la accioacuten lexicograacutefica comuacuten

options Ignore_Case = true Common_Token_Action = true

31

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarIndicativo(String indicativo) Systemoutprint(indicativo + )

static void grabarLexema (int num String lexema) Systemoutprint(lexema + + num + n)

static void CommonTokenAction(Token pieza) numero++ grabarLexema(numero piezaimage)

TOKEN lt variable [a-z] gt

grabarIndicativo(Var)

32

TOKEN lt constante ( [0-9] ) + gt

grabarIndicativo(Const)

SKIP | n | t | r

A modo de ejemplo de aplicacioacuten de la accioacuten lexicograacutefica comuacuten en lo que sigue se ofrece una solucioacuten para la grabacioacuten (en el fichero predefinido de salida) del texto analizado con las liacuteneas numeradas

∆ Ejemplo 8 [ Listado del texto analizado ]Se desea completar el ejemplo de presentacioacuten con la grabacioacuten de un listado con las liacuteneas numeradas del texto analizado (noacutetese que una expresioacuten puede escribirse ocupando varias liacuteneas) No se muestra la especificacioacuten sintaacutectica debido a que no hay ninguna modificacioacuten en ella Los aspectos resentildeables de la solucioacuten que se propone son- se usa la variable numLin como contador de liacuteneas

- en la variable linea se van yuxtaponiendo los lexemas de las piezas sintaacutecticas de una liacutenea esta yuxtaposicioacuten se realiza mediante la accioacuten lexicograacutefica comuacuten- las secuencias reconocidas con los patrones especificados como SKIP no implican la ejecucioacuten de la accioacuten lexicograacutefica comuacuten por ello su lexema (almacenado en la variable image) ha de yuxtaponer-se expliacutecitamente- hay que separar el patroacuten correspondiente al fin de liacutenea para detectar el momento en que ha de gra-barse la liacutenea completa cuyo final se ha alcanzado

options Ignore_Case = true Common_Token_Action = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(nnAnalisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

TOKEN_MGR_DECLS static int numLin = 0 static String linea =

33

static void CommonTokenAction(Token pieza) linea = linea + piezaimage

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP lt | t | r gt linea = linea + image

SKIP n numLin++ Systemoutprintln(numLin + + linea) linea =

A modo de recapitulacioacutenↄ Accioacuten lexicograacutefica Accioacuten sintaacutectica

bull accioacuten lexicograacutefica bloque de coacutedigo Java asociado a una pieza sintaacutectica nominal (TOKEN) o a una secuencia de caracteres (SKIP) se ejecuta cuando se detecta en la entrada la pieza o la secuencia corres-pondiente no se puede aplicar a piezas sintaacutecticas anoacutenimas (autodefinidas con comillas)bull accioacuten sintaacutectica bloque de coacutedigo Java asociado a un punto de la estructura sintaacutectica (el punto indica-do por el sitio de la produccioacuten donde se inserta el bloque) se ejecuta cuando el anaacutelisis de la entrada pa-sa por ese punto de la estructura

ↄ Valor de una pieza sintaacutectica comunicadaEl analizador lexicograacutefico comunica al analizador sintaacutectico una pieza a traveacutes de un valor de la clase Token (fichero Tokenjava) un objeto de la clase Token representa las caracteriacutesticas de la pieza comunicada En la clase Token entre otros se tienen los campos

String imagelexema de la pieza

int beginLine beginColumn endLine endColumnposicioacuten del lexema en el fichero analizado (indicada por los nuacutemeros de fila y de columna de su comienzo y de su terminacioacuten)

ↄ Valor asociado a un siacutembolo

34

En las producciones que especifican la sintaxis se pueden incluir asignaciones que sirven para dejar ano-tado el valor asociado a un siacutembolo

bull siacutembolo no terminal valor = nombreSimbolo()el meacutetodo de anaacutelisis asociado a nombreSimbolo se ha declarado de un cierto tipo (distinto del tipo void) valor ha de ser una variable del mismo tipo que el meacutetodo

bull siacutembolo terminal dato = lt nombrePieza gtel valor asignado es el valor de la clase Token representativo de la pieza sintaacutectica (nominal) dato ha de ser una variable de tipo Token

ↄ Miradas sobre la pieza sintaacutectica comunicadaLa pieza sintaacutectica comunicada puede mirarse desde cada uno de los dos analizadores

bull analizador sintaacutecticola pieza comunicada (desde el analizador lexicograacutefico) se tiene anotada en un campo de la clase del analizador sintaacutectico declarado como

(static) public Token token

este valor es accesible desde el coacutedigo de todos los meacutetodos de anaacutelisis sintaacutectico el contenido del campo variacutea cuando se recibe una nueva pieza sintaacutectica

bull analizador lexicograacuteficola pieza dispuesta para ser comunicada (al analizador sintaacutectico) se tiene anotada en una variable local declarada como

Token matchedToken

este valor es accesible para el coacutedigo de todas las acciones lexicograacuteficas

ↄ El lexema de la pieza comunicadaEl lexema de la pieza sintaacutectica comunicada se encuentra disponible en dos sitios distintos que compar-ten el mismo nombre pero que son de distinto tipo y se usan de distinta manera

bull String image

es un campo de la clase TOKEN puede consultarse en relacioacuten con un objeto de esa clase que esteacute disponible

bull StringBuffer image

es un campo de la clase del analizador lexicograacutefico ( ∙ ∙ ∙ ∙ TokenManagerjava) es accesible desde el coacutedigo de la clase y por lo tanto desde todas las acciones lexicograacuteficas (bloques de coacute-digo que estaacuten incorporados al analizador lexicograacutefico)

se trata de un valor disponible tanto para las piezas sintaacutecticas nominales (TOKEN) como para las secuencias que se saltan (SKIP)

ↄ Resumen relativo a la lexicografiacutea

TOKEN pieza sintaacutectica nominal∙ ∙ ∙ pieza sintaacutectica anoacutenimaSKIP secuencia de caracteres que no forman pieza

35

Precisiones sobre el analizador sintaacutectico generadoPuede decirse que en su funcionamiento maacutes sencillo cuando no se emplean las posibilidades de examen por adelantado (ldquolookaheadrdquo) JavaCC comprueba que las reglas sintaacutecticas cumplen la condicioacuten LL(1) y generan un analizador descendente-predictivo-recursivo Pero esta apreciacioacuten hay que matizarla ya que el analizador sintaacutectico generado no se ajusta en sentido estricto al modelo de analizador LL(1)

En el modelo de anaacutelisis LL(1) el orden del examen de las distintas alternativas de expansioacuten de un siacutembo-lo no terminal es indiferente ya que se conocen los siacutembolos directores de cada una de ellas (que resultan ser conjuntos disjuntos) en la implementacioacuten del subprograma de anaacutelisis asociado al siacutembolo no terminal ltNombreSimbgt cuyas producciones son

ltNombreSimbgt = α1

| α2

∙ ∙ ∙

| αn

se podriacutea preguntar en cualquier orden si la pieza actual pertenece al conjunto de siacutembolos directores de las distintas reglas Sin embargo en el analizador sintaacutectico generado por JavaCC la implementacioacuten del meacute-todo asociado al siacutembolo no terminal ltNombreSimbgt no sigue esa pauta

Si las anteriores reglas sintaacutecticas se transcriben en JavaCC de la forma

void nombreSimb () α1-jcc

| α2-jcc

∙ ∙ ∙

| αn-jcc

1

accioacutenlexicograacutefica

comuacuten

Commom -TokenAction2

accioacutenlexicograacutefica

propia

(accioacuten ligadaa una pieza)3

bloque dedeclaracioneslexicograacuteficas

TOKEN_MGR_DECLS4

declaracioneslexicograacuteficaspredefinidas

(disponibilidad

de uso)TOKENsisisisi∙ ∙ ∙sino--SKIPnosisisi

Las indicaciones si de las columnas 3 y 4 son consecuencia de las indicaciones de la columna 2

La accioacuten lexicograacutefica comuacuten se ejecuta despueacutes de la accioacuten lexicograacutefica propia

36

el meacutetodo de anaacutelisis generado procede de la siguiente manera

se van considerando las alternativas sucesivamente en el mismo orden en el que estaacuten especifica-das primero la de α1 (α1-jcc) despueacutes la de α2 (α2-jcc) etc

para cada alternativa seleccionada se comprueba si la pieza por adelantado coincide con alguno de los siacutembolos iniciales de la parte derecha y si hay coincidencia se prosigue el anaacutelisis con ella si no hay coincidencia se pasa a la alternativa siguiente

siguiendo con este orden si se llega al final de las alternativas sin haberse encontrado coincidencia alguna se produce un error sintaacutectico

En el analizador generado por JavaCC hay una peculiaridad que no se tiene en los analizadores LL(1) es-trictamente considerados la palabra vaciacutea siacute se considera como posible siacutembolo inicial de la parte derecha una regla (esto ocurre siempre que la parte derecha es anulable) De esta peculiaridad se deriva una conse-cuencia que ha de tenerse en cuenta al escribir la especificacioacuten dado que siempre es posible considerar la palabra vaciacutea como la siguiente pieza por adelantado si se llega a examinar la alternativa correspondiente a una produccioacuten anulable el analizador generado siempre seleccionaraacute esa alternativa con independencia de la siguiente pieza que esteacute presente en la entrada que queda por analizar

Asiacute pues si alguna de las producciones que definen un siacutembolo no terminal es anulable es importante el or-den en que se escriben las alternativas en una especificacioacuten sintaacutectica JavaCC el funcionamiento del ana-lizador generado siacute depende de la colocacioacuten elegida para las reglas

Para ilustrar estas peculiaridades del analizador sintaacutectico generado por JavaCC a continuacioacuten se expone un ejemplo

∆ Ejemplo 9

Se quiere obtener un analizador leacutexico-sintaacutectico de textos que se ajustan al siguiente formato una se-cuencia de uno o maacutes nombres seguida del siacutembolo igual seguido de un nuacutemero entero si la secuencia tiene maacutes de un nombre han de estar separados bien por una coma en todos los casos o bien por el siacutem-bolo dos puntos en todos los casos esto es son correctas las secuencias

uno dos tres = 123exclusivo = 0Abajo Arriba = 57

y son incorrectas las secuenciasuno dos tres = 123

exclusivo inclusive = 0

fuera dentro = 56

Una gramaacutetica que define las secuencias con este formato es

ltListagt = nombre ltOtrosNombresgt = numero

ltOtrosNombresgt = nombre nombre | nombre nombre | ε

se trata de una gramaacutetica que cumple la condicioacuten LL(1)- el siacutembolo director para la alternativa vaciacutea es el siacutembolo igual es evidente pues que las tres reglas de ltOtrosNombresgt tienen siacutembolos directores distintos

37

- la decisioacuten de abandonar la repeticioacuten del anaacutelisis de un nuevo nombre (en las dos primeras alterna-tivas de ltOtrosNombresgt) se puede tomar ante la presencia del siacutembolo igual

En la implementacioacuten de un analizador sintaacutectico LL(1) en sentido estricto la seleccioacuten de la alternativa vaciacutea para expandir el siacutembolo ltOtrosNombresgt se hariacutea tras comprobar expliacutecitamente que la pie-za sintaacutectica por adelantado es el siacutembolo igual

El funcionamiento del analizador generado por JavaCC depende del orden de colocacioacuten de las reglas A continuacioacuten se exponen varias posibilidades de especificacioacuten y se comenta el analizador generado en cada caso

bull Especificacioacuten primeraSe transcriben literalmente las reglas tal y como se han escrito antes en la notacioacuten BNF noacutetese que para poder codificar la parte derecha de la regla vaciacutea ha de incluirse una accioacuten sintaacutectica vaciacutea

PARSER_BEGIN (Igualdad)

public class Igualdad

public static void main (String[] argum) throws ParseException Igualdad analisis = new Igualdad(Systemin) analisissecuencia() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (Igualdad)

void secuencia() lista() ltEOFgt

void lista() lt nombre gt otrosNombres() = lt numero gt

void otrosNombres() lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt ) |

TOKEN lt nombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN lt numero ( [ 0 - 9 ] )+ gt

SKIP | r | n

38

El analizador generado funciona correctamente puesto que en el meacutetodo otrosNombres se selec-ciona la alternativa vaciacutea despueacutes de haber comprobado que la pieza por adelantado no es siacutembolo ini-cial de ninguna de las dos alternativas precedentes (no es una coma ni un siacutembolo dos puntos)

bull Especificacioacuten segundaSe considera la misma especificacioacuten anterior pero cambiando el orden de colocacioacuten de las reglas ahora la produccioacuten vaciacutea se coloca como primera alternativa (soacutelo se muestran las partes modifica-das)

void otrosNombres() | lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt )

Con la entrada de esta especificacioacuten el generador JavaCC produce un mensaje en el que se avisa de que las dos alternativas que estaacuten colocadas detraacutes de la alternativa vaciacutea nunca podraacuten seleccionarse aun asiacute se genera un analizador Pero es un analizador que funciona incorrectamente asiacute por ejemplo la entrada

mxa nyz = 57

se considera incorrecta despueacutes de visto el primer nombre se intenta aplicar la produccioacuten vaciacutea (se espera encontrar el siacutembolo igual) pero habriacutea que aplicar la regla cuyo siacutembolo director es la coma

bull Especificacioacuten terceraSe utilizan las posibilidades de la notacioacuten BNF-Ampliada para prescindir de la especificacioacuten expliacuteci-ta de la palabra vaciacutea se aprovecha que JavaCC admite el operador + para indicar repeticiones de una o maacutes veces la alternativa vaciacutea estaacute impliacutecitamente contemplada en la posibilidad de cero repeti-ciones que contempla el operador (soacutelo se muestran las partes modificadas en la especificacioacuten)

void otrosNombres() ( lt nombre gt )+ | ( lt nombre gt )

El meacutetodo otrosNombres() generado hace las comprobaciones en el siguiente orden

- se comprueba si la pieza por adelantado es una coma (siacutembolo inicial de la primera alternativa) y en este caso se selecciona la primera regla- si no es una coma se comprueba si es un siacutembolo dos puntos (siacutembolo inicial de la segunda alter-nativa) y en este caso se selecciona la segunda regla- si no es un siacutembolo dos puntos se considera la palabra vaciacutea como siacutembolo inicial (siempre posi-ble impliacutecitamente) para la segunda alternativa en este caso para que no haya error la pieza por adelantado ha de ser un siacutembolo igual

Se trata pues de un analizador que funciona correctamente

bull Especificacioacuten cuarta

39

Se pone la misma especificacioacuten anterior pero cambiando el orden de consideracioacuten de la regla vaciacutea ahora se incluye impliacutecitamente en la primera alternativa (soacutelo se muestran las partes modificadas)

void otrosNombres() ( lt nombre gt ) | ( lt nombre gt )+

Para esta especificacioacuten el generador JavaCC emite un aviso en el que se indica que la segunda alter -nativa nunca se seleccionaraacute no obstante se genera un analizador que no funciona como se supone que deberiacutea hacerlo por ejemplo el anaacutelisis de la entrada

nyz mxa = 1

produce un error despueacutes de tratado el primer nombre se intenta aplicar la produccioacuten vaciacutea (corres-pondiente a la repeticioacuten de cero veces contemplada en la primera regla) por lo que no se encuentra el esperado siacutembolo igual

40

Page 15: JavaCC

Es posible definir piezas sintaacutecticas auxiliares llamadas ldquointernasrdquo porque no se comunican al analizador sintaacutectico sino que soacutelo sirven para facilitar la escritura de la forma de las piezas sintaacutecticas que siacute se co-munican con el uso de las piezas internas es posible evitar repeticiones de escritura y hacer maacutes legible la especificacioacuten de las caracteriacutesticas lexicograacuteficas Las piezas internas permiten dar nombre a subexpre-siones regulares que a su vez pueden emplearse como componentes para escribir expresiones maacutes com-plejas que incorporan esos nombres Para indicar que una pieza sintaacutectica se considere como interna se pone el siacutembolo delante del nombre asociado a la pieza

A continuacioacuten se muestra un ejemplo tiacutepico de utilizacioacuten de las piezas internas en una especificacioacuten le-xicograacutefica en JavaCC Se quieren definir tres piezas sintaacutecticas que representan un nombre una constante entera y una constante decimal una posible especificacioacuten es

TOKEN ltnombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN ltcteEntera ( [ 0 - 9 ] )+ gt

TOKEN ltcteDecimal ( [ 0 - 9 ] )+ ( [ 0 - 9 ] )+ gt

Con la inclusioacuten de tres piezas sintaacutecticas internas auxiliares esta misma especificacioacuten podriacutea escribirse de la siguiente manera

TOKEN lt letra [ a - z A - Z ntilde Ntilde ] gt

TOKEN lt cifra [ 0 - 9 ] gt

TOKEN lt numero ( ltcifragt )+ gt

TOKEN lt nombre ( ltletragt )+ gt

TOKEN lt cteEntera ltnumerogt gt

TOKEN lt cteDecimal ltnumerogt ltnumerogt gt

Los nombres letra cifra y numero son exclusivos (internos) del analizador lexicograacutefico y no se pueden emplear como nombres de piezas en la especificacioacuten de las reglas sintaacutecticas cuando esos nom-bres se usan en la definicioacuten de otras piezas han de escribirse delimitados por los siacutembolos lt y gt

bull Distincioacuten restringida entre minuacutesculas y mayuacutesculasSe tiene la opcioacuten Ignore_Case para determinar si en el anaacutelisis del texto de entrada se distingue entre las letras minuacutesculas y las mayuacutesculas el valor por defecto false indica que siacute se diferencian si se asig-na el valor true a esta opcioacuten entonces no se diferencian en ninguacuten caso con independencia de que en los patrones que se definan para las piezas sintaacutecticas se pongan minuacutesculas o mayuacutesculas

Pero a veces conviene que la diferenciacioacuten entre minuacutesculas y mayuacutesculas no se aplique de manera ge-neral a toda la entrada sino en particular a las secuencias de caracteres que se acoplen a ciertos patrones Para este fin existe la posibilidad de establecer que en la expresioacuten regular que define el patroacuten de una determinada pieza sintaacutectica no se diferencie entre minuacutesculas y mayuacutesculas ello se consigue poniendo

14

[ IGNORE_CASE ]

inmediatamente detraacutes de la palabra TOKEN con la que empieza la especificacioacuten de la pieza sintaacutectica en la que se pretende la no distincioacuten entre minuacutesculas y mayuacutesculas

Noacutetese que ignore_case tiene dos significados

- es el nombre de un paraacutemetro de la seccioacuten de opciones (puede ponerse en minuacutesculas o mayuacutesculas)- es una palabra reservada que puede aplicarse (escribieacutendola entre corchetes y en mayuacutesculas) a la de -finicioacuten de una pieza sintaacutectica

Por ejemplo sea un lenguaje que en general distingue entre minuacutesculas y mayuacutesculas pero en la codifi -cacioacuten del exponente de una constante real exponencial puede ponerse la letra e en minuacutescula o en ma-yuacutescula En la especificacioacuten JavaCC para ese lenguaje la opcioacuten Ignore_Case ha de tomar el valor false en la definicioacuten de una constante exponencial son posibles las siguientes formas equivalentes

1) sin hacer uso de IGNORE_CASE

TOKEN lt cifras ( [ 0 - 9 ] )+ gt

TOKEN lt signo [+ -] gt

TOKEN lt constante

ltcifrasgt ( ltcifrasgt) [e E] (ltsignogt) ltcifrasgt gt

2) aplicando IGNORE_CASE

TOKEN lt cifras ( [ 0 - 9 ] )+ gt

TOKEN lt signo [+ -] gt

TOKEN [IGNORE_CASE] lt constante

ltcifrasgt ( ltcifrasgt) e (ltsignogt) ltcifrasgt gt

bull Secuencias de caracteres sin efecto en la estructura sintaacutecticaEn los lenguajes de programacioacuten suele haber secuencias de caracteres que no forman parte de la estructu-ra sintaacutectica de un programa esas secuencias son detectadas por el analizador lexicograacutefico pero no son comunicadas al sintaacutectico no han de considerarse como piezas sino que han de saltarse En JavaCC se pueden definir estas secuencias mediante la palabra reservada SKIP la notacioacuten de una especificacioacuten con la palabra SKIP de una secuencia que ha de saltarse es la misma que la notacioacuten usada para las espe-cificaciones con la palabra TOKEN de las piezas que han de comunicarse

Los dos casos maacutes habituales de secuencias que no se comunican al analizador sintaacutectico son

a) los caracteres separadores que no tienen significado en el texto analizadoen los lenguajes de codificacioacuten en formato libre hay caracteres del texto fuente que han de ignorarse en la mayoriacutea de los casos estos caracteres son el espacio en blanco el tabulador el retorno de carro y el fin de liacutenea esta caracteriacutestica se especifica

SKIP |t | r |n

b) los comentarios

15

en el anaacutelisis sintaacutectico de un programa los comentarios han de ignorarse una manera de especificar esta circunstancia es poniendo en una definicioacuten SKIP la expresioacuten regular que denota la forma de los comentarios del lenguaje por ejemplo para los comentarios de una liacutenea de Java se pondriacutea

SKIP

lt (~ [n r]) gt

Tareas asociadas a la estructura sintaacutecticaJavaCC genera un analizador sintaacutectico descendente recursivo constituido por un conjunto de meacutetodos ca-da siacutembolo no terminal de la gramaacutetica sintaacutectica tiene asociado un meacutetodo de anaacutelisis (el meacutetodo tiene el mismo nombre que el siacutembolo correspondiente) Un meacutetodo de anaacutelisis ademaacutes de la realizacioacuten de la parte del anaacutelisis sintaacutectico que le corresponde puede llevar a cabo otras tareas complementarias que sirvan para efectuar un anaacutelisis maacutes complejo del texto de entrada

loz Bloque para un siacutemboloAl describir la forma de las reglas sintaacutecticas de un siacutembolo no terminal en una especificacioacuten JavaCC ya se ha visto que detraacutes de la definicioacuten del nombre del siacutembolo y antes de empezar con las diferentes alter-nativas que tiene asociadas se incluye un bloque de coacutedigo Java (en el ejemplo de presentacioacuten este bloque estaacute vaciacuteo en todos los siacutembolos no terminales de la gramaacutetica) Cada bloque situado en esa posicioacuten puede considerarse ligado al siacutembolo no terminal en cuya descripcioacuten estaacute incluido el coacutedigo Java del bloque se traslada literalmente al principio del meacutetodo generado correspondiente al siacutembolo no terminal y por lo tan-to es lo primero que se considera (se ejecuta) cuando en el transcurso del proceso de anaacutelisis se realiza una llamada al meacutetodo Asiacute pues cada siacutembolo no terminal siempre tiene asociado un uacutenico bloque de coacute-digo Java ndashbloque de presencia obligada aunque pueda estar vaciacuteo- en este bloque se pueden poner de-claraciones y sentencias

loz Acciones sintaacutecticasAdemaacutes del bloque asociado a cada uno de los siacutembolos no terminales de la gramaacutetica se pueden incluir otros bloques de coacutedigo Java intercalados en cualquiera de las partes derechas de las reglas sintaacutecticas Los bloques incluidos en las partes derechas se denominan ldquoacciones sintaacutecticasrdquo (parser actions) ya que son trozos de coacutedigo que pueden considerarse asociados a los puntos de la estructura sintaacutectica donde estaacuten co-locados Una accioacuten sintaacutectica se puede poner en cualquier posicioacuten al principio al final o en el medio de la parte derecha de una regla precedida o seguida de siacutembolos terminales o no terminales el coacutedigo Java de una accioacuten sintaacutectica se traslada literalmente al meacutetodo asociado al siacutembolo no terminal de la parte izquierda de la regla de manera que se ejecuta cada vez que el analizador transcurre por el punto de la estructura sintaacutectica donde se ha incorporado la accioacuten (esto es las acciones sintaacutecticas se incorporan al coacutedigo del analizador sintaacutectico) No obstante su nombre en los bloques de las acciones sintaacutecticas suele ponerse coacutedigo dedicado a la realizacioacuten de tareas semaacutenticas

Si en el bloque asociado a un siacutembolo no terminal se ponen declaraciones los nombres ahiacute definidos son accesibles en todas las acciones sintaacutecticas intercaladas en las partes derechas de las reglas de ese siacutembolo la justificacioacuten es sencilla el coacutedigo del bloque asociado al siacutembolo se incluye al principio de un meacutetodo y el coacutedigo de las acciones pertenece a ese mismo meacutetodo

∆ Ejemplo 1

Se considera de nuevo el ejemplo de presentacioacuten ahora se pretende completar el anaacutelisis leacutexico-sintaacutec-tico con la realizacioacuten de las siguientes tareas

- cada vez que se analiza una expresioacuten un teacutermino o un factor se graba el nombre del siacutembolo no terminal correspondiente y

16

- cuando se analiza un teacutermino en el que hay operadores multiplicativos se cuenta su nuacutemero y se graba el resultado (noacutetese que se cuentan los operadores de cada teacutermino pero no se acumula la cuenta de todos los operadores de la expresioacuten)

Para la realizacioacuten de estas tareas se antildeaden bloques de coacutedigo Java En la especificacioacuten que se describe a continuacioacuten puede apreciarse- para los siacutembolos no terminales expresion termino y factor se ha incluido en los blo-ques correspondientes la operacioacuten de grabacioacuten de su nombre que se ejecutaraacute cada vez que se efec-tuacutee una llamada al meacutetodo- en el bloque asociado a termino se tiene la declaracioacuten del contador de operadores (que seraacute una variable local del meacutetodo)- en la parte derecha de la regla que define la estructura de un teacutermino se han incluido dos acciones sintaacutecticas una para contar la cantidad de operadores (situada detraacutes de la pieza sintaacutectica del opera-dor) y otra para grabar la cantidad de operadores del teacutermino (situada al final de la parte derecha)- se ha definido el meacutetodo grabar para facilitar la escritura del coacutedigo Java

options Ignore_Case = true

PARSER_BEGIN (ExprMin) public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

private static void grabar(String nombre) Systemoutprintln ( -gt + nombre + n)

PARSER_END (ExprMin)

void unaExpresion()

expresion() ltEOFgt

void expresion() grabar(Expresion)

termino() ( + termino() )

void termino() int nAst = 0 grabar(Termino)

17

factor() ( nAst++ factor() ) if (nAst gt 0) Systemoutprintln (n Asteriscos + nAst + n)

void factor() grabar(Factor)

ltconstantegt | ltvariablegt | ( expresion() )

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP | t | n | r

Cuando se incluyen acciones sintaacutecticas la legibilidad del aspecto sintaacutectico de la especificacioacuten queda oscurecida para paliar ese inconveniente puede antildeadirse un comentario que muestre la regla sintaacutectica escueta sin las acciones por ejemplo podriacutea escribirse

void termino() int nAst = 0 grabar(Termino)

factor() ( factor() )

factor()

( nAst++ factor() )

if (nAst gt 0) Systemoutprintln (n Asteriscos + nAst + n)

loz Valor comunicado por un meacutetodoEn todo lo visto hasta ahora los meacutetodos de anaacutelisis sintaacutectico generados por JavaCC no devuelven valor alguno al terminar su tarea de anaacutelisis ya que en la especificacioacuten siempre se ha declarado el nombre de un meacutetodo precedido de void (son meacutetodos de tipo void) Pero en general un meacutetodo de anaacutelisis sintaacutec-tico puede ser de cualquier tipo (primitivo o no) por ello un meacutetodo puede devolver un valor de cualquier tipo (de cualquier clase) al meacutetodo desde el que se efectuoacute la llamada

Esta posibilidad de devolucioacuten de valores tiene una utilidad fundamental en la implementacioacuten de analiza-dores de lenguajes permite la comunicacioacuten de datos (atributos de las entidades analizadas) entre los dife -rentes meacutetodos que colaboran en el anaacutelisis del programa completo

En una especificacioacuten JavaCC la llamada a un meacutetodo ocurre cuando en la parte derecha de una regla estaacute el nombre del siacutembolo no terminal asociado al meacutetodo Cuando un meacutetodo devuelve un valor (tiene un tipo distinto de void) es habitual la recepcioacuten del valor devuelto en una variable en la propia especificacioacuten

18

se puede indicar el nombre de la variable a la que se asignaraacute el valor devuelto tras la llamada para ello se emplea una notacioacuten ideacutentica a la forma que tienen las sentencias de asignacioacuten en el lenguaje Java

dato = nombreSimb()

donde dato es el nombre de la variable a la que se asigna el valor devuelto por el meacutetodo llamado esa va -riable ha de estar oportunamente declarada del mismo tipo que dicho meacutetodo

∆ Ejemplo 2

En el mismo ejemplo de presentacioacuten se desea ademaacutes del anaacutelisis leacutexico-sintaacutectico de la entrada contar la cantidad de operadores que hay en la expresioacuten A continuacioacuten se expone una especificacioacuten JavaCC que es una posible solucioacuten soacutelo se muestran las reglas sintaacutecticas (el resto de la especificacioacuten es igual que en el ejemplo de presentacioacuten)

En la especificacioacuten que se propone puede apreciarse- los siacutembolos no terminales expresion termino y factor se han declarado de tipo int los meacutetodos correspondientes devuelven un valor de tipo entero- el valor devuelto por los meacutetodos sirve para propagar a traveacutes de la estructura sintaacutectica la cuenta de los operadores que hay en el texto analizado- por tratarse de meacutetodos que devuelven un valor es preciso incluir las correspondientes sentencias de devolucioacuten return

void unaExpresion() int nOper nOper = expresion() ltEOFgt Systemoutprintln (Cantidad de operadores = + nOper)

int expresion() int n i n = termino() ( + n++ i = termino() n = n + i )

return n

int termino() int n i n = factor() ( n++ i = factor() n = n + i )

return n

int factor() int n ltconstantegt return 0 | ltvariablegt return 0 | ( n = expresion() ) return n

La especificacioacuten de un factor tambieacuten podriacutea escribirse de esta otra manera

19

int factor() int n = 0

( ltconstantegt | ltvariablegt | ( n = expresion() ) )

return n

A continuacioacuten se expone un nuevo ejemplo para ilustrar la utilidad que proporciona el valor devuelto por un meacutetodo de anaacutelisis sintaacutectico para la propagacioacuten (ascendente) de atributos (propiedades de las entida-des analizadas)

∆ Ejemplo 3

Se pretende ampliar el analizador del ejemplo de presentacioacuten para que se emita un mensaje indicativo cuando la expresioacuten analizada sea una expresioacuten en la que soacutelo aparecen constantes en la solucioacuten que se muestra soacutelo se incluyen las reglas sintaacutecticas (en el resto de la especificacioacuten no hay modificacio-nes)

void unaExpresion() boolean esCte

expresion() ltEOFgt

esCte = expresion() ltEOFgt

if (esCte) Systemoutprintln(nExpresion constanten)

boolean expresion() boolean esCte esTambienCte

termino() ( + termino() )

esCte = termino() ( + esTambienCte = termino() esCte = esCte ampamp esTambienCte )

return esCte

boolean termino() boolean esCte esTambienCte

factor() ( factor() )

esCte = factor() ( esTambienCte = factor() esCte = esCte ampamp esTambienCte )

return esCte

20

boolean factor() boolean esCte

ltconstantegt return true | ltvariablegt return false | ( esCte = expresion() ) return esCte

bull Reconsideracioacuten sobre las producciones vaciacuteasAnteriormente se ha comentado que para representar una produccioacuten vaciacutea en una especificacioacuten JavaCC hay que hacer uso de los metasiacutembolos de opcionalidad para ilustrar esta idea se ha incluido como ejem-plo la especificacioacuten de una lista de paraacutemetros compuesta por cero o maacutes expresiones separadas entre siacute por el caraacutecter coma

void parametros() [ expresion() ( expresion() ) ]

Pero si se aprovecha la posibilidad de incorporar acciones sintaacutecticas se tiene una manera de especificar directamente producciones vaciacuteas para conseguirlo basta considerar una produccioacuten vaciacutea (no tiene nada en su parte derecha) acompantildeada de una accioacuten sintaacutectica cuyo bloque de coacutedigo esteacute vaciacuteo Seguacuten esta posibilidad la especificacioacuten anterior podriacutea tambieacuten escribirse asiacute

void parametros() expresion() ( expresion() ) |

En general la accioacuten sintaacutectica asociada a una produccioacuten vaciacutea no tiene por queacute ser un bloque vaciacuteo de coacutedigo por ejemplo si se quiere que el meacutetodo que analiza los paraacutemetros de una llamada devuelva la cantidad de paraacutemetros encontrados se podriacutea escribir la siguiente especificacioacuten

int parametros()

int cantidad

( expresion() cantidad = 1

( expresion() cantidad++ )

| cantidad = 0 )

return cantidad

y si no quisiera ponerse expliacutecitamente la alternativa de la produccioacuten vaciacutea podriacutea escribirse

int parametros() int cantidad = 0

21

[ expresion() cantidad++ ( expresion() cantidad++ ) ]

return cantidad

Comunicacioacuten entre los analizadores leacutexico y sintaacutecticoCada vez que el analizador sintaacutectico realiza una llamada al analizador lexicograacutefico recibe como resultado de la llamada una representacioacuten de la pieza sintaacutectica encontrada en el texto analizado En los analizadores generados por JavaCC la comunicacioacuten de la pieza se efectuacutea mediante un valor de clase Token Esta cla-se estaacute definida en el fichero Tokenjava que se obtiene en todo caso siempre con el mismo contenido cualquiera que sea la especificacioacuten proporcionada como entrada al generador

Entre los campos de la clase Token se encuentran los siguientes

public int kind

nuacutemero entero que sirve de representacioacuten interna de la pieza sintaacutectica estos nuacutemeros asociados a las piezas son asignados automaacuteticamente por JavaCC los valores considerados pueden consultar-se en el fichero generado de nombre Constantsjava

public String image

cadena que contiene de la secuencia de caracteres (lexema) que constituyen la pieza sintaacutectica co-municada

public int beginLine beginColumn endLine endColumn

posiciones ocupadas (nuacutemeros de liacutenea y columna en el fichero de entrada) por el comienzo y el fi-nal del lexema de la pieza sintaacutectica comunicada

La clase Token tiene otros campos y meacutetodos en el fichero Tokenjava se tienen comentarios des-criptivos sobre todos los campos y meacutetodos que forman parte de la clase todos los campos de la clase se declaran como public

Ya se ha comentado anteriormente que cuando un meacutetodo de anaacutelisis sintaacutectico (asociado a un siacutembolo no terminal) tiene tipo el valor devuelto tras una llamada se puede almacenar en una variable por si se desea utilizar en alguna accioacuten sintaacutectica para ello se escribe

valor = nombreSimbolo()

(siendo valor el nombre de la variable y nombreSimbolo el nombre del siacutembolo no terminal) De manera anaacuteloga el objeto de la clase Token representativo de la pieza sintaacutectica encontrada en la entrada por el analizador lexicograacutefico puede asignarse a una variable del mismo tipo para tener accesibles las ca -racteriacutesticas de la pieza por si se precisaran en alguna accioacuten sintaacutectica para ello en la parte de la especifi -cacioacuten sintaacutectica donde aparezca la pieza sintaacutectica se escribe

dato = lt nombrePieza gt

donde nombrePieza es el nombre de la pieza sintaacutectica y dato es el nombre de una variable de tipo Token en la que se anota el valor indicativo de las propiedades de la pieza sintaacutectica comunicada

Tambieacuten existe otra manera de acceder a las caracteriacutesticas de la pieza sintaacutectica que el analizador lexico -graacutefico comunica al analizador sintaacutectico en la clase del analizador sintaacutectico se tiene declarado el campo

[ static ] public Token token

22

cada vez que el analizador sintaacutectico recibe una pieza sintaacutectica comunicada por el analizador lexicograacutefico se deja anotada en el campo token y ese contenido no se altera hasta que sea proporcionada la siguiente pieza de la entrada

∆ Ejemplo 4

Se considera una ampliacioacuten del ejemplo de presentacioacuten ademaacutes del anaacutelisis de la entrada se pretende obtener una relacioacuten numerada de las variables y las constantes indicando el nuacutemero de liacutenea y el nuacuteme-ro de columna donde estaacuten situadas por ejemplo si en la entrada se tiene la expresioacuten (grabada en dos liacuteneas)

x (y + 11) + 22

en la salida se tendraacute

1- x linea 1 columna 1

2- y linea 1 columna 6

3- 11 linea 1 columna 10

4- 22 linea 2 columna 5

En la especificacioacuten que se propone como solucioacuten puede apreciarse- en la clase del analizador sintaacutectico se han incluido meacutetodos auxiliares en los que se usan los nom-bres image beginLine beginColumn que son campos de un objeto de clase Token

- en la especificacioacuten sintaacutectica de factor se emplea la variable pieza de tipo Token para reco-ger las caracteriacutesticas de las piezas sintaacutecticas detectadas- la variable numero hace de contador para numerar las constantes y variables

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin

static int numero = 0

private static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

private static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

private static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

23

PARSER_END (ExprMin)

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() Token pieza pieza = ltconstantegt numero++ grabarDatosPieza(numero pieza) | pieza = ltvariablegt numero++ grabarDatosPieza(numero pieza) | ( expresion() )

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP | n | t | r

Si se agrupan las dos acciones sintaacutecticas comunes la especificacioacuten de factor tambieacuten puede escri-birse de esta forma

void factor() Token pieza ( pieza = ltconstantegt | pieza = ltvariablegt

) numero++ grabarDatosPieza(numero pieza)

| ( expresion() )

24

Si se accede al campo token de la clase del analizador sintaacutectico se puede resolver este ejemplo pres-cindiendo de la variable pieza

void factor() ( ltconstantegt | ltvariablegt

) numero++ grabarDatosPieza(numero token)

| ( expresion() )

Tareas asociadas a las piezas sintaacutecticasYa se ha visto que es posible asociar bloques de coacutedigo Java a puntos de la estructura sintaacutectica para que se realicen ciertas operaciones cuando el analizador sintaacutectico pase por esa zona de la estructura Tambieacuten es posible asociar coacutedigo Java a las piezas sintaacutecticas para que el analizador lexicograacutefico efectuacutee determina-das tareas cuando detecte en la entrada analizada piezas que tengan asociado coacutedigo

loz Accioacuten ligada a una pieza sintaacutecticaSi se asocia un bloque de coacutedigo a una pieza sintaacutectica se consigue que cada vez que se detecta en la en-trada analizada la presencia de esa pieza se ejecute en ese preciso momento el coacutedigo del bloque asociado a la pieza Estas acciones se denominan ldquoacciones lexicograacuteficasrdquo (lexical actions) el coacutedigo del bloque se traslada literalmente al coacutedigo del analizador lexicograacutefico generado para que se ejecute oportunamente cuando se detecte la pieza ligada al bloque de coacutedigo La accioacuten lexicograacutefica se ejecuta inmediatamente antes que la pieza sea comunicada al analizador sintaacutectico

Se puede asociar una accioacuten lexicograacutefica a una pieza sintaacutectica declarada con TOKEN y tambieacuten a una se-cuencia de caracteres declarada con SKIP ademaacutes de eacutestas hay otras dos formas de declaraciones lexico-graacuteficas (no mencionadas en esta introduccioacuten) que tambieacuten pueden llevar una accioacuten asociada

En una especificacioacuten JavaCC la forma de una pieza sintaacutectica (o bien de una secuencia de caracteres) se define poniendo su descripcioacuten delimitada entre llaves detraacutes de la indicacioacuten TOKEN (o bien SKIP) si se quiere asociar una accioacuten lexicograacutefica a la pieza (o a la secuencia) hay que colocar el bloque de coacutedigo de la accioacuten a continuacioacuten de la descripcioacuten de la forma de la pieza (o de la secuencia) el bloque (delimi-tado por llaves) queda situado entre la descripcioacuten y la llave que cierra la especificacioacuten

La pieza sintaacutectica especial que representa el final del fichero (EOF) tambieacuten puede llevar asociada una ac-cioacuten lexicograacutefica para ello hay que poner

ltgt TOKEN lt EOF gt coacutedigo de la accioacuten asociada

para justificar la presencia de los caracteres ltgt delante de la palabra TOKEN hay que basarse en el con-cepto de ldquoestado lexicograacuteficordquo (cuya explicacioacuten queda fuera del alcance de esta introduccioacuten)

∆ Ejemplo 5

Se considera nuevamente el ejemplo de presentacioacuten como resultado del anaacutelisis de una expresioacuten se pretende obtener una relacioacuten de los pareacutentesis contenidos en la expresioacuten en el mismo orden en que se encuentran ademaacutes por cada salto de liacutenea del texto de entrada se grabaraacute en la salida el caraacutecter y

25

se pasaraacute a una nueva liacutenea tambieacuten se anotaraacute la frase Final del trabajo cuando se alcance el final del fichero de entrada Asiacute por ejemplo si la entrada analizada es

(a + b) (( x )) (y + 7)

los resultados obtenidos seraacuten

() (())()

Final del trabajo

En la especificacioacuten JavaCC que se propone como solucioacuten soacutelo se muestran las reglas sintaacutecticas y le -xicograacuteficas (en el resto no hay cambios) en esta solucioacuten puede apreciarse- las piezas sintaacutecticas correspondientes a los pareacutentesis se han definido de manera nominal para po-der asociarles una accioacuten lexicograacutefica- los caracteres incluidos en la pieza de tipo SKIP se han descompuesto en dos grupos ya que la ac-cioacuten lexicograacutefica incorporada soacutelo afecta a uno de ellos

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ltpAbrirgt expresion() ltpCerrargt

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

TOKEN

26

lt pAbrir ( gt Systemoutprint(()

TOKEN lt pCerrar ) gt Systemoutprint())

ltgtTOKEN lt EOF gt Systemoutprintln ( Final del trabajo )

SKIP n Systemoutprint( n)

SKIP | t | r

loz Bloque de declaraciones lexicograacuteficasCada definicioacuten de una pieza sintaacutectica puede llevar asociada una accioacuten lexicograacutefica que se ejecuta in-mediatamente despueacutes de detectar en la entrada un lexema que se ajusta a la definicioacuten de la pieza cada accioacuten lexicograacutefica es un bloque de coacutedigo Java que puede contener sus propias declaraciones Pero a ve-ces interesa tener declaraciones que sean compartidas por todas las acciones lexicograacuteficas con indepen-dencia de la pieza a la que esteacute asociada cada una de ellas

Para incluir declaraciones que puedan usarse desde el coacutedigo asociado a cualquiera de las piezas sintaacutecti -cas se dispone de una construccioacuten con el siguiente formato

TOKEN_MGR_DECLS

que consta de una palabra reservada seguida del siacutembolo dos puntos seguido de un bloque de coacutedigo Java (delimitado por llaves) esta declaracioacuten se puede colocar en cualquier parte de una especificacioacuten JavaCC si se incluye soacutelo puede ponerse una vez

Todas las declaraciones de variables o de meacutetodos puestas en el bloque de declaraciones lexicograacuteficas compartidas son accesibles desde el coacutedigo de cualquier accioacuten lexicograacutefica la justificacioacuten es sencilla el bloque de declaraciones se traslada al principio del coacutedigo del analizador lexicograacutefico generado y las ac-ciones lexicograacuteficas tambieacuten se trasladan a ese analizador

loz Declaraciones lexicograacuteficas predefinidasDe lo expuesto hasta ahora se aprecia que en una accioacuten lexicograacutefica asociada a una pieza sintaacutectica re-sultan accesibles

- las declaraciones de su propio bloque (si tiene declaraciones)- los componentes del bloque de declaraciones compartidas (si estaacute incluido en la especificacioacuten)

Ademaacutes de esto se tienen otras declaraciones pertenecientes a la clase TokenManager que constituye el analizador lexicograacutefico que tambieacuten son utilizables desde cualquiera de las acciones lexico-graacuteficas estas declaraciones pueden considerarse como ldquopredefinidasrdquo ya que son incorporadas automaacuteti-camente por el generador Entre estas declaraciones accesibles se encuentran las siguientes

[ static ] StringBuffer imagecontiene la cadena de caracteres representativa de la pieza sintaacutectica actual que se acaba de recono-cer en le entrada se trata de un campo de la clase y se declara como static cuando el analizador

27

se ha generado con la opcioacuten (por defecto) Static = true si se generase con la opcioacuten Sta-tic = false no quedariacutea declarada como static

Token matchedToken

variable de clase Token que representa la pieza sintaacutectica actual la que se acaba de encontrar en la entrada se trata de una variable local del meacutetodo al que se ha trasladado el coacutedigo de la accioacuten (y por ello accesible desde ese coacutedigo)

El campo image puede usarse tanto si la accioacuten lexicograacutefica estaacute asociada a una pieza declarada con TOKEN como si lo estaacute a una secuencia declarada con SKIP Sin embargo la variable matchedToken no puede usarse en una accioacuten asociada a un patroacuten especificado como SKIP el motivo es que lo declara-do como SKIP no constituye propiamente una pieza sintaacutectica ya que no se va a comunicar al analizador sintaacutectico

∆ Ejemplo 6

Se propone aquiacute una nueva solucioacuten para el ejemplo nordm 4 expuesto anteriormente

Esta nueva solucioacuten tal y como se expone aquiacute admite algunas mejoras ya que su pretensioacuten soacutelo es ser-vir de ilustracioacuten para algunos aspectos que se acaban de citar (maacutes adelante se propone otra solucioacuten al-ternativa)

En la especificacioacuten de la solucioacuten es posible apreciar esto- las acciones lexicograacuteficas pueden llevar sus propias declaraciones las variables nLineaVar nColumnaVar nLineaCte y nColumnaCte se declaran dentro de las acciones

- la variable matchedToken a la que se hace referencia en el coacutedigo de la accioacuten lexicograacutefica aso-ciada a una variable contiene la informacioacuten sobre la pieza sintaacutectica que se acaba de detectar una va-riable de la expresioacuten analizada anaacutelogamente con la referencia relativa a una constante- el nombre image que aparece en las acciones lexicograacuteficas se refiere al campo de la clase Expr-MinTokenManager (analizador lexicograacutefico) noacutetese que en efecto se comunica como un paraacuteme-tro de clase StringBuffer

- la variable numero se emplea para numerar las constantes y variables encontradas

- los nombres beginLine y beginColumn son campos (puacuteblicos) de la clase Token por ello se pueden aplicar a un objeto de esa clase

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

void unaExpresion()

28

expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n StringBuffer lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

TOKEN lt variable [a-z] gt

int nLineaVar nColumnaVar numero++ nLineaVar = matchedTokenbeginLine nColumnaVar = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaVar nColumnaVar)

TOKEN lt constante ( [0-9] ) + gt

int nLineaCte nColumnaCte

29

numero++ nLineaCte = matchedTokenbeginLine nColumnaCte = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaCte nColumnaCte)

SKIP | n | t | r

La especificacioacuten precedente se puede escribir de otra manera maacutes escueta como se indica a continua-cioacuten (soacutelo se muestran las modificaciones) sobre esta nueva especificacioacuten puede mencionarse- se han eliminado por innecesarias las variables declaradas dentro de las acciones lexicograacuteficas- el nombre image que aparece en el bloque de las declaraciones compartidas se refiere al campo de la clase Token noacutetese que en efecto se aplica a un objeto de esa clase y se comunica como un paraacute -metro de tipo String

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

TOKEN lt variable [a-z] gt

numero++ grabarDatosPieza(numero matchedToken)

TOKEN lt constante ( [0-9] ) + gt

numero++ grabarDatosPieza(numero matchedToken)

loz Accioacuten lexicograacutefica comuacutenSi hubiera que realizar la misma tarea tras la deteccioacuten de todas y cada una de las piezas sintaacutecticas una posibilidad incoacutemoda seriacutea repetir el mismo coacutedigo en todas las acciones lexicograacuteficas para evitar tanta reiteracioacuten se tiene la opcioacuten Common_Token_Action su valor por defecto es false pero si se le asigna el valor true es posible definir una ldquoaccioacuten lexicograacutefica comuacutenrdquo que se escribe una uacutenica vez y se aplica a todas las piezas sintaacutecticas

30

Cuando la opcioacuten Common_Token_Action se establece con el valor true es obligado incluir el blo-que de declaraciones lexicograacuteficas compartidas (TOKEN_MGR_DECLS) Ademaacutes en ese bloque se preci-sa la definicioacuten de un meacutetodo cuyo encabezamiento sea

[ static ] void CommonTokenAction(Token t)

el coacutedigo de este meacutetodo constituye la accioacuten lexicograacutefica comuacuten que se ejecutaraacute despueacutes de la deteccioacuten de cualquier pieza sintaacutectica el meacutetodo tiene un paraacutemetro de tipo Token mediante el que se puede co-municar la informacioacuten correspondiente a la pieza que se acaba de encontrar en la entrada analizada

Si una pieza sintaacutectica tiene asociada una accioacuten lexicograacutefica propia la accioacuten lexicograacutefica comuacuten (si es-taacute definida) se ejecutaraacute inmediatamente despueacutes de realizar la accioacuten propia

La accioacuten lexicograacutefica comuacuten se aplica a todas las piezas sintaacutecticas definidas como TOKEN bien sea no-minalmente (en las reglas lexicograacuteficas) o bien anoacutenimamente (incorporadas dentro de las reglas sintaacutecti-cas) sin embargo no se aplica cuando la secuencia acoplada se corresponde con una expresioacuten regular es-pecificada como SKIP (noacutetese que por el contrario una especificacioacuten SKIP siacute puede tener asociada una accioacuten lexicograacutefica propia)

El coacutedigo del meacutetodo CommonTokenAction se traslada como un componente maacutes del bloque de decla-raciones compartidas (TOKEN_MGR_DECLS) al principio de la clase del analizador lexicograacutefico

∆ Ejemplo 7

Se pretende ahora ampliar el ejemplo de presentacioacuten con la obtencioacuten de una relacioacuten numerada de los lexemas de las piezas sintaacutecticas encontradas en la entrada analizada en el caso de las variables y de las constantes antes del lexema se mostraraacute un indicativo (Var Const) por ejemplo si la expresioacuten de la entrada es

( a + 987 ) b

se grabaraacute la salida

( 1Var a 2+ 3Const 987 4) 5 6Var b 7 8

En la solucioacuten que se propone a continuacioacuten se muestran la definicioacuten de las opciones y las reglas sin-taacutecticas y lexicograacuteficas (en el resto no hay modificaciones) puede resaltarse lo siguiente- las llamadas al meacutetodo CommonTokenAction se incorporan automaacuteticamente no hay que incluir-las en la especificacioacuten como paraacutemetro de llamada a este meacutetodo siempre se emplea el objeto que re -presenta la pieza que se acaba de detectar- la accioacuten lexicograacutefica comuacuten realiza la actualizacioacuten del contador de piezas y la grabacioacuten del lexe-ma de la pieza detectada con su nuacutemero de orden- la accioacuten lexicograacutefica comuacuten tambieacuten se aplica a las piezas sintaacutecticas anoacutenimas (pareacutentesis y ope-radores) y a la pieza especial que representa el final del fichero (EOF)

- las piezas sintaacutecticas ltconstantegt y ltvariablegt tienen asociada una accioacuten lexicograacutefica propia que se ejecuta antes de la accioacuten lexicograacutefica comuacuten- los caracteres que se saltan (SKIP) no estaacuten afectados por la accioacuten lexicograacutefica comuacuten

options Ignore_Case = true Common_Token_Action = true

31

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarIndicativo(String indicativo) Systemoutprint(indicativo + )

static void grabarLexema (int num String lexema) Systemoutprint(lexema + + num + n)

static void CommonTokenAction(Token pieza) numero++ grabarLexema(numero piezaimage)

TOKEN lt variable [a-z] gt

grabarIndicativo(Var)

32

TOKEN lt constante ( [0-9] ) + gt

grabarIndicativo(Const)

SKIP | n | t | r

A modo de ejemplo de aplicacioacuten de la accioacuten lexicograacutefica comuacuten en lo que sigue se ofrece una solucioacuten para la grabacioacuten (en el fichero predefinido de salida) del texto analizado con las liacuteneas numeradas

∆ Ejemplo 8 [ Listado del texto analizado ]Se desea completar el ejemplo de presentacioacuten con la grabacioacuten de un listado con las liacuteneas numeradas del texto analizado (noacutetese que una expresioacuten puede escribirse ocupando varias liacuteneas) No se muestra la especificacioacuten sintaacutectica debido a que no hay ninguna modificacioacuten en ella Los aspectos resentildeables de la solucioacuten que se propone son- se usa la variable numLin como contador de liacuteneas

- en la variable linea se van yuxtaponiendo los lexemas de las piezas sintaacutecticas de una liacutenea esta yuxtaposicioacuten se realiza mediante la accioacuten lexicograacutefica comuacuten- las secuencias reconocidas con los patrones especificados como SKIP no implican la ejecucioacuten de la accioacuten lexicograacutefica comuacuten por ello su lexema (almacenado en la variable image) ha de yuxtaponer-se expliacutecitamente- hay que separar el patroacuten correspondiente al fin de liacutenea para detectar el momento en que ha de gra-barse la liacutenea completa cuyo final se ha alcanzado

options Ignore_Case = true Common_Token_Action = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(nnAnalisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

TOKEN_MGR_DECLS static int numLin = 0 static String linea =

33

static void CommonTokenAction(Token pieza) linea = linea + piezaimage

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP lt | t | r gt linea = linea + image

SKIP n numLin++ Systemoutprintln(numLin + + linea) linea =

A modo de recapitulacioacutenↄ Accioacuten lexicograacutefica Accioacuten sintaacutectica

bull accioacuten lexicograacutefica bloque de coacutedigo Java asociado a una pieza sintaacutectica nominal (TOKEN) o a una secuencia de caracteres (SKIP) se ejecuta cuando se detecta en la entrada la pieza o la secuencia corres-pondiente no se puede aplicar a piezas sintaacutecticas anoacutenimas (autodefinidas con comillas)bull accioacuten sintaacutectica bloque de coacutedigo Java asociado a un punto de la estructura sintaacutectica (el punto indica-do por el sitio de la produccioacuten donde se inserta el bloque) se ejecuta cuando el anaacutelisis de la entrada pa-sa por ese punto de la estructura

ↄ Valor de una pieza sintaacutectica comunicadaEl analizador lexicograacutefico comunica al analizador sintaacutectico una pieza a traveacutes de un valor de la clase Token (fichero Tokenjava) un objeto de la clase Token representa las caracteriacutesticas de la pieza comunicada En la clase Token entre otros se tienen los campos

String imagelexema de la pieza

int beginLine beginColumn endLine endColumnposicioacuten del lexema en el fichero analizado (indicada por los nuacutemeros de fila y de columna de su comienzo y de su terminacioacuten)

ↄ Valor asociado a un siacutembolo

34

En las producciones que especifican la sintaxis se pueden incluir asignaciones que sirven para dejar ano-tado el valor asociado a un siacutembolo

bull siacutembolo no terminal valor = nombreSimbolo()el meacutetodo de anaacutelisis asociado a nombreSimbolo se ha declarado de un cierto tipo (distinto del tipo void) valor ha de ser una variable del mismo tipo que el meacutetodo

bull siacutembolo terminal dato = lt nombrePieza gtel valor asignado es el valor de la clase Token representativo de la pieza sintaacutectica (nominal) dato ha de ser una variable de tipo Token

ↄ Miradas sobre la pieza sintaacutectica comunicadaLa pieza sintaacutectica comunicada puede mirarse desde cada uno de los dos analizadores

bull analizador sintaacutecticola pieza comunicada (desde el analizador lexicograacutefico) se tiene anotada en un campo de la clase del analizador sintaacutectico declarado como

(static) public Token token

este valor es accesible desde el coacutedigo de todos los meacutetodos de anaacutelisis sintaacutectico el contenido del campo variacutea cuando se recibe una nueva pieza sintaacutectica

bull analizador lexicograacuteficola pieza dispuesta para ser comunicada (al analizador sintaacutectico) se tiene anotada en una variable local declarada como

Token matchedToken

este valor es accesible para el coacutedigo de todas las acciones lexicograacuteficas

ↄ El lexema de la pieza comunicadaEl lexema de la pieza sintaacutectica comunicada se encuentra disponible en dos sitios distintos que compar-ten el mismo nombre pero que son de distinto tipo y se usan de distinta manera

bull String image

es un campo de la clase TOKEN puede consultarse en relacioacuten con un objeto de esa clase que esteacute disponible

bull StringBuffer image

es un campo de la clase del analizador lexicograacutefico ( ∙ ∙ ∙ ∙ TokenManagerjava) es accesible desde el coacutedigo de la clase y por lo tanto desde todas las acciones lexicograacuteficas (bloques de coacute-digo que estaacuten incorporados al analizador lexicograacutefico)

se trata de un valor disponible tanto para las piezas sintaacutecticas nominales (TOKEN) como para las secuencias que se saltan (SKIP)

ↄ Resumen relativo a la lexicografiacutea

TOKEN pieza sintaacutectica nominal∙ ∙ ∙ pieza sintaacutectica anoacutenimaSKIP secuencia de caracteres que no forman pieza

35

Precisiones sobre el analizador sintaacutectico generadoPuede decirse que en su funcionamiento maacutes sencillo cuando no se emplean las posibilidades de examen por adelantado (ldquolookaheadrdquo) JavaCC comprueba que las reglas sintaacutecticas cumplen la condicioacuten LL(1) y generan un analizador descendente-predictivo-recursivo Pero esta apreciacioacuten hay que matizarla ya que el analizador sintaacutectico generado no se ajusta en sentido estricto al modelo de analizador LL(1)

En el modelo de anaacutelisis LL(1) el orden del examen de las distintas alternativas de expansioacuten de un siacutembo-lo no terminal es indiferente ya que se conocen los siacutembolos directores de cada una de ellas (que resultan ser conjuntos disjuntos) en la implementacioacuten del subprograma de anaacutelisis asociado al siacutembolo no terminal ltNombreSimbgt cuyas producciones son

ltNombreSimbgt = α1

| α2

∙ ∙ ∙

| αn

se podriacutea preguntar en cualquier orden si la pieza actual pertenece al conjunto de siacutembolos directores de las distintas reglas Sin embargo en el analizador sintaacutectico generado por JavaCC la implementacioacuten del meacute-todo asociado al siacutembolo no terminal ltNombreSimbgt no sigue esa pauta

Si las anteriores reglas sintaacutecticas se transcriben en JavaCC de la forma

void nombreSimb () α1-jcc

| α2-jcc

∙ ∙ ∙

| αn-jcc

1

accioacutenlexicograacutefica

comuacuten

Commom -TokenAction2

accioacutenlexicograacutefica

propia

(accioacuten ligadaa una pieza)3

bloque dedeclaracioneslexicograacuteficas

TOKEN_MGR_DECLS4

declaracioneslexicograacuteficaspredefinidas

(disponibilidad

de uso)TOKENsisisisi∙ ∙ ∙sino--SKIPnosisisi

Las indicaciones si de las columnas 3 y 4 son consecuencia de las indicaciones de la columna 2

La accioacuten lexicograacutefica comuacuten se ejecuta despueacutes de la accioacuten lexicograacutefica propia

36

el meacutetodo de anaacutelisis generado procede de la siguiente manera

se van considerando las alternativas sucesivamente en el mismo orden en el que estaacuten especifica-das primero la de α1 (α1-jcc) despueacutes la de α2 (α2-jcc) etc

para cada alternativa seleccionada se comprueba si la pieza por adelantado coincide con alguno de los siacutembolos iniciales de la parte derecha y si hay coincidencia se prosigue el anaacutelisis con ella si no hay coincidencia se pasa a la alternativa siguiente

siguiendo con este orden si se llega al final de las alternativas sin haberse encontrado coincidencia alguna se produce un error sintaacutectico

En el analizador generado por JavaCC hay una peculiaridad que no se tiene en los analizadores LL(1) es-trictamente considerados la palabra vaciacutea siacute se considera como posible siacutembolo inicial de la parte derecha una regla (esto ocurre siempre que la parte derecha es anulable) De esta peculiaridad se deriva una conse-cuencia que ha de tenerse en cuenta al escribir la especificacioacuten dado que siempre es posible considerar la palabra vaciacutea como la siguiente pieza por adelantado si se llega a examinar la alternativa correspondiente a una produccioacuten anulable el analizador generado siempre seleccionaraacute esa alternativa con independencia de la siguiente pieza que esteacute presente en la entrada que queda por analizar

Asiacute pues si alguna de las producciones que definen un siacutembolo no terminal es anulable es importante el or-den en que se escriben las alternativas en una especificacioacuten sintaacutectica JavaCC el funcionamiento del ana-lizador generado siacute depende de la colocacioacuten elegida para las reglas

Para ilustrar estas peculiaridades del analizador sintaacutectico generado por JavaCC a continuacioacuten se expone un ejemplo

∆ Ejemplo 9

Se quiere obtener un analizador leacutexico-sintaacutectico de textos que se ajustan al siguiente formato una se-cuencia de uno o maacutes nombres seguida del siacutembolo igual seguido de un nuacutemero entero si la secuencia tiene maacutes de un nombre han de estar separados bien por una coma en todos los casos o bien por el siacutem-bolo dos puntos en todos los casos esto es son correctas las secuencias

uno dos tres = 123exclusivo = 0Abajo Arriba = 57

y son incorrectas las secuenciasuno dos tres = 123

exclusivo inclusive = 0

fuera dentro = 56

Una gramaacutetica que define las secuencias con este formato es

ltListagt = nombre ltOtrosNombresgt = numero

ltOtrosNombresgt = nombre nombre | nombre nombre | ε

se trata de una gramaacutetica que cumple la condicioacuten LL(1)- el siacutembolo director para la alternativa vaciacutea es el siacutembolo igual es evidente pues que las tres reglas de ltOtrosNombresgt tienen siacutembolos directores distintos

37

- la decisioacuten de abandonar la repeticioacuten del anaacutelisis de un nuevo nombre (en las dos primeras alterna-tivas de ltOtrosNombresgt) se puede tomar ante la presencia del siacutembolo igual

En la implementacioacuten de un analizador sintaacutectico LL(1) en sentido estricto la seleccioacuten de la alternativa vaciacutea para expandir el siacutembolo ltOtrosNombresgt se hariacutea tras comprobar expliacutecitamente que la pie-za sintaacutectica por adelantado es el siacutembolo igual

El funcionamiento del analizador generado por JavaCC depende del orden de colocacioacuten de las reglas A continuacioacuten se exponen varias posibilidades de especificacioacuten y se comenta el analizador generado en cada caso

bull Especificacioacuten primeraSe transcriben literalmente las reglas tal y como se han escrito antes en la notacioacuten BNF noacutetese que para poder codificar la parte derecha de la regla vaciacutea ha de incluirse una accioacuten sintaacutectica vaciacutea

PARSER_BEGIN (Igualdad)

public class Igualdad

public static void main (String[] argum) throws ParseException Igualdad analisis = new Igualdad(Systemin) analisissecuencia() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (Igualdad)

void secuencia() lista() ltEOFgt

void lista() lt nombre gt otrosNombres() = lt numero gt

void otrosNombres() lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt ) |

TOKEN lt nombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN lt numero ( [ 0 - 9 ] )+ gt

SKIP | r | n

38

El analizador generado funciona correctamente puesto que en el meacutetodo otrosNombres se selec-ciona la alternativa vaciacutea despueacutes de haber comprobado que la pieza por adelantado no es siacutembolo ini-cial de ninguna de las dos alternativas precedentes (no es una coma ni un siacutembolo dos puntos)

bull Especificacioacuten segundaSe considera la misma especificacioacuten anterior pero cambiando el orden de colocacioacuten de las reglas ahora la produccioacuten vaciacutea se coloca como primera alternativa (soacutelo se muestran las partes modifica-das)

void otrosNombres() | lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt )

Con la entrada de esta especificacioacuten el generador JavaCC produce un mensaje en el que se avisa de que las dos alternativas que estaacuten colocadas detraacutes de la alternativa vaciacutea nunca podraacuten seleccionarse aun asiacute se genera un analizador Pero es un analizador que funciona incorrectamente asiacute por ejemplo la entrada

mxa nyz = 57

se considera incorrecta despueacutes de visto el primer nombre se intenta aplicar la produccioacuten vaciacutea (se espera encontrar el siacutembolo igual) pero habriacutea que aplicar la regla cuyo siacutembolo director es la coma

bull Especificacioacuten terceraSe utilizan las posibilidades de la notacioacuten BNF-Ampliada para prescindir de la especificacioacuten expliacuteci-ta de la palabra vaciacutea se aprovecha que JavaCC admite el operador + para indicar repeticiones de una o maacutes veces la alternativa vaciacutea estaacute impliacutecitamente contemplada en la posibilidad de cero repeti-ciones que contempla el operador (soacutelo se muestran las partes modificadas en la especificacioacuten)

void otrosNombres() ( lt nombre gt )+ | ( lt nombre gt )

El meacutetodo otrosNombres() generado hace las comprobaciones en el siguiente orden

- se comprueba si la pieza por adelantado es una coma (siacutembolo inicial de la primera alternativa) y en este caso se selecciona la primera regla- si no es una coma se comprueba si es un siacutembolo dos puntos (siacutembolo inicial de la segunda alter-nativa) y en este caso se selecciona la segunda regla- si no es un siacutembolo dos puntos se considera la palabra vaciacutea como siacutembolo inicial (siempre posi-ble impliacutecitamente) para la segunda alternativa en este caso para que no haya error la pieza por adelantado ha de ser un siacutembolo igual

Se trata pues de un analizador que funciona correctamente

bull Especificacioacuten cuarta

39

Se pone la misma especificacioacuten anterior pero cambiando el orden de consideracioacuten de la regla vaciacutea ahora se incluye impliacutecitamente en la primera alternativa (soacutelo se muestran las partes modificadas)

void otrosNombres() ( lt nombre gt ) | ( lt nombre gt )+

Para esta especificacioacuten el generador JavaCC emite un aviso en el que se indica que la segunda alter -nativa nunca se seleccionaraacute no obstante se genera un analizador que no funciona como se supone que deberiacutea hacerlo por ejemplo el anaacutelisis de la entrada

nyz mxa = 1

produce un error despueacutes de tratado el primer nombre se intenta aplicar la produccioacuten vaciacutea (corres-pondiente a la repeticioacuten de cero veces contemplada en la primera regla) por lo que no se encuentra el esperado siacutembolo igual

40

Page 16: JavaCC

[ IGNORE_CASE ]

inmediatamente detraacutes de la palabra TOKEN con la que empieza la especificacioacuten de la pieza sintaacutectica en la que se pretende la no distincioacuten entre minuacutesculas y mayuacutesculas

Noacutetese que ignore_case tiene dos significados

- es el nombre de un paraacutemetro de la seccioacuten de opciones (puede ponerse en minuacutesculas o mayuacutesculas)- es una palabra reservada que puede aplicarse (escribieacutendola entre corchetes y en mayuacutesculas) a la de -finicioacuten de una pieza sintaacutectica

Por ejemplo sea un lenguaje que en general distingue entre minuacutesculas y mayuacutesculas pero en la codifi -cacioacuten del exponente de una constante real exponencial puede ponerse la letra e en minuacutescula o en ma-yuacutescula En la especificacioacuten JavaCC para ese lenguaje la opcioacuten Ignore_Case ha de tomar el valor false en la definicioacuten de una constante exponencial son posibles las siguientes formas equivalentes

1) sin hacer uso de IGNORE_CASE

TOKEN lt cifras ( [ 0 - 9 ] )+ gt

TOKEN lt signo [+ -] gt

TOKEN lt constante

ltcifrasgt ( ltcifrasgt) [e E] (ltsignogt) ltcifrasgt gt

2) aplicando IGNORE_CASE

TOKEN lt cifras ( [ 0 - 9 ] )+ gt

TOKEN lt signo [+ -] gt

TOKEN [IGNORE_CASE] lt constante

ltcifrasgt ( ltcifrasgt) e (ltsignogt) ltcifrasgt gt

bull Secuencias de caracteres sin efecto en la estructura sintaacutecticaEn los lenguajes de programacioacuten suele haber secuencias de caracteres que no forman parte de la estructu-ra sintaacutectica de un programa esas secuencias son detectadas por el analizador lexicograacutefico pero no son comunicadas al sintaacutectico no han de considerarse como piezas sino que han de saltarse En JavaCC se pueden definir estas secuencias mediante la palabra reservada SKIP la notacioacuten de una especificacioacuten con la palabra SKIP de una secuencia que ha de saltarse es la misma que la notacioacuten usada para las espe-cificaciones con la palabra TOKEN de las piezas que han de comunicarse

Los dos casos maacutes habituales de secuencias que no se comunican al analizador sintaacutectico son

a) los caracteres separadores que no tienen significado en el texto analizadoen los lenguajes de codificacioacuten en formato libre hay caracteres del texto fuente que han de ignorarse en la mayoriacutea de los casos estos caracteres son el espacio en blanco el tabulador el retorno de carro y el fin de liacutenea esta caracteriacutestica se especifica

SKIP |t | r |n

b) los comentarios

15

en el anaacutelisis sintaacutectico de un programa los comentarios han de ignorarse una manera de especificar esta circunstancia es poniendo en una definicioacuten SKIP la expresioacuten regular que denota la forma de los comentarios del lenguaje por ejemplo para los comentarios de una liacutenea de Java se pondriacutea

SKIP

lt (~ [n r]) gt

Tareas asociadas a la estructura sintaacutecticaJavaCC genera un analizador sintaacutectico descendente recursivo constituido por un conjunto de meacutetodos ca-da siacutembolo no terminal de la gramaacutetica sintaacutectica tiene asociado un meacutetodo de anaacutelisis (el meacutetodo tiene el mismo nombre que el siacutembolo correspondiente) Un meacutetodo de anaacutelisis ademaacutes de la realizacioacuten de la parte del anaacutelisis sintaacutectico que le corresponde puede llevar a cabo otras tareas complementarias que sirvan para efectuar un anaacutelisis maacutes complejo del texto de entrada

loz Bloque para un siacutemboloAl describir la forma de las reglas sintaacutecticas de un siacutembolo no terminal en una especificacioacuten JavaCC ya se ha visto que detraacutes de la definicioacuten del nombre del siacutembolo y antes de empezar con las diferentes alter-nativas que tiene asociadas se incluye un bloque de coacutedigo Java (en el ejemplo de presentacioacuten este bloque estaacute vaciacuteo en todos los siacutembolos no terminales de la gramaacutetica) Cada bloque situado en esa posicioacuten puede considerarse ligado al siacutembolo no terminal en cuya descripcioacuten estaacute incluido el coacutedigo Java del bloque se traslada literalmente al principio del meacutetodo generado correspondiente al siacutembolo no terminal y por lo tan-to es lo primero que se considera (se ejecuta) cuando en el transcurso del proceso de anaacutelisis se realiza una llamada al meacutetodo Asiacute pues cada siacutembolo no terminal siempre tiene asociado un uacutenico bloque de coacute-digo Java ndashbloque de presencia obligada aunque pueda estar vaciacuteo- en este bloque se pueden poner de-claraciones y sentencias

loz Acciones sintaacutecticasAdemaacutes del bloque asociado a cada uno de los siacutembolos no terminales de la gramaacutetica se pueden incluir otros bloques de coacutedigo Java intercalados en cualquiera de las partes derechas de las reglas sintaacutecticas Los bloques incluidos en las partes derechas se denominan ldquoacciones sintaacutecticasrdquo (parser actions) ya que son trozos de coacutedigo que pueden considerarse asociados a los puntos de la estructura sintaacutectica donde estaacuten co-locados Una accioacuten sintaacutectica se puede poner en cualquier posicioacuten al principio al final o en el medio de la parte derecha de una regla precedida o seguida de siacutembolos terminales o no terminales el coacutedigo Java de una accioacuten sintaacutectica se traslada literalmente al meacutetodo asociado al siacutembolo no terminal de la parte izquierda de la regla de manera que se ejecuta cada vez que el analizador transcurre por el punto de la estructura sintaacutectica donde se ha incorporado la accioacuten (esto es las acciones sintaacutecticas se incorporan al coacutedigo del analizador sintaacutectico) No obstante su nombre en los bloques de las acciones sintaacutecticas suele ponerse coacutedigo dedicado a la realizacioacuten de tareas semaacutenticas

Si en el bloque asociado a un siacutembolo no terminal se ponen declaraciones los nombres ahiacute definidos son accesibles en todas las acciones sintaacutecticas intercaladas en las partes derechas de las reglas de ese siacutembolo la justificacioacuten es sencilla el coacutedigo del bloque asociado al siacutembolo se incluye al principio de un meacutetodo y el coacutedigo de las acciones pertenece a ese mismo meacutetodo

∆ Ejemplo 1

Se considera de nuevo el ejemplo de presentacioacuten ahora se pretende completar el anaacutelisis leacutexico-sintaacutec-tico con la realizacioacuten de las siguientes tareas

- cada vez que se analiza una expresioacuten un teacutermino o un factor se graba el nombre del siacutembolo no terminal correspondiente y

16

- cuando se analiza un teacutermino en el que hay operadores multiplicativos se cuenta su nuacutemero y se graba el resultado (noacutetese que se cuentan los operadores de cada teacutermino pero no se acumula la cuenta de todos los operadores de la expresioacuten)

Para la realizacioacuten de estas tareas se antildeaden bloques de coacutedigo Java En la especificacioacuten que se describe a continuacioacuten puede apreciarse- para los siacutembolos no terminales expresion termino y factor se ha incluido en los blo-ques correspondientes la operacioacuten de grabacioacuten de su nombre que se ejecutaraacute cada vez que se efec-tuacutee una llamada al meacutetodo- en el bloque asociado a termino se tiene la declaracioacuten del contador de operadores (que seraacute una variable local del meacutetodo)- en la parte derecha de la regla que define la estructura de un teacutermino se han incluido dos acciones sintaacutecticas una para contar la cantidad de operadores (situada detraacutes de la pieza sintaacutectica del opera-dor) y otra para grabar la cantidad de operadores del teacutermino (situada al final de la parte derecha)- se ha definido el meacutetodo grabar para facilitar la escritura del coacutedigo Java

options Ignore_Case = true

PARSER_BEGIN (ExprMin) public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

private static void grabar(String nombre) Systemoutprintln ( -gt + nombre + n)

PARSER_END (ExprMin)

void unaExpresion()

expresion() ltEOFgt

void expresion() grabar(Expresion)

termino() ( + termino() )

void termino() int nAst = 0 grabar(Termino)

17

factor() ( nAst++ factor() ) if (nAst gt 0) Systemoutprintln (n Asteriscos + nAst + n)

void factor() grabar(Factor)

ltconstantegt | ltvariablegt | ( expresion() )

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP | t | n | r

Cuando se incluyen acciones sintaacutecticas la legibilidad del aspecto sintaacutectico de la especificacioacuten queda oscurecida para paliar ese inconveniente puede antildeadirse un comentario que muestre la regla sintaacutectica escueta sin las acciones por ejemplo podriacutea escribirse

void termino() int nAst = 0 grabar(Termino)

factor() ( factor() )

factor()

( nAst++ factor() )

if (nAst gt 0) Systemoutprintln (n Asteriscos + nAst + n)

loz Valor comunicado por un meacutetodoEn todo lo visto hasta ahora los meacutetodos de anaacutelisis sintaacutectico generados por JavaCC no devuelven valor alguno al terminar su tarea de anaacutelisis ya que en la especificacioacuten siempre se ha declarado el nombre de un meacutetodo precedido de void (son meacutetodos de tipo void) Pero en general un meacutetodo de anaacutelisis sintaacutec-tico puede ser de cualquier tipo (primitivo o no) por ello un meacutetodo puede devolver un valor de cualquier tipo (de cualquier clase) al meacutetodo desde el que se efectuoacute la llamada

Esta posibilidad de devolucioacuten de valores tiene una utilidad fundamental en la implementacioacuten de analiza-dores de lenguajes permite la comunicacioacuten de datos (atributos de las entidades analizadas) entre los dife -rentes meacutetodos que colaboran en el anaacutelisis del programa completo

En una especificacioacuten JavaCC la llamada a un meacutetodo ocurre cuando en la parte derecha de una regla estaacute el nombre del siacutembolo no terminal asociado al meacutetodo Cuando un meacutetodo devuelve un valor (tiene un tipo distinto de void) es habitual la recepcioacuten del valor devuelto en una variable en la propia especificacioacuten

18

se puede indicar el nombre de la variable a la que se asignaraacute el valor devuelto tras la llamada para ello se emplea una notacioacuten ideacutentica a la forma que tienen las sentencias de asignacioacuten en el lenguaje Java

dato = nombreSimb()

donde dato es el nombre de la variable a la que se asigna el valor devuelto por el meacutetodo llamado esa va -riable ha de estar oportunamente declarada del mismo tipo que dicho meacutetodo

∆ Ejemplo 2

En el mismo ejemplo de presentacioacuten se desea ademaacutes del anaacutelisis leacutexico-sintaacutectico de la entrada contar la cantidad de operadores que hay en la expresioacuten A continuacioacuten se expone una especificacioacuten JavaCC que es una posible solucioacuten soacutelo se muestran las reglas sintaacutecticas (el resto de la especificacioacuten es igual que en el ejemplo de presentacioacuten)

En la especificacioacuten que se propone puede apreciarse- los siacutembolos no terminales expresion termino y factor se han declarado de tipo int los meacutetodos correspondientes devuelven un valor de tipo entero- el valor devuelto por los meacutetodos sirve para propagar a traveacutes de la estructura sintaacutectica la cuenta de los operadores que hay en el texto analizado- por tratarse de meacutetodos que devuelven un valor es preciso incluir las correspondientes sentencias de devolucioacuten return

void unaExpresion() int nOper nOper = expresion() ltEOFgt Systemoutprintln (Cantidad de operadores = + nOper)

int expresion() int n i n = termino() ( + n++ i = termino() n = n + i )

return n

int termino() int n i n = factor() ( n++ i = factor() n = n + i )

return n

int factor() int n ltconstantegt return 0 | ltvariablegt return 0 | ( n = expresion() ) return n

La especificacioacuten de un factor tambieacuten podriacutea escribirse de esta otra manera

19

int factor() int n = 0

( ltconstantegt | ltvariablegt | ( n = expresion() ) )

return n

A continuacioacuten se expone un nuevo ejemplo para ilustrar la utilidad que proporciona el valor devuelto por un meacutetodo de anaacutelisis sintaacutectico para la propagacioacuten (ascendente) de atributos (propiedades de las entida-des analizadas)

∆ Ejemplo 3

Se pretende ampliar el analizador del ejemplo de presentacioacuten para que se emita un mensaje indicativo cuando la expresioacuten analizada sea una expresioacuten en la que soacutelo aparecen constantes en la solucioacuten que se muestra soacutelo se incluyen las reglas sintaacutecticas (en el resto de la especificacioacuten no hay modificacio-nes)

void unaExpresion() boolean esCte

expresion() ltEOFgt

esCte = expresion() ltEOFgt

if (esCte) Systemoutprintln(nExpresion constanten)

boolean expresion() boolean esCte esTambienCte

termino() ( + termino() )

esCte = termino() ( + esTambienCte = termino() esCte = esCte ampamp esTambienCte )

return esCte

boolean termino() boolean esCte esTambienCte

factor() ( factor() )

esCte = factor() ( esTambienCte = factor() esCte = esCte ampamp esTambienCte )

return esCte

20

boolean factor() boolean esCte

ltconstantegt return true | ltvariablegt return false | ( esCte = expresion() ) return esCte

bull Reconsideracioacuten sobre las producciones vaciacuteasAnteriormente se ha comentado que para representar una produccioacuten vaciacutea en una especificacioacuten JavaCC hay que hacer uso de los metasiacutembolos de opcionalidad para ilustrar esta idea se ha incluido como ejem-plo la especificacioacuten de una lista de paraacutemetros compuesta por cero o maacutes expresiones separadas entre siacute por el caraacutecter coma

void parametros() [ expresion() ( expresion() ) ]

Pero si se aprovecha la posibilidad de incorporar acciones sintaacutecticas se tiene una manera de especificar directamente producciones vaciacuteas para conseguirlo basta considerar una produccioacuten vaciacutea (no tiene nada en su parte derecha) acompantildeada de una accioacuten sintaacutectica cuyo bloque de coacutedigo esteacute vaciacuteo Seguacuten esta posibilidad la especificacioacuten anterior podriacutea tambieacuten escribirse asiacute

void parametros() expresion() ( expresion() ) |

En general la accioacuten sintaacutectica asociada a una produccioacuten vaciacutea no tiene por queacute ser un bloque vaciacuteo de coacutedigo por ejemplo si se quiere que el meacutetodo que analiza los paraacutemetros de una llamada devuelva la cantidad de paraacutemetros encontrados se podriacutea escribir la siguiente especificacioacuten

int parametros()

int cantidad

( expresion() cantidad = 1

( expresion() cantidad++ )

| cantidad = 0 )

return cantidad

y si no quisiera ponerse expliacutecitamente la alternativa de la produccioacuten vaciacutea podriacutea escribirse

int parametros() int cantidad = 0

21

[ expresion() cantidad++ ( expresion() cantidad++ ) ]

return cantidad

Comunicacioacuten entre los analizadores leacutexico y sintaacutecticoCada vez que el analizador sintaacutectico realiza una llamada al analizador lexicograacutefico recibe como resultado de la llamada una representacioacuten de la pieza sintaacutectica encontrada en el texto analizado En los analizadores generados por JavaCC la comunicacioacuten de la pieza se efectuacutea mediante un valor de clase Token Esta cla-se estaacute definida en el fichero Tokenjava que se obtiene en todo caso siempre con el mismo contenido cualquiera que sea la especificacioacuten proporcionada como entrada al generador

Entre los campos de la clase Token se encuentran los siguientes

public int kind

nuacutemero entero que sirve de representacioacuten interna de la pieza sintaacutectica estos nuacutemeros asociados a las piezas son asignados automaacuteticamente por JavaCC los valores considerados pueden consultar-se en el fichero generado de nombre Constantsjava

public String image

cadena que contiene de la secuencia de caracteres (lexema) que constituyen la pieza sintaacutectica co-municada

public int beginLine beginColumn endLine endColumn

posiciones ocupadas (nuacutemeros de liacutenea y columna en el fichero de entrada) por el comienzo y el fi-nal del lexema de la pieza sintaacutectica comunicada

La clase Token tiene otros campos y meacutetodos en el fichero Tokenjava se tienen comentarios des-criptivos sobre todos los campos y meacutetodos que forman parte de la clase todos los campos de la clase se declaran como public

Ya se ha comentado anteriormente que cuando un meacutetodo de anaacutelisis sintaacutectico (asociado a un siacutembolo no terminal) tiene tipo el valor devuelto tras una llamada se puede almacenar en una variable por si se desea utilizar en alguna accioacuten sintaacutectica para ello se escribe

valor = nombreSimbolo()

(siendo valor el nombre de la variable y nombreSimbolo el nombre del siacutembolo no terminal) De manera anaacuteloga el objeto de la clase Token representativo de la pieza sintaacutectica encontrada en la entrada por el analizador lexicograacutefico puede asignarse a una variable del mismo tipo para tener accesibles las ca -racteriacutesticas de la pieza por si se precisaran en alguna accioacuten sintaacutectica para ello en la parte de la especifi -cacioacuten sintaacutectica donde aparezca la pieza sintaacutectica se escribe

dato = lt nombrePieza gt

donde nombrePieza es el nombre de la pieza sintaacutectica y dato es el nombre de una variable de tipo Token en la que se anota el valor indicativo de las propiedades de la pieza sintaacutectica comunicada

Tambieacuten existe otra manera de acceder a las caracteriacutesticas de la pieza sintaacutectica que el analizador lexico -graacutefico comunica al analizador sintaacutectico en la clase del analizador sintaacutectico se tiene declarado el campo

[ static ] public Token token

22

cada vez que el analizador sintaacutectico recibe una pieza sintaacutectica comunicada por el analizador lexicograacutefico se deja anotada en el campo token y ese contenido no se altera hasta que sea proporcionada la siguiente pieza de la entrada

∆ Ejemplo 4

Se considera una ampliacioacuten del ejemplo de presentacioacuten ademaacutes del anaacutelisis de la entrada se pretende obtener una relacioacuten numerada de las variables y las constantes indicando el nuacutemero de liacutenea y el nuacuteme-ro de columna donde estaacuten situadas por ejemplo si en la entrada se tiene la expresioacuten (grabada en dos liacuteneas)

x (y + 11) + 22

en la salida se tendraacute

1- x linea 1 columna 1

2- y linea 1 columna 6

3- 11 linea 1 columna 10

4- 22 linea 2 columna 5

En la especificacioacuten que se propone como solucioacuten puede apreciarse- en la clase del analizador sintaacutectico se han incluido meacutetodos auxiliares en los que se usan los nom-bres image beginLine beginColumn que son campos de un objeto de clase Token

- en la especificacioacuten sintaacutectica de factor se emplea la variable pieza de tipo Token para reco-ger las caracteriacutesticas de las piezas sintaacutecticas detectadas- la variable numero hace de contador para numerar las constantes y variables

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin

static int numero = 0

private static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

private static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

private static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

23

PARSER_END (ExprMin)

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() Token pieza pieza = ltconstantegt numero++ grabarDatosPieza(numero pieza) | pieza = ltvariablegt numero++ grabarDatosPieza(numero pieza) | ( expresion() )

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP | n | t | r

Si se agrupan las dos acciones sintaacutecticas comunes la especificacioacuten de factor tambieacuten puede escri-birse de esta forma

void factor() Token pieza ( pieza = ltconstantegt | pieza = ltvariablegt

) numero++ grabarDatosPieza(numero pieza)

| ( expresion() )

24

Si se accede al campo token de la clase del analizador sintaacutectico se puede resolver este ejemplo pres-cindiendo de la variable pieza

void factor() ( ltconstantegt | ltvariablegt

) numero++ grabarDatosPieza(numero token)

| ( expresion() )

Tareas asociadas a las piezas sintaacutecticasYa se ha visto que es posible asociar bloques de coacutedigo Java a puntos de la estructura sintaacutectica para que se realicen ciertas operaciones cuando el analizador sintaacutectico pase por esa zona de la estructura Tambieacuten es posible asociar coacutedigo Java a las piezas sintaacutecticas para que el analizador lexicograacutefico efectuacutee determina-das tareas cuando detecte en la entrada analizada piezas que tengan asociado coacutedigo

loz Accioacuten ligada a una pieza sintaacutecticaSi se asocia un bloque de coacutedigo a una pieza sintaacutectica se consigue que cada vez que se detecta en la en-trada analizada la presencia de esa pieza se ejecute en ese preciso momento el coacutedigo del bloque asociado a la pieza Estas acciones se denominan ldquoacciones lexicograacuteficasrdquo (lexical actions) el coacutedigo del bloque se traslada literalmente al coacutedigo del analizador lexicograacutefico generado para que se ejecute oportunamente cuando se detecte la pieza ligada al bloque de coacutedigo La accioacuten lexicograacutefica se ejecuta inmediatamente antes que la pieza sea comunicada al analizador sintaacutectico

Se puede asociar una accioacuten lexicograacutefica a una pieza sintaacutectica declarada con TOKEN y tambieacuten a una se-cuencia de caracteres declarada con SKIP ademaacutes de eacutestas hay otras dos formas de declaraciones lexico-graacuteficas (no mencionadas en esta introduccioacuten) que tambieacuten pueden llevar una accioacuten asociada

En una especificacioacuten JavaCC la forma de una pieza sintaacutectica (o bien de una secuencia de caracteres) se define poniendo su descripcioacuten delimitada entre llaves detraacutes de la indicacioacuten TOKEN (o bien SKIP) si se quiere asociar una accioacuten lexicograacutefica a la pieza (o a la secuencia) hay que colocar el bloque de coacutedigo de la accioacuten a continuacioacuten de la descripcioacuten de la forma de la pieza (o de la secuencia) el bloque (delimi-tado por llaves) queda situado entre la descripcioacuten y la llave que cierra la especificacioacuten

La pieza sintaacutectica especial que representa el final del fichero (EOF) tambieacuten puede llevar asociada una ac-cioacuten lexicograacutefica para ello hay que poner

ltgt TOKEN lt EOF gt coacutedigo de la accioacuten asociada

para justificar la presencia de los caracteres ltgt delante de la palabra TOKEN hay que basarse en el con-cepto de ldquoestado lexicograacuteficordquo (cuya explicacioacuten queda fuera del alcance de esta introduccioacuten)

∆ Ejemplo 5

Se considera nuevamente el ejemplo de presentacioacuten como resultado del anaacutelisis de una expresioacuten se pretende obtener una relacioacuten de los pareacutentesis contenidos en la expresioacuten en el mismo orden en que se encuentran ademaacutes por cada salto de liacutenea del texto de entrada se grabaraacute en la salida el caraacutecter y

25

se pasaraacute a una nueva liacutenea tambieacuten se anotaraacute la frase Final del trabajo cuando se alcance el final del fichero de entrada Asiacute por ejemplo si la entrada analizada es

(a + b) (( x )) (y + 7)

los resultados obtenidos seraacuten

() (())()

Final del trabajo

En la especificacioacuten JavaCC que se propone como solucioacuten soacutelo se muestran las reglas sintaacutecticas y le -xicograacuteficas (en el resto no hay cambios) en esta solucioacuten puede apreciarse- las piezas sintaacutecticas correspondientes a los pareacutentesis se han definido de manera nominal para po-der asociarles una accioacuten lexicograacutefica- los caracteres incluidos en la pieza de tipo SKIP se han descompuesto en dos grupos ya que la ac-cioacuten lexicograacutefica incorporada soacutelo afecta a uno de ellos

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ltpAbrirgt expresion() ltpCerrargt

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

TOKEN

26

lt pAbrir ( gt Systemoutprint(()

TOKEN lt pCerrar ) gt Systemoutprint())

ltgtTOKEN lt EOF gt Systemoutprintln ( Final del trabajo )

SKIP n Systemoutprint( n)

SKIP | t | r

loz Bloque de declaraciones lexicograacuteficasCada definicioacuten de una pieza sintaacutectica puede llevar asociada una accioacuten lexicograacutefica que se ejecuta in-mediatamente despueacutes de detectar en la entrada un lexema que se ajusta a la definicioacuten de la pieza cada accioacuten lexicograacutefica es un bloque de coacutedigo Java que puede contener sus propias declaraciones Pero a ve-ces interesa tener declaraciones que sean compartidas por todas las acciones lexicograacuteficas con indepen-dencia de la pieza a la que esteacute asociada cada una de ellas

Para incluir declaraciones que puedan usarse desde el coacutedigo asociado a cualquiera de las piezas sintaacutecti -cas se dispone de una construccioacuten con el siguiente formato

TOKEN_MGR_DECLS

que consta de una palabra reservada seguida del siacutembolo dos puntos seguido de un bloque de coacutedigo Java (delimitado por llaves) esta declaracioacuten se puede colocar en cualquier parte de una especificacioacuten JavaCC si se incluye soacutelo puede ponerse una vez

Todas las declaraciones de variables o de meacutetodos puestas en el bloque de declaraciones lexicograacuteficas compartidas son accesibles desde el coacutedigo de cualquier accioacuten lexicograacutefica la justificacioacuten es sencilla el bloque de declaraciones se traslada al principio del coacutedigo del analizador lexicograacutefico generado y las ac-ciones lexicograacuteficas tambieacuten se trasladan a ese analizador

loz Declaraciones lexicograacuteficas predefinidasDe lo expuesto hasta ahora se aprecia que en una accioacuten lexicograacutefica asociada a una pieza sintaacutectica re-sultan accesibles

- las declaraciones de su propio bloque (si tiene declaraciones)- los componentes del bloque de declaraciones compartidas (si estaacute incluido en la especificacioacuten)

Ademaacutes de esto se tienen otras declaraciones pertenecientes a la clase TokenManager que constituye el analizador lexicograacutefico que tambieacuten son utilizables desde cualquiera de las acciones lexico-graacuteficas estas declaraciones pueden considerarse como ldquopredefinidasrdquo ya que son incorporadas automaacuteti-camente por el generador Entre estas declaraciones accesibles se encuentran las siguientes

[ static ] StringBuffer imagecontiene la cadena de caracteres representativa de la pieza sintaacutectica actual que se acaba de recono-cer en le entrada se trata de un campo de la clase y se declara como static cuando el analizador

27

se ha generado con la opcioacuten (por defecto) Static = true si se generase con la opcioacuten Sta-tic = false no quedariacutea declarada como static

Token matchedToken

variable de clase Token que representa la pieza sintaacutectica actual la que se acaba de encontrar en la entrada se trata de una variable local del meacutetodo al que se ha trasladado el coacutedigo de la accioacuten (y por ello accesible desde ese coacutedigo)

El campo image puede usarse tanto si la accioacuten lexicograacutefica estaacute asociada a una pieza declarada con TOKEN como si lo estaacute a una secuencia declarada con SKIP Sin embargo la variable matchedToken no puede usarse en una accioacuten asociada a un patroacuten especificado como SKIP el motivo es que lo declara-do como SKIP no constituye propiamente una pieza sintaacutectica ya que no se va a comunicar al analizador sintaacutectico

∆ Ejemplo 6

Se propone aquiacute una nueva solucioacuten para el ejemplo nordm 4 expuesto anteriormente

Esta nueva solucioacuten tal y como se expone aquiacute admite algunas mejoras ya que su pretensioacuten soacutelo es ser-vir de ilustracioacuten para algunos aspectos que se acaban de citar (maacutes adelante se propone otra solucioacuten al-ternativa)

En la especificacioacuten de la solucioacuten es posible apreciar esto- las acciones lexicograacuteficas pueden llevar sus propias declaraciones las variables nLineaVar nColumnaVar nLineaCte y nColumnaCte se declaran dentro de las acciones

- la variable matchedToken a la que se hace referencia en el coacutedigo de la accioacuten lexicograacutefica aso-ciada a una variable contiene la informacioacuten sobre la pieza sintaacutectica que se acaba de detectar una va-riable de la expresioacuten analizada anaacutelogamente con la referencia relativa a una constante- el nombre image que aparece en las acciones lexicograacuteficas se refiere al campo de la clase Expr-MinTokenManager (analizador lexicograacutefico) noacutetese que en efecto se comunica como un paraacuteme-tro de clase StringBuffer

- la variable numero se emplea para numerar las constantes y variables encontradas

- los nombres beginLine y beginColumn son campos (puacuteblicos) de la clase Token por ello se pueden aplicar a un objeto de esa clase

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

void unaExpresion()

28

expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n StringBuffer lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

TOKEN lt variable [a-z] gt

int nLineaVar nColumnaVar numero++ nLineaVar = matchedTokenbeginLine nColumnaVar = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaVar nColumnaVar)

TOKEN lt constante ( [0-9] ) + gt

int nLineaCte nColumnaCte

29

numero++ nLineaCte = matchedTokenbeginLine nColumnaCte = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaCte nColumnaCte)

SKIP | n | t | r

La especificacioacuten precedente se puede escribir de otra manera maacutes escueta como se indica a continua-cioacuten (soacutelo se muestran las modificaciones) sobre esta nueva especificacioacuten puede mencionarse- se han eliminado por innecesarias las variables declaradas dentro de las acciones lexicograacuteficas- el nombre image que aparece en el bloque de las declaraciones compartidas se refiere al campo de la clase Token noacutetese que en efecto se aplica a un objeto de esa clase y se comunica como un paraacute -metro de tipo String

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

TOKEN lt variable [a-z] gt

numero++ grabarDatosPieza(numero matchedToken)

TOKEN lt constante ( [0-9] ) + gt

numero++ grabarDatosPieza(numero matchedToken)

loz Accioacuten lexicograacutefica comuacutenSi hubiera que realizar la misma tarea tras la deteccioacuten de todas y cada una de las piezas sintaacutecticas una posibilidad incoacutemoda seriacutea repetir el mismo coacutedigo en todas las acciones lexicograacuteficas para evitar tanta reiteracioacuten se tiene la opcioacuten Common_Token_Action su valor por defecto es false pero si se le asigna el valor true es posible definir una ldquoaccioacuten lexicograacutefica comuacutenrdquo que se escribe una uacutenica vez y se aplica a todas las piezas sintaacutecticas

30

Cuando la opcioacuten Common_Token_Action se establece con el valor true es obligado incluir el blo-que de declaraciones lexicograacuteficas compartidas (TOKEN_MGR_DECLS) Ademaacutes en ese bloque se preci-sa la definicioacuten de un meacutetodo cuyo encabezamiento sea

[ static ] void CommonTokenAction(Token t)

el coacutedigo de este meacutetodo constituye la accioacuten lexicograacutefica comuacuten que se ejecutaraacute despueacutes de la deteccioacuten de cualquier pieza sintaacutectica el meacutetodo tiene un paraacutemetro de tipo Token mediante el que se puede co-municar la informacioacuten correspondiente a la pieza que se acaba de encontrar en la entrada analizada

Si una pieza sintaacutectica tiene asociada una accioacuten lexicograacutefica propia la accioacuten lexicograacutefica comuacuten (si es-taacute definida) se ejecutaraacute inmediatamente despueacutes de realizar la accioacuten propia

La accioacuten lexicograacutefica comuacuten se aplica a todas las piezas sintaacutecticas definidas como TOKEN bien sea no-minalmente (en las reglas lexicograacuteficas) o bien anoacutenimamente (incorporadas dentro de las reglas sintaacutecti-cas) sin embargo no se aplica cuando la secuencia acoplada se corresponde con una expresioacuten regular es-pecificada como SKIP (noacutetese que por el contrario una especificacioacuten SKIP siacute puede tener asociada una accioacuten lexicograacutefica propia)

El coacutedigo del meacutetodo CommonTokenAction se traslada como un componente maacutes del bloque de decla-raciones compartidas (TOKEN_MGR_DECLS) al principio de la clase del analizador lexicograacutefico

∆ Ejemplo 7

Se pretende ahora ampliar el ejemplo de presentacioacuten con la obtencioacuten de una relacioacuten numerada de los lexemas de las piezas sintaacutecticas encontradas en la entrada analizada en el caso de las variables y de las constantes antes del lexema se mostraraacute un indicativo (Var Const) por ejemplo si la expresioacuten de la entrada es

( a + 987 ) b

se grabaraacute la salida

( 1Var a 2+ 3Const 987 4) 5 6Var b 7 8

En la solucioacuten que se propone a continuacioacuten se muestran la definicioacuten de las opciones y las reglas sin-taacutecticas y lexicograacuteficas (en el resto no hay modificaciones) puede resaltarse lo siguiente- las llamadas al meacutetodo CommonTokenAction se incorporan automaacuteticamente no hay que incluir-las en la especificacioacuten como paraacutemetro de llamada a este meacutetodo siempre se emplea el objeto que re -presenta la pieza que se acaba de detectar- la accioacuten lexicograacutefica comuacuten realiza la actualizacioacuten del contador de piezas y la grabacioacuten del lexe-ma de la pieza detectada con su nuacutemero de orden- la accioacuten lexicograacutefica comuacuten tambieacuten se aplica a las piezas sintaacutecticas anoacutenimas (pareacutentesis y ope-radores) y a la pieza especial que representa el final del fichero (EOF)

- las piezas sintaacutecticas ltconstantegt y ltvariablegt tienen asociada una accioacuten lexicograacutefica propia que se ejecuta antes de la accioacuten lexicograacutefica comuacuten- los caracteres que se saltan (SKIP) no estaacuten afectados por la accioacuten lexicograacutefica comuacuten

options Ignore_Case = true Common_Token_Action = true

31

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarIndicativo(String indicativo) Systemoutprint(indicativo + )

static void grabarLexema (int num String lexema) Systemoutprint(lexema + + num + n)

static void CommonTokenAction(Token pieza) numero++ grabarLexema(numero piezaimage)

TOKEN lt variable [a-z] gt

grabarIndicativo(Var)

32

TOKEN lt constante ( [0-9] ) + gt

grabarIndicativo(Const)

SKIP | n | t | r

A modo de ejemplo de aplicacioacuten de la accioacuten lexicograacutefica comuacuten en lo que sigue se ofrece una solucioacuten para la grabacioacuten (en el fichero predefinido de salida) del texto analizado con las liacuteneas numeradas

∆ Ejemplo 8 [ Listado del texto analizado ]Se desea completar el ejemplo de presentacioacuten con la grabacioacuten de un listado con las liacuteneas numeradas del texto analizado (noacutetese que una expresioacuten puede escribirse ocupando varias liacuteneas) No se muestra la especificacioacuten sintaacutectica debido a que no hay ninguna modificacioacuten en ella Los aspectos resentildeables de la solucioacuten que se propone son- se usa la variable numLin como contador de liacuteneas

- en la variable linea se van yuxtaponiendo los lexemas de las piezas sintaacutecticas de una liacutenea esta yuxtaposicioacuten se realiza mediante la accioacuten lexicograacutefica comuacuten- las secuencias reconocidas con los patrones especificados como SKIP no implican la ejecucioacuten de la accioacuten lexicograacutefica comuacuten por ello su lexema (almacenado en la variable image) ha de yuxtaponer-se expliacutecitamente- hay que separar el patroacuten correspondiente al fin de liacutenea para detectar el momento en que ha de gra-barse la liacutenea completa cuyo final se ha alcanzado

options Ignore_Case = true Common_Token_Action = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(nnAnalisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

TOKEN_MGR_DECLS static int numLin = 0 static String linea =

33

static void CommonTokenAction(Token pieza) linea = linea + piezaimage

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP lt | t | r gt linea = linea + image

SKIP n numLin++ Systemoutprintln(numLin + + linea) linea =

A modo de recapitulacioacutenↄ Accioacuten lexicograacutefica Accioacuten sintaacutectica

bull accioacuten lexicograacutefica bloque de coacutedigo Java asociado a una pieza sintaacutectica nominal (TOKEN) o a una secuencia de caracteres (SKIP) se ejecuta cuando se detecta en la entrada la pieza o la secuencia corres-pondiente no se puede aplicar a piezas sintaacutecticas anoacutenimas (autodefinidas con comillas)bull accioacuten sintaacutectica bloque de coacutedigo Java asociado a un punto de la estructura sintaacutectica (el punto indica-do por el sitio de la produccioacuten donde se inserta el bloque) se ejecuta cuando el anaacutelisis de la entrada pa-sa por ese punto de la estructura

ↄ Valor de una pieza sintaacutectica comunicadaEl analizador lexicograacutefico comunica al analizador sintaacutectico una pieza a traveacutes de un valor de la clase Token (fichero Tokenjava) un objeto de la clase Token representa las caracteriacutesticas de la pieza comunicada En la clase Token entre otros se tienen los campos

String imagelexema de la pieza

int beginLine beginColumn endLine endColumnposicioacuten del lexema en el fichero analizado (indicada por los nuacutemeros de fila y de columna de su comienzo y de su terminacioacuten)

ↄ Valor asociado a un siacutembolo

34

En las producciones que especifican la sintaxis se pueden incluir asignaciones que sirven para dejar ano-tado el valor asociado a un siacutembolo

bull siacutembolo no terminal valor = nombreSimbolo()el meacutetodo de anaacutelisis asociado a nombreSimbolo se ha declarado de un cierto tipo (distinto del tipo void) valor ha de ser una variable del mismo tipo que el meacutetodo

bull siacutembolo terminal dato = lt nombrePieza gtel valor asignado es el valor de la clase Token representativo de la pieza sintaacutectica (nominal) dato ha de ser una variable de tipo Token

ↄ Miradas sobre la pieza sintaacutectica comunicadaLa pieza sintaacutectica comunicada puede mirarse desde cada uno de los dos analizadores

bull analizador sintaacutecticola pieza comunicada (desde el analizador lexicograacutefico) se tiene anotada en un campo de la clase del analizador sintaacutectico declarado como

(static) public Token token

este valor es accesible desde el coacutedigo de todos los meacutetodos de anaacutelisis sintaacutectico el contenido del campo variacutea cuando se recibe una nueva pieza sintaacutectica

bull analizador lexicograacuteficola pieza dispuesta para ser comunicada (al analizador sintaacutectico) se tiene anotada en una variable local declarada como

Token matchedToken

este valor es accesible para el coacutedigo de todas las acciones lexicograacuteficas

ↄ El lexema de la pieza comunicadaEl lexema de la pieza sintaacutectica comunicada se encuentra disponible en dos sitios distintos que compar-ten el mismo nombre pero que son de distinto tipo y se usan de distinta manera

bull String image

es un campo de la clase TOKEN puede consultarse en relacioacuten con un objeto de esa clase que esteacute disponible

bull StringBuffer image

es un campo de la clase del analizador lexicograacutefico ( ∙ ∙ ∙ ∙ TokenManagerjava) es accesible desde el coacutedigo de la clase y por lo tanto desde todas las acciones lexicograacuteficas (bloques de coacute-digo que estaacuten incorporados al analizador lexicograacutefico)

se trata de un valor disponible tanto para las piezas sintaacutecticas nominales (TOKEN) como para las secuencias que se saltan (SKIP)

ↄ Resumen relativo a la lexicografiacutea

TOKEN pieza sintaacutectica nominal∙ ∙ ∙ pieza sintaacutectica anoacutenimaSKIP secuencia de caracteres que no forman pieza

35

Precisiones sobre el analizador sintaacutectico generadoPuede decirse que en su funcionamiento maacutes sencillo cuando no se emplean las posibilidades de examen por adelantado (ldquolookaheadrdquo) JavaCC comprueba que las reglas sintaacutecticas cumplen la condicioacuten LL(1) y generan un analizador descendente-predictivo-recursivo Pero esta apreciacioacuten hay que matizarla ya que el analizador sintaacutectico generado no se ajusta en sentido estricto al modelo de analizador LL(1)

En el modelo de anaacutelisis LL(1) el orden del examen de las distintas alternativas de expansioacuten de un siacutembo-lo no terminal es indiferente ya que se conocen los siacutembolos directores de cada una de ellas (que resultan ser conjuntos disjuntos) en la implementacioacuten del subprograma de anaacutelisis asociado al siacutembolo no terminal ltNombreSimbgt cuyas producciones son

ltNombreSimbgt = α1

| α2

∙ ∙ ∙

| αn

se podriacutea preguntar en cualquier orden si la pieza actual pertenece al conjunto de siacutembolos directores de las distintas reglas Sin embargo en el analizador sintaacutectico generado por JavaCC la implementacioacuten del meacute-todo asociado al siacutembolo no terminal ltNombreSimbgt no sigue esa pauta

Si las anteriores reglas sintaacutecticas se transcriben en JavaCC de la forma

void nombreSimb () α1-jcc

| α2-jcc

∙ ∙ ∙

| αn-jcc

1

accioacutenlexicograacutefica

comuacuten

Commom -TokenAction2

accioacutenlexicograacutefica

propia

(accioacuten ligadaa una pieza)3

bloque dedeclaracioneslexicograacuteficas

TOKEN_MGR_DECLS4

declaracioneslexicograacuteficaspredefinidas

(disponibilidad

de uso)TOKENsisisisi∙ ∙ ∙sino--SKIPnosisisi

Las indicaciones si de las columnas 3 y 4 son consecuencia de las indicaciones de la columna 2

La accioacuten lexicograacutefica comuacuten se ejecuta despueacutes de la accioacuten lexicograacutefica propia

36

el meacutetodo de anaacutelisis generado procede de la siguiente manera

se van considerando las alternativas sucesivamente en el mismo orden en el que estaacuten especifica-das primero la de α1 (α1-jcc) despueacutes la de α2 (α2-jcc) etc

para cada alternativa seleccionada se comprueba si la pieza por adelantado coincide con alguno de los siacutembolos iniciales de la parte derecha y si hay coincidencia se prosigue el anaacutelisis con ella si no hay coincidencia se pasa a la alternativa siguiente

siguiendo con este orden si se llega al final de las alternativas sin haberse encontrado coincidencia alguna se produce un error sintaacutectico

En el analizador generado por JavaCC hay una peculiaridad que no se tiene en los analizadores LL(1) es-trictamente considerados la palabra vaciacutea siacute se considera como posible siacutembolo inicial de la parte derecha una regla (esto ocurre siempre que la parte derecha es anulable) De esta peculiaridad se deriva una conse-cuencia que ha de tenerse en cuenta al escribir la especificacioacuten dado que siempre es posible considerar la palabra vaciacutea como la siguiente pieza por adelantado si se llega a examinar la alternativa correspondiente a una produccioacuten anulable el analizador generado siempre seleccionaraacute esa alternativa con independencia de la siguiente pieza que esteacute presente en la entrada que queda por analizar

Asiacute pues si alguna de las producciones que definen un siacutembolo no terminal es anulable es importante el or-den en que se escriben las alternativas en una especificacioacuten sintaacutectica JavaCC el funcionamiento del ana-lizador generado siacute depende de la colocacioacuten elegida para las reglas

Para ilustrar estas peculiaridades del analizador sintaacutectico generado por JavaCC a continuacioacuten se expone un ejemplo

∆ Ejemplo 9

Se quiere obtener un analizador leacutexico-sintaacutectico de textos que se ajustan al siguiente formato una se-cuencia de uno o maacutes nombres seguida del siacutembolo igual seguido de un nuacutemero entero si la secuencia tiene maacutes de un nombre han de estar separados bien por una coma en todos los casos o bien por el siacutem-bolo dos puntos en todos los casos esto es son correctas las secuencias

uno dos tres = 123exclusivo = 0Abajo Arriba = 57

y son incorrectas las secuenciasuno dos tres = 123

exclusivo inclusive = 0

fuera dentro = 56

Una gramaacutetica que define las secuencias con este formato es

ltListagt = nombre ltOtrosNombresgt = numero

ltOtrosNombresgt = nombre nombre | nombre nombre | ε

se trata de una gramaacutetica que cumple la condicioacuten LL(1)- el siacutembolo director para la alternativa vaciacutea es el siacutembolo igual es evidente pues que las tres reglas de ltOtrosNombresgt tienen siacutembolos directores distintos

37

- la decisioacuten de abandonar la repeticioacuten del anaacutelisis de un nuevo nombre (en las dos primeras alterna-tivas de ltOtrosNombresgt) se puede tomar ante la presencia del siacutembolo igual

En la implementacioacuten de un analizador sintaacutectico LL(1) en sentido estricto la seleccioacuten de la alternativa vaciacutea para expandir el siacutembolo ltOtrosNombresgt se hariacutea tras comprobar expliacutecitamente que la pie-za sintaacutectica por adelantado es el siacutembolo igual

El funcionamiento del analizador generado por JavaCC depende del orden de colocacioacuten de las reglas A continuacioacuten se exponen varias posibilidades de especificacioacuten y se comenta el analizador generado en cada caso

bull Especificacioacuten primeraSe transcriben literalmente las reglas tal y como se han escrito antes en la notacioacuten BNF noacutetese que para poder codificar la parte derecha de la regla vaciacutea ha de incluirse una accioacuten sintaacutectica vaciacutea

PARSER_BEGIN (Igualdad)

public class Igualdad

public static void main (String[] argum) throws ParseException Igualdad analisis = new Igualdad(Systemin) analisissecuencia() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (Igualdad)

void secuencia() lista() ltEOFgt

void lista() lt nombre gt otrosNombres() = lt numero gt

void otrosNombres() lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt ) |

TOKEN lt nombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN lt numero ( [ 0 - 9 ] )+ gt

SKIP | r | n

38

El analizador generado funciona correctamente puesto que en el meacutetodo otrosNombres se selec-ciona la alternativa vaciacutea despueacutes de haber comprobado que la pieza por adelantado no es siacutembolo ini-cial de ninguna de las dos alternativas precedentes (no es una coma ni un siacutembolo dos puntos)

bull Especificacioacuten segundaSe considera la misma especificacioacuten anterior pero cambiando el orden de colocacioacuten de las reglas ahora la produccioacuten vaciacutea se coloca como primera alternativa (soacutelo se muestran las partes modifica-das)

void otrosNombres() | lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt )

Con la entrada de esta especificacioacuten el generador JavaCC produce un mensaje en el que se avisa de que las dos alternativas que estaacuten colocadas detraacutes de la alternativa vaciacutea nunca podraacuten seleccionarse aun asiacute se genera un analizador Pero es un analizador que funciona incorrectamente asiacute por ejemplo la entrada

mxa nyz = 57

se considera incorrecta despueacutes de visto el primer nombre se intenta aplicar la produccioacuten vaciacutea (se espera encontrar el siacutembolo igual) pero habriacutea que aplicar la regla cuyo siacutembolo director es la coma

bull Especificacioacuten terceraSe utilizan las posibilidades de la notacioacuten BNF-Ampliada para prescindir de la especificacioacuten expliacuteci-ta de la palabra vaciacutea se aprovecha que JavaCC admite el operador + para indicar repeticiones de una o maacutes veces la alternativa vaciacutea estaacute impliacutecitamente contemplada en la posibilidad de cero repeti-ciones que contempla el operador (soacutelo se muestran las partes modificadas en la especificacioacuten)

void otrosNombres() ( lt nombre gt )+ | ( lt nombre gt )

El meacutetodo otrosNombres() generado hace las comprobaciones en el siguiente orden

- se comprueba si la pieza por adelantado es una coma (siacutembolo inicial de la primera alternativa) y en este caso se selecciona la primera regla- si no es una coma se comprueba si es un siacutembolo dos puntos (siacutembolo inicial de la segunda alter-nativa) y en este caso se selecciona la segunda regla- si no es un siacutembolo dos puntos se considera la palabra vaciacutea como siacutembolo inicial (siempre posi-ble impliacutecitamente) para la segunda alternativa en este caso para que no haya error la pieza por adelantado ha de ser un siacutembolo igual

Se trata pues de un analizador que funciona correctamente

bull Especificacioacuten cuarta

39

Se pone la misma especificacioacuten anterior pero cambiando el orden de consideracioacuten de la regla vaciacutea ahora se incluye impliacutecitamente en la primera alternativa (soacutelo se muestran las partes modificadas)

void otrosNombres() ( lt nombre gt ) | ( lt nombre gt )+

Para esta especificacioacuten el generador JavaCC emite un aviso en el que se indica que la segunda alter -nativa nunca se seleccionaraacute no obstante se genera un analizador que no funciona como se supone que deberiacutea hacerlo por ejemplo el anaacutelisis de la entrada

nyz mxa = 1

produce un error despueacutes de tratado el primer nombre se intenta aplicar la produccioacuten vaciacutea (corres-pondiente a la repeticioacuten de cero veces contemplada en la primera regla) por lo que no se encuentra el esperado siacutembolo igual

40

Page 17: JavaCC

en el anaacutelisis sintaacutectico de un programa los comentarios han de ignorarse una manera de especificar esta circunstancia es poniendo en una definicioacuten SKIP la expresioacuten regular que denota la forma de los comentarios del lenguaje por ejemplo para los comentarios de una liacutenea de Java se pondriacutea

SKIP

lt (~ [n r]) gt

Tareas asociadas a la estructura sintaacutecticaJavaCC genera un analizador sintaacutectico descendente recursivo constituido por un conjunto de meacutetodos ca-da siacutembolo no terminal de la gramaacutetica sintaacutectica tiene asociado un meacutetodo de anaacutelisis (el meacutetodo tiene el mismo nombre que el siacutembolo correspondiente) Un meacutetodo de anaacutelisis ademaacutes de la realizacioacuten de la parte del anaacutelisis sintaacutectico que le corresponde puede llevar a cabo otras tareas complementarias que sirvan para efectuar un anaacutelisis maacutes complejo del texto de entrada

loz Bloque para un siacutemboloAl describir la forma de las reglas sintaacutecticas de un siacutembolo no terminal en una especificacioacuten JavaCC ya se ha visto que detraacutes de la definicioacuten del nombre del siacutembolo y antes de empezar con las diferentes alter-nativas que tiene asociadas se incluye un bloque de coacutedigo Java (en el ejemplo de presentacioacuten este bloque estaacute vaciacuteo en todos los siacutembolos no terminales de la gramaacutetica) Cada bloque situado en esa posicioacuten puede considerarse ligado al siacutembolo no terminal en cuya descripcioacuten estaacute incluido el coacutedigo Java del bloque se traslada literalmente al principio del meacutetodo generado correspondiente al siacutembolo no terminal y por lo tan-to es lo primero que se considera (se ejecuta) cuando en el transcurso del proceso de anaacutelisis se realiza una llamada al meacutetodo Asiacute pues cada siacutembolo no terminal siempre tiene asociado un uacutenico bloque de coacute-digo Java ndashbloque de presencia obligada aunque pueda estar vaciacuteo- en este bloque se pueden poner de-claraciones y sentencias

loz Acciones sintaacutecticasAdemaacutes del bloque asociado a cada uno de los siacutembolos no terminales de la gramaacutetica se pueden incluir otros bloques de coacutedigo Java intercalados en cualquiera de las partes derechas de las reglas sintaacutecticas Los bloques incluidos en las partes derechas se denominan ldquoacciones sintaacutecticasrdquo (parser actions) ya que son trozos de coacutedigo que pueden considerarse asociados a los puntos de la estructura sintaacutectica donde estaacuten co-locados Una accioacuten sintaacutectica se puede poner en cualquier posicioacuten al principio al final o en el medio de la parte derecha de una regla precedida o seguida de siacutembolos terminales o no terminales el coacutedigo Java de una accioacuten sintaacutectica se traslada literalmente al meacutetodo asociado al siacutembolo no terminal de la parte izquierda de la regla de manera que se ejecuta cada vez que el analizador transcurre por el punto de la estructura sintaacutectica donde se ha incorporado la accioacuten (esto es las acciones sintaacutecticas se incorporan al coacutedigo del analizador sintaacutectico) No obstante su nombre en los bloques de las acciones sintaacutecticas suele ponerse coacutedigo dedicado a la realizacioacuten de tareas semaacutenticas

Si en el bloque asociado a un siacutembolo no terminal se ponen declaraciones los nombres ahiacute definidos son accesibles en todas las acciones sintaacutecticas intercaladas en las partes derechas de las reglas de ese siacutembolo la justificacioacuten es sencilla el coacutedigo del bloque asociado al siacutembolo se incluye al principio de un meacutetodo y el coacutedigo de las acciones pertenece a ese mismo meacutetodo

∆ Ejemplo 1

Se considera de nuevo el ejemplo de presentacioacuten ahora se pretende completar el anaacutelisis leacutexico-sintaacutec-tico con la realizacioacuten de las siguientes tareas

- cada vez que se analiza una expresioacuten un teacutermino o un factor se graba el nombre del siacutembolo no terminal correspondiente y

16

- cuando se analiza un teacutermino en el que hay operadores multiplicativos se cuenta su nuacutemero y se graba el resultado (noacutetese que se cuentan los operadores de cada teacutermino pero no se acumula la cuenta de todos los operadores de la expresioacuten)

Para la realizacioacuten de estas tareas se antildeaden bloques de coacutedigo Java En la especificacioacuten que se describe a continuacioacuten puede apreciarse- para los siacutembolos no terminales expresion termino y factor se ha incluido en los blo-ques correspondientes la operacioacuten de grabacioacuten de su nombre que se ejecutaraacute cada vez que se efec-tuacutee una llamada al meacutetodo- en el bloque asociado a termino se tiene la declaracioacuten del contador de operadores (que seraacute una variable local del meacutetodo)- en la parte derecha de la regla que define la estructura de un teacutermino se han incluido dos acciones sintaacutecticas una para contar la cantidad de operadores (situada detraacutes de la pieza sintaacutectica del opera-dor) y otra para grabar la cantidad de operadores del teacutermino (situada al final de la parte derecha)- se ha definido el meacutetodo grabar para facilitar la escritura del coacutedigo Java

options Ignore_Case = true

PARSER_BEGIN (ExprMin) public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

private static void grabar(String nombre) Systemoutprintln ( -gt + nombre + n)

PARSER_END (ExprMin)

void unaExpresion()

expresion() ltEOFgt

void expresion() grabar(Expresion)

termino() ( + termino() )

void termino() int nAst = 0 grabar(Termino)

17

factor() ( nAst++ factor() ) if (nAst gt 0) Systemoutprintln (n Asteriscos + nAst + n)

void factor() grabar(Factor)

ltconstantegt | ltvariablegt | ( expresion() )

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP | t | n | r

Cuando se incluyen acciones sintaacutecticas la legibilidad del aspecto sintaacutectico de la especificacioacuten queda oscurecida para paliar ese inconveniente puede antildeadirse un comentario que muestre la regla sintaacutectica escueta sin las acciones por ejemplo podriacutea escribirse

void termino() int nAst = 0 grabar(Termino)

factor() ( factor() )

factor()

( nAst++ factor() )

if (nAst gt 0) Systemoutprintln (n Asteriscos + nAst + n)

loz Valor comunicado por un meacutetodoEn todo lo visto hasta ahora los meacutetodos de anaacutelisis sintaacutectico generados por JavaCC no devuelven valor alguno al terminar su tarea de anaacutelisis ya que en la especificacioacuten siempre se ha declarado el nombre de un meacutetodo precedido de void (son meacutetodos de tipo void) Pero en general un meacutetodo de anaacutelisis sintaacutec-tico puede ser de cualquier tipo (primitivo o no) por ello un meacutetodo puede devolver un valor de cualquier tipo (de cualquier clase) al meacutetodo desde el que se efectuoacute la llamada

Esta posibilidad de devolucioacuten de valores tiene una utilidad fundamental en la implementacioacuten de analiza-dores de lenguajes permite la comunicacioacuten de datos (atributos de las entidades analizadas) entre los dife -rentes meacutetodos que colaboran en el anaacutelisis del programa completo

En una especificacioacuten JavaCC la llamada a un meacutetodo ocurre cuando en la parte derecha de una regla estaacute el nombre del siacutembolo no terminal asociado al meacutetodo Cuando un meacutetodo devuelve un valor (tiene un tipo distinto de void) es habitual la recepcioacuten del valor devuelto en una variable en la propia especificacioacuten

18

se puede indicar el nombre de la variable a la que se asignaraacute el valor devuelto tras la llamada para ello se emplea una notacioacuten ideacutentica a la forma que tienen las sentencias de asignacioacuten en el lenguaje Java

dato = nombreSimb()

donde dato es el nombre de la variable a la que se asigna el valor devuelto por el meacutetodo llamado esa va -riable ha de estar oportunamente declarada del mismo tipo que dicho meacutetodo

∆ Ejemplo 2

En el mismo ejemplo de presentacioacuten se desea ademaacutes del anaacutelisis leacutexico-sintaacutectico de la entrada contar la cantidad de operadores que hay en la expresioacuten A continuacioacuten se expone una especificacioacuten JavaCC que es una posible solucioacuten soacutelo se muestran las reglas sintaacutecticas (el resto de la especificacioacuten es igual que en el ejemplo de presentacioacuten)

En la especificacioacuten que se propone puede apreciarse- los siacutembolos no terminales expresion termino y factor se han declarado de tipo int los meacutetodos correspondientes devuelven un valor de tipo entero- el valor devuelto por los meacutetodos sirve para propagar a traveacutes de la estructura sintaacutectica la cuenta de los operadores que hay en el texto analizado- por tratarse de meacutetodos que devuelven un valor es preciso incluir las correspondientes sentencias de devolucioacuten return

void unaExpresion() int nOper nOper = expresion() ltEOFgt Systemoutprintln (Cantidad de operadores = + nOper)

int expresion() int n i n = termino() ( + n++ i = termino() n = n + i )

return n

int termino() int n i n = factor() ( n++ i = factor() n = n + i )

return n

int factor() int n ltconstantegt return 0 | ltvariablegt return 0 | ( n = expresion() ) return n

La especificacioacuten de un factor tambieacuten podriacutea escribirse de esta otra manera

19

int factor() int n = 0

( ltconstantegt | ltvariablegt | ( n = expresion() ) )

return n

A continuacioacuten se expone un nuevo ejemplo para ilustrar la utilidad que proporciona el valor devuelto por un meacutetodo de anaacutelisis sintaacutectico para la propagacioacuten (ascendente) de atributos (propiedades de las entida-des analizadas)

∆ Ejemplo 3

Se pretende ampliar el analizador del ejemplo de presentacioacuten para que se emita un mensaje indicativo cuando la expresioacuten analizada sea una expresioacuten en la que soacutelo aparecen constantes en la solucioacuten que se muestra soacutelo se incluyen las reglas sintaacutecticas (en el resto de la especificacioacuten no hay modificacio-nes)

void unaExpresion() boolean esCte

expresion() ltEOFgt

esCte = expresion() ltEOFgt

if (esCte) Systemoutprintln(nExpresion constanten)

boolean expresion() boolean esCte esTambienCte

termino() ( + termino() )

esCte = termino() ( + esTambienCte = termino() esCte = esCte ampamp esTambienCte )

return esCte

boolean termino() boolean esCte esTambienCte

factor() ( factor() )

esCte = factor() ( esTambienCte = factor() esCte = esCte ampamp esTambienCte )

return esCte

20

boolean factor() boolean esCte

ltconstantegt return true | ltvariablegt return false | ( esCte = expresion() ) return esCte

bull Reconsideracioacuten sobre las producciones vaciacuteasAnteriormente se ha comentado que para representar una produccioacuten vaciacutea en una especificacioacuten JavaCC hay que hacer uso de los metasiacutembolos de opcionalidad para ilustrar esta idea se ha incluido como ejem-plo la especificacioacuten de una lista de paraacutemetros compuesta por cero o maacutes expresiones separadas entre siacute por el caraacutecter coma

void parametros() [ expresion() ( expresion() ) ]

Pero si se aprovecha la posibilidad de incorporar acciones sintaacutecticas se tiene una manera de especificar directamente producciones vaciacuteas para conseguirlo basta considerar una produccioacuten vaciacutea (no tiene nada en su parte derecha) acompantildeada de una accioacuten sintaacutectica cuyo bloque de coacutedigo esteacute vaciacuteo Seguacuten esta posibilidad la especificacioacuten anterior podriacutea tambieacuten escribirse asiacute

void parametros() expresion() ( expresion() ) |

En general la accioacuten sintaacutectica asociada a una produccioacuten vaciacutea no tiene por queacute ser un bloque vaciacuteo de coacutedigo por ejemplo si se quiere que el meacutetodo que analiza los paraacutemetros de una llamada devuelva la cantidad de paraacutemetros encontrados se podriacutea escribir la siguiente especificacioacuten

int parametros()

int cantidad

( expresion() cantidad = 1

( expresion() cantidad++ )

| cantidad = 0 )

return cantidad

y si no quisiera ponerse expliacutecitamente la alternativa de la produccioacuten vaciacutea podriacutea escribirse

int parametros() int cantidad = 0

21

[ expresion() cantidad++ ( expresion() cantidad++ ) ]

return cantidad

Comunicacioacuten entre los analizadores leacutexico y sintaacutecticoCada vez que el analizador sintaacutectico realiza una llamada al analizador lexicograacutefico recibe como resultado de la llamada una representacioacuten de la pieza sintaacutectica encontrada en el texto analizado En los analizadores generados por JavaCC la comunicacioacuten de la pieza se efectuacutea mediante un valor de clase Token Esta cla-se estaacute definida en el fichero Tokenjava que se obtiene en todo caso siempre con el mismo contenido cualquiera que sea la especificacioacuten proporcionada como entrada al generador

Entre los campos de la clase Token se encuentran los siguientes

public int kind

nuacutemero entero que sirve de representacioacuten interna de la pieza sintaacutectica estos nuacutemeros asociados a las piezas son asignados automaacuteticamente por JavaCC los valores considerados pueden consultar-se en el fichero generado de nombre Constantsjava

public String image

cadena que contiene de la secuencia de caracteres (lexema) que constituyen la pieza sintaacutectica co-municada

public int beginLine beginColumn endLine endColumn

posiciones ocupadas (nuacutemeros de liacutenea y columna en el fichero de entrada) por el comienzo y el fi-nal del lexema de la pieza sintaacutectica comunicada

La clase Token tiene otros campos y meacutetodos en el fichero Tokenjava se tienen comentarios des-criptivos sobre todos los campos y meacutetodos que forman parte de la clase todos los campos de la clase se declaran como public

Ya se ha comentado anteriormente que cuando un meacutetodo de anaacutelisis sintaacutectico (asociado a un siacutembolo no terminal) tiene tipo el valor devuelto tras una llamada se puede almacenar en una variable por si se desea utilizar en alguna accioacuten sintaacutectica para ello se escribe

valor = nombreSimbolo()

(siendo valor el nombre de la variable y nombreSimbolo el nombre del siacutembolo no terminal) De manera anaacuteloga el objeto de la clase Token representativo de la pieza sintaacutectica encontrada en la entrada por el analizador lexicograacutefico puede asignarse a una variable del mismo tipo para tener accesibles las ca -racteriacutesticas de la pieza por si se precisaran en alguna accioacuten sintaacutectica para ello en la parte de la especifi -cacioacuten sintaacutectica donde aparezca la pieza sintaacutectica se escribe

dato = lt nombrePieza gt

donde nombrePieza es el nombre de la pieza sintaacutectica y dato es el nombre de una variable de tipo Token en la que se anota el valor indicativo de las propiedades de la pieza sintaacutectica comunicada

Tambieacuten existe otra manera de acceder a las caracteriacutesticas de la pieza sintaacutectica que el analizador lexico -graacutefico comunica al analizador sintaacutectico en la clase del analizador sintaacutectico se tiene declarado el campo

[ static ] public Token token

22

cada vez que el analizador sintaacutectico recibe una pieza sintaacutectica comunicada por el analizador lexicograacutefico se deja anotada en el campo token y ese contenido no se altera hasta que sea proporcionada la siguiente pieza de la entrada

∆ Ejemplo 4

Se considera una ampliacioacuten del ejemplo de presentacioacuten ademaacutes del anaacutelisis de la entrada se pretende obtener una relacioacuten numerada de las variables y las constantes indicando el nuacutemero de liacutenea y el nuacuteme-ro de columna donde estaacuten situadas por ejemplo si en la entrada se tiene la expresioacuten (grabada en dos liacuteneas)

x (y + 11) + 22

en la salida se tendraacute

1- x linea 1 columna 1

2- y linea 1 columna 6

3- 11 linea 1 columna 10

4- 22 linea 2 columna 5

En la especificacioacuten que se propone como solucioacuten puede apreciarse- en la clase del analizador sintaacutectico se han incluido meacutetodos auxiliares en los que se usan los nom-bres image beginLine beginColumn que son campos de un objeto de clase Token

- en la especificacioacuten sintaacutectica de factor se emplea la variable pieza de tipo Token para reco-ger las caracteriacutesticas de las piezas sintaacutecticas detectadas- la variable numero hace de contador para numerar las constantes y variables

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin

static int numero = 0

private static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

private static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

private static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

23

PARSER_END (ExprMin)

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() Token pieza pieza = ltconstantegt numero++ grabarDatosPieza(numero pieza) | pieza = ltvariablegt numero++ grabarDatosPieza(numero pieza) | ( expresion() )

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP | n | t | r

Si se agrupan las dos acciones sintaacutecticas comunes la especificacioacuten de factor tambieacuten puede escri-birse de esta forma

void factor() Token pieza ( pieza = ltconstantegt | pieza = ltvariablegt

) numero++ grabarDatosPieza(numero pieza)

| ( expresion() )

24

Si se accede al campo token de la clase del analizador sintaacutectico se puede resolver este ejemplo pres-cindiendo de la variable pieza

void factor() ( ltconstantegt | ltvariablegt

) numero++ grabarDatosPieza(numero token)

| ( expresion() )

Tareas asociadas a las piezas sintaacutecticasYa se ha visto que es posible asociar bloques de coacutedigo Java a puntos de la estructura sintaacutectica para que se realicen ciertas operaciones cuando el analizador sintaacutectico pase por esa zona de la estructura Tambieacuten es posible asociar coacutedigo Java a las piezas sintaacutecticas para que el analizador lexicograacutefico efectuacutee determina-das tareas cuando detecte en la entrada analizada piezas que tengan asociado coacutedigo

loz Accioacuten ligada a una pieza sintaacutecticaSi se asocia un bloque de coacutedigo a una pieza sintaacutectica se consigue que cada vez que se detecta en la en-trada analizada la presencia de esa pieza se ejecute en ese preciso momento el coacutedigo del bloque asociado a la pieza Estas acciones se denominan ldquoacciones lexicograacuteficasrdquo (lexical actions) el coacutedigo del bloque se traslada literalmente al coacutedigo del analizador lexicograacutefico generado para que se ejecute oportunamente cuando se detecte la pieza ligada al bloque de coacutedigo La accioacuten lexicograacutefica se ejecuta inmediatamente antes que la pieza sea comunicada al analizador sintaacutectico

Se puede asociar una accioacuten lexicograacutefica a una pieza sintaacutectica declarada con TOKEN y tambieacuten a una se-cuencia de caracteres declarada con SKIP ademaacutes de eacutestas hay otras dos formas de declaraciones lexico-graacuteficas (no mencionadas en esta introduccioacuten) que tambieacuten pueden llevar una accioacuten asociada

En una especificacioacuten JavaCC la forma de una pieza sintaacutectica (o bien de una secuencia de caracteres) se define poniendo su descripcioacuten delimitada entre llaves detraacutes de la indicacioacuten TOKEN (o bien SKIP) si se quiere asociar una accioacuten lexicograacutefica a la pieza (o a la secuencia) hay que colocar el bloque de coacutedigo de la accioacuten a continuacioacuten de la descripcioacuten de la forma de la pieza (o de la secuencia) el bloque (delimi-tado por llaves) queda situado entre la descripcioacuten y la llave que cierra la especificacioacuten

La pieza sintaacutectica especial que representa el final del fichero (EOF) tambieacuten puede llevar asociada una ac-cioacuten lexicograacutefica para ello hay que poner

ltgt TOKEN lt EOF gt coacutedigo de la accioacuten asociada

para justificar la presencia de los caracteres ltgt delante de la palabra TOKEN hay que basarse en el con-cepto de ldquoestado lexicograacuteficordquo (cuya explicacioacuten queda fuera del alcance de esta introduccioacuten)

∆ Ejemplo 5

Se considera nuevamente el ejemplo de presentacioacuten como resultado del anaacutelisis de una expresioacuten se pretende obtener una relacioacuten de los pareacutentesis contenidos en la expresioacuten en el mismo orden en que se encuentran ademaacutes por cada salto de liacutenea del texto de entrada se grabaraacute en la salida el caraacutecter y

25

se pasaraacute a una nueva liacutenea tambieacuten se anotaraacute la frase Final del trabajo cuando se alcance el final del fichero de entrada Asiacute por ejemplo si la entrada analizada es

(a + b) (( x )) (y + 7)

los resultados obtenidos seraacuten

() (())()

Final del trabajo

En la especificacioacuten JavaCC que se propone como solucioacuten soacutelo se muestran las reglas sintaacutecticas y le -xicograacuteficas (en el resto no hay cambios) en esta solucioacuten puede apreciarse- las piezas sintaacutecticas correspondientes a los pareacutentesis se han definido de manera nominal para po-der asociarles una accioacuten lexicograacutefica- los caracteres incluidos en la pieza de tipo SKIP se han descompuesto en dos grupos ya que la ac-cioacuten lexicograacutefica incorporada soacutelo afecta a uno de ellos

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ltpAbrirgt expresion() ltpCerrargt

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

TOKEN

26

lt pAbrir ( gt Systemoutprint(()

TOKEN lt pCerrar ) gt Systemoutprint())

ltgtTOKEN lt EOF gt Systemoutprintln ( Final del trabajo )

SKIP n Systemoutprint( n)

SKIP | t | r

loz Bloque de declaraciones lexicograacuteficasCada definicioacuten de una pieza sintaacutectica puede llevar asociada una accioacuten lexicograacutefica que se ejecuta in-mediatamente despueacutes de detectar en la entrada un lexema que se ajusta a la definicioacuten de la pieza cada accioacuten lexicograacutefica es un bloque de coacutedigo Java que puede contener sus propias declaraciones Pero a ve-ces interesa tener declaraciones que sean compartidas por todas las acciones lexicograacuteficas con indepen-dencia de la pieza a la que esteacute asociada cada una de ellas

Para incluir declaraciones que puedan usarse desde el coacutedigo asociado a cualquiera de las piezas sintaacutecti -cas se dispone de una construccioacuten con el siguiente formato

TOKEN_MGR_DECLS

que consta de una palabra reservada seguida del siacutembolo dos puntos seguido de un bloque de coacutedigo Java (delimitado por llaves) esta declaracioacuten se puede colocar en cualquier parte de una especificacioacuten JavaCC si se incluye soacutelo puede ponerse una vez

Todas las declaraciones de variables o de meacutetodos puestas en el bloque de declaraciones lexicograacuteficas compartidas son accesibles desde el coacutedigo de cualquier accioacuten lexicograacutefica la justificacioacuten es sencilla el bloque de declaraciones se traslada al principio del coacutedigo del analizador lexicograacutefico generado y las ac-ciones lexicograacuteficas tambieacuten se trasladan a ese analizador

loz Declaraciones lexicograacuteficas predefinidasDe lo expuesto hasta ahora se aprecia que en una accioacuten lexicograacutefica asociada a una pieza sintaacutectica re-sultan accesibles

- las declaraciones de su propio bloque (si tiene declaraciones)- los componentes del bloque de declaraciones compartidas (si estaacute incluido en la especificacioacuten)

Ademaacutes de esto se tienen otras declaraciones pertenecientes a la clase TokenManager que constituye el analizador lexicograacutefico que tambieacuten son utilizables desde cualquiera de las acciones lexico-graacuteficas estas declaraciones pueden considerarse como ldquopredefinidasrdquo ya que son incorporadas automaacuteti-camente por el generador Entre estas declaraciones accesibles se encuentran las siguientes

[ static ] StringBuffer imagecontiene la cadena de caracteres representativa de la pieza sintaacutectica actual que se acaba de recono-cer en le entrada se trata de un campo de la clase y se declara como static cuando el analizador

27

se ha generado con la opcioacuten (por defecto) Static = true si se generase con la opcioacuten Sta-tic = false no quedariacutea declarada como static

Token matchedToken

variable de clase Token que representa la pieza sintaacutectica actual la que se acaba de encontrar en la entrada se trata de una variable local del meacutetodo al que se ha trasladado el coacutedigo de la accioacuten (y por ello accesible desde ese coacutedigo)

El campo image puede usarse tanto si la accioacuten lexicograacutefica estaacute asociada a una pieza declarada con TOKEN como si lo estaacute a una secuencia declarada con SKIP Sin embargo la variable matchedToken no puede usarse en una accioacuten asociada a un patroacuten especificado como SKIP el motivo es que lo declara-do como SKIP no constituye propiamente una pieza sintaacutectica ya que no se va a comunicar al analizador sintaacutectico

∆ Ejemplo 6

Se propone aquiacute una nueva solucioacuten para el ejemplo nordm 4 expuesto anteriormente

Esta nueva solucioacuten tal y como se expone aquiacute admite algunas mejoras ya que su pretensioacuten soacutelo es ser-vir de ilustracioacuten para algunos aspectos que se acaban de citar (maacutes adelante se propone otra solucioacuten al-ternativa)

En la especificacioacuten de la solucioacuten es posible apreciar esto- las acciones lexicograacuteficas pueden llevar sus propias declaraciones las variables nLineaVar nColumnaVar nLineaCte y nColumnaCte se declaran dentro de las acciones

- la variable matchedToken a la que se hace referencia en el coacutedigo de la accioacuten lexicograacutefica aso-ciada a una variable contiene la informacioacuten sobre la pieza sintaacutectica que se acaba de detectar una va-riable de la expresioacuten analizada anaacutelogamente con la referencia relativa a una constante- el nombre image que aparece en las acciones lexicograacuteficas se refiere al campo de la clase Expr-MinTokenManager (analizador lexicograacutefico) noacutetese que en efecto se comunica como un paraacuteme-tro de clase StringBuffer

- la variable numero se emplea para numerar las constantes y variables encontradas

- los nombres beginLine y beginColumn son campos (puacuteblicos) de la clase Token por ello se pueden aplicar a un objeto de esa clase

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

void unaExpresion()

28

expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n StringBuffer lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

TOKEN lt variable [a-z] gt

int nLineaVar nColumnaVar numero++ nLineaVar = matchedTokenbeginLine nColumnaVar = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaVar nColumnaVar)

TOKEN lt constante ( [0-9] ) + gt

int nLineaCte nColumnaCte

29

numero++ nLineaCte = matchedTokenbeginLine nColumnaCte = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaCte nColumnaCte)

SKIP | n | t | r

La especificacioacuten precedente se puede escribir de otra manera maacutes escueta como se indica a continua-cioacuten (soacutelo se muestran las modificaciones) sobre esta nueva especificacioacuten puede mencionarse- se han eliminado por innecesarias las variables declaradas dentro de las acciones lexicograacuteficas- el nombre image que aparece en el bloque de las declaraciones compartidas se refiere al campo de la clase Token noacutetese que en efecto se aplica a un objeto de esa clase y se comunica como un paraacute -metro de tipo String

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

TOKEN lt variable [a-z] gt

numero++ grabarDatosPieza(numero matchedToken)

TOKEN lt constante ( [0-9] ) + gt

numero++ grabarDatosPieza(numero matchedToken)

loz Accioacuten lexicograacutefica comuacutenSi hubiera que realizar la misma tarea tras la deteccioacuten de todas y cada una de las piezas sintaacutecticas una posibilidad incoacutemoda seriacutea repetir el mismo coacutedigo en todas las acciones lexicograacuteficas para evitar tanta reiteracioacuten se tiene la opcioacuten Common_Token_Action su valor por defecto es false pero si se le asigna el valor true es posible definir una ldquoaccioacuten lexicograacutefica comuacutenrdquo que se escribe una uacutenica vez y se aplica a todas las piezas sintaacutecticas

30

Cuando la opcioacuten Common_Token_Action se establece con el valor true es obligado incluir el blo-que de declaraciones lexicograacuteficas compartidas (TOKEN_MGR_DECLS) Ademaacutes en ese bloque se preci-sa la definicioacuten de un meacutetodo cuyo encabezamiento sea

[ static ] void CommonTokenAction(Token t)

el coacutedigo de este meacutetodo constituye la accioacuten lexicograacutefica comuacuten que se ejecutaraacute despueacutes de la deteccioacuten de cualquier pieza sintaacutectica el meacutetodo tiene un paraacutemetro de tipo Token mediante el que se puede co-municar la informacioacuten correspondiente a la pieza que se acaba de encontrar en la entrada analizada

Si una pieza sintaacutectica tiene asociada una accioacuten lexicograacutefica propia la accioacuten lexicograacutefica comuacuten (si es-taacute definida) se ejecutaraacute inmediatamente despueacutes de realizar la accioacuten propia

La accioacuten lexicograacutefica comuacuten se aplica a todas las piezas sintaacutecticas definidas como TOKEN bien sea no-minalmente (en las reglas lexicograacuteficas) o bien anoacutenimamente (incorporadas dentro de las reglas sintaacutecti-cas) sin embargo no se aplica cuando la secuencia acoplada se corresponde con una expresioacuten regular es-pecificada como SKIP (noacutetese que por el contrario una especificacioacuten SKIP siacute puede tener asociada una accioacuten lexicograacutefica propia)

El coacutedigo del meacutetodo CommonTokenAction se traslada como un componente maacutes del bloque de decla-raciones compartidas (TOKEN_MGR_DECLS) al principio de la clase del analizador lexicograacutefico

∆ Ejemplo 7

Se pretende ahora ampliar el ejemplo de presentacioacuten con la obtencioacuten de una relacioacuten numerada de los lexemas de las piezas sintaacutecticas encontradas en la entrada analizada en el caso de las variables y de las constantes antes del lexema se mostraraacute un indicativo (Var Const) por ejemplo si la expresioacuten de la entrada es

( a + 987 ) b

se grabaraacute la salida

( 1Var a 2+ 3Const 987 4) 5 6Var b 7 8

En la solucioacuten que se propone a continuacioacuten se muestran la definicioacuten de las opciones y las reglas sin-taacutecticas y lexicograacuteficas (en el resto no hay modificaciones) puede resaltarse lo siguiente- las llamadas al meacutetodo CommonTokenAction se incorporan automaacuteticamente no hay que incluir-las en la especificacioacuten como paraacutemetro de llamada a este meacutetodo siempre se emplea el objeto que re -presenta la pieza que se acaba de detectar- la accioacuten lexicograacutefica comuacuten realiza la actualizacioacuten del contador de piezas y la grabacioacuten del lexe-ma de la pieza detectada con su nuacutemero de orden- la accioacuten lexicograacutefica comuacuten tambieacuten se aplica a las piezas sintaacutecticas anoacutenimas (pareacutentesis y ope-radores) y a la pieza especial que representa el final del fichero (EOF)

- las piezas sintaacutecticas ltconstantegt y ltvariablegt tienen asociada una accioacuten lexicograacutefica propia que se ejecuta antes de la accioacuten lexicograacutefica comuacuten- los caracteres que se saltan (SKIP) no estaacuten afectados por la accioacuten lexicograacutefica comuacuten

options Ignore_Case = true Common_Token_Action = true

31

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarIndicativo(String indicativo) Systemoutprint(indicativo + )

static void grabarLexema (int num String lexema) Systemoutprint(lexema + + num + n)

static void CommonTokenAction(Token pieza) numero++ grabarLexema(numero piezaimage)

TOKEN lt variable [a-z] gt

grabarIndicativo(Var)

32

TOKEN lt constante ( [0-9] ) + gt

grabarIndicativo(Const)

SKIP | n | t | r

A modo de ejemplo de aplicacioacuten de la accioacuten lexicograacutefica comuacuten en lo que sigue se ofrece una solucioacuten para la grabacioacuten (en el fichero predefinido de salida) del texto analizado con las liacuteneas numeradas

∆ Ejemplo 8 [ Listado del texto analizado ]Se desea completar el ejemplo de presentacioacuten con la grabacioacuten de un listado con las liacuteneas numeradas del texto analizado (noacutetese que una expresioacuten puede escribirse ocupando varias liacuteneas) No se muestra la especificacioacuten sintaacutectica debido a que no hay ninguna modificacioacuten en ella Los aspectos resentildeables de la solucioacuten que se propone son- se usa la variable numLin como contador de liacuteneas

- en la variable linea se van yuxtaponiendo los lexemas de las piezas sintaacutecticas de una liacutenea esta yuxtaposicioacuten se realiza mediante la accioacuten lexicograacutefica comuacuten- las secuencias reconocidas con los patrones especificados como SKIP no implican la ejecucioacuten de la accioacuten lexicograacutefica comuacuten por ello su lexema (almacenado en la variable image) ha de yuxtaponer-se expliacutecitamente- hay que separar el patroacuten correspondiente al fin de liacutenea para detectar el momento en que ha de gra-barse la liacutenea completa cuyo final se ha alcanzado

options Ignore_Case = true Common_Token_Action = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(nnAnalisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

TOKEN_MGR_DECLS static int numLin = 0 static String linea =

33

static void CommonTokenAction(Token pieza) linea = linea + piezaimage

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP lt | t | r gt linea = linea + image

SKIP n numLin++ Systemoutprintln(numLin + + linea) linea =

A modo de recapitulacioacutenↄ Accioacuten lexicograacutefica Accioacuten sintaacutectica

bull accioacuten lexicograacutefica bloque de coacutedigo Java asociado a una pieza sintaacutectica nominal (TOKEN) o a una secuencia de caracteres (SKIP) se ejecuta cuando se detecta en la entrada la pieza o la secuencia corres-pondiente no se puede aplicar a piezas sintaacutecticas anoacutenimas (autodefinidas con comillas)bull accioacuten sintaacutectica bloque de coacutedigo Java asociado a un punto de la estructura sintaacutectica (el punto indica-do por el sitio de la produccioacuten donde se inserta el bloque) se ejecuta cuando el anaacutelisis de la entrada pa-sa por ese punto de la estructura

ↄ Valor de una pieza sintaacutectica comunicadaEl analizador lexicograacutefico comunica al analizador sintaacutectico una pieza a traveacutes de un valor de la clase Token (fichero Tokenjava) un objeto de la clase Token representa las caracteriacutesticas de la pieza comunicada En la clase Token entre otros se tienen los campos

String imagelexema de la pieza

int beginLine beginColumn endLine endColumnposicioacuten del lexema en el fichero analizado (indicada por los nuacutemeros de fila y de columna de su comienzo y de su terminacioacuten)

ↄ Valor asociado a un siacutembolo

34

En las producciones que especifican la sintaxis se pueden incluir asignaciones que sirven para dejar ano-tado el valor asociado a un siacutembolo

bull siacutembolo no terminal valor = nombreSimbolo()el meacutetodo de anaacutelisis asociado a nombreSimbolo se ha declarado de un cierto tipo (distinto del tipo void) valor ha de ser una variable del mismo tipo que el meacutetodo

bull siacutembolo terminal dato = lt nombrePieza gtel valor asignado es el valor de la clase Token representativo de la pieza sintaacutectica (nominal) dato ha de ser una variable de tipo Token

ↄ Miradas sobre la pieza sintaacutectica comunicadaLa pieza sintaacutectica comunicada puede mirarse desde cada uno de los dos analizadores

bull analizador sintaacutecticola pieza comunicada (desde el analizador lexicograacutefico) se tiene anotada en un campo de la clase del analizador sintaacutectico declarado como

(static) public Token token

este valor es accesible desde el coacutedigo de todos los meacutetodos de anaacutelisis sintaacutectico el contenido del campo variacutea cuando se recibe una nueva pieza sintaacutectica

bull analizador lexicograacuteficola pieza dispuesta para ser comunicada (al analizador sintaacutectico) se tiene anotada en una variable local declarada como

Token matchedToken

este valor es accesible para el coacutedigo de todas las acciones lexicograacuteficas

ↄ El lexema de la pieza comunicadaEl lexema de la pieza sintaacutectica comunicada se encuentra disponible en dos sitios distintos que compar-ten el mismo nombre pero que son de distinto tipo y se usan de distinta manera

bull String image

es un campo de la clase TOKEN puede consultarse en relacioacuten con un objeto de esa clase que esteacute disponible

bull StringBuffer image

es un campo de la clase del analizador lexicograacutefico ( ∙ ∙ ∙ ∙ TokenManagerjava) es accesible desde el coacutedigo de la clase y por lo tanto desde todas las acciones lexicograacuteficas (bloques de coacute-digo que estaacuten incorporados al analizador lexicograacutefico)

se trata de un valor disponible tanto para las piezas sintaacutecticas nominales (TOKEN) como para las secuencias que se saltan (SKIP)

ↄ Resumen relativo a la lexicografiacutea

TOKEN pieza sintaacutectica nominal∙ ∙ ∙ pieza sintaacutectica anoacutenimaSKIP secuencia de caracteres que no forman pieza

35

Precisiones sobre el analizador sintaacutectico generadoPuede decirse que en su funcionamiento maacutes sencillo cuando no se emplean las posibilidades de examen por adelantado (ldquolookaheadrdquo) JavaCC comprueba que las reglas sintaacutecticas cumplen la condicioacuten LL(1) y generan un analizador descendente-predictivo-recursivo Pero esta apreciacioacuten hay que matizarla ya que el analizador sintaacutectico generado no se ajusta en sentido estricto al modelo de analizador LL(1)

En el modelo de anaacutelisis LL(1) el orden del examen de las distintas alternativas de expansioacuten de un siacutembo-lo no terminal es indiferente ya que se conocen los siacutembolos directores de cada una de ellas (que resultan ser conjuntos disjuntos) en la implementacioacuten del subprograma de anaacutelisis asociado al siacutembolo no terminal ltNombreSimbgt cuyas producciones son

ltNombreSimbgt = α1

| α2

∙ ∙ ∙

| αn

se podriacutea preguntar en cualquier orden si la pieza actual pertenece al conjunto de siacutembolos directores de las distintas reglas Sin embargo en el analizador sintaacutectico generado por JavaCC la implementacioacuten del meacute-todo asociado al siacutembolo no terminal ltNombreSimbgt no sigue esa pauta

Si las anteriores reglas sintaacutecticas se transcriben en JavaCC de la forma

void nombreSimb () α1-jcc

| α2-jcc

∙ ∙ ∙

| αn-jcc

1

accioacutenlexicograacutefica

comuacuten

Commom -TokenAction2

accioacutenlexicograacutefica

propia

(accioacuten ligadaa una pieza)3

bloque dedeclaracioneslexicograacuteficas

TOKEN_MGR_DECLS4

declaracioneslexicograacuteficaspredefinidas

(disponibilidad

de uso)TOKENsisisisi∙ ∙ ∙sino--SKIPnosisisi

Las indicaciones si de las columnas 3 y 4 son consecuencia de las indicaciones de la columna 2

La accioacuten lexicograacutefica comuacuten se ejecuta despueacutes de la accioacuten lexicograacutefica propia

36

el meacutetodo de anaacutelisis generado procede de la siguiente manera

se van considerando las alternativas sucesivamente en el mismo orden en el que estaacuten especifica-das primero la de α1 (α1-jcc) despueacutes la de α2 (α2-jcc) etc

para cada alternativa seleccionada se comprueba si la pieza por adelantado coincide con alguno de los siacutembolos iniciales de la parte derecha y si hay coincidencia se prosigue el anaacutelisis con ella si no hay coincidencia se pasa a la alternativa siguiente

siguiendo con este orden si se llega al final de las alternativas sin haberse encontrado coincidencia alguna se produce un error sintaacutectico

En el analizador generado por JavaCC hay una peculiaridad que no se tiene en los analizadores LL(1) es-trictamente considerados la palabra vaciacutea siacute se considera como posible siacutembolo inicial de la parte derecha una regla (esto ocurre siempre que la parte derecha es anulable) De esta peculiaridad se deriva una conse-cuencia que ha de tenerse en cuenta al escribir la especificacioacuten dado que siempre es posible considerar la palabra vaciacutea como la siguiente pieza por adelantado si se llega a examinar la alternativa correspondiente a una produccioacuten anulable el analizador generado siempre seleccionaraacute esa alternativa con independencia de la siguiente pieza que esteacute presente en la entrada que queda por analizar

Asiacute pues si alguna de las producciones que definen un siacutembolo no terminal es anulable es importante el or-den en que se escriben las alternativas en una especificacioacuten sintaacutectica JavaCC el funcionamiento del ana-lizador generado siacute depende de la colocacioacuten elegida para las reglas

Para ilustrar estas peculiaridades del analizador sintaacutectico generado por JavaCC a continuacioacuten se expone un ejemplo

∆ Ejemplo 9

Se quiere obtener un analizador leacutexico-sintaacutectico de textos que se ajustan al siguiente formato una se-cuencia de uno o maacutes nombres seguida del siacutembolo igual seguido de un nuacutemero entero si la secuencia tiene maacutes de un nombre han de estar separados bien por una coma en todos los casos o bien por el siacutem-bolo dos puntos en todos los casos esto es son correctas las secuencias

uno dos tres = 123exclusivo = 0Abajo Arriba = 57

y son incorrectas las secuenciasuno dos tres = 123

exclusivo inclusive = 0

fuera dentro = 56

Una gramaacutetica que define las secuencias con este formato es

ltListagt = nombre ltOtrosNombresgt = numero

ltOtrosNombresgt = nombre nombre | nombre nombre | ε

se trata de una gramaacutetica que cumple la condicioacuten LL(1)- el siacutembolo director para la alternativa vaciacutea es el siacutembolo igual es evidente pues que las tres reglas de ltOtrosNombresgt tienen siacutembolos directores distintos

37

- la decisioacuten de abandonar la repeticioacuten del anaacutelisis de un nuevo nombre (en las dos primeras alterna-tivas de ltOtrosNombresgt) se puede tomar ante la presencia del siacutembolo igual

En la implementacioacuten de un analizador sintaacutectico LL(1) en sentido estricto la seleccioacuten de la alternativa vaciacutea para expandir el siacutembolo ltOtrosNombresgt se hariacutea tras comprobar expliacutecitamente que la pie-za sintaacutectica por adelantado es el siacutembolo igual

El funcionamiento del analizador generado por JavaCC depende del orden de colocacioacuten de las reglas A continuacioacuten se exponen varias posibilidades de especificacioacuten y se comenta el analizador generado en cada caso

bull Especificacioacuten primeraSe transcriben literalmente las reglas tal y como se han escrito antes en la notacioacuten BNF noacutetese que para poder codificar la parte derecha de la regla vaciacutea ha de incluirse una accioacuten sintaacutectica vaciacutea

PARSER_BEGIN (Igualdad)

public class Igualdad

public static void main (String[] argum) throws ParseException Igualdad analisis = new Igualdad(Systemin) analisissecuencia() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (Igualdad)

void secuencia() lista() ltEOFgt

void lista() lt nombre gt otrosNombres() = lt numero gt

void otrosNombres() lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt ) |

TOKEN lt nombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN lt numero ( [ 0 - 9 ] )+ gt

SKIP | r | n

38

El analizador generado funciona correctamente puesto que en el meacutetodo otrosNombres se selec-ciona la alternativa vaciacutea despueacutes de haber comprobado que la pieza por adelantado no es siacutembolo ini-cial de ninguna de las dos alternativas precedentes (no es una coma ni un siacutembolo dos puntos)

bull Especificacioacuten segundaSe considera la misma especificacioacuten anterior pero cambiando el orden de colocacioacuten de las reglas ahora la produccioacuten vaciacutea se coloca como primera alternativa (soacutelo se muestran las partes modifica-das)

void otrosNombres() | lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt )

Con la entrada de esta especificacioacuten el generador JavaCC produce un mensaje en el que se avisa de que las dos alternativas que estaacuten colocadas detraacutes de la alternativa vaciacutea nunca podraacuten seleccionarse aun asiacute se genera un analizador Pero es un analizador que funciona incorrectamente asiacute por ejemplo la entrada

mxa nyz = 57

se considera incorrecta despueacutes de visto el primer nombre se intenta aplicar la produccioacuten vaciacutea (se espera encontrar el siacutembolo igual) pero habriacutea que aplicar la regla cuyo siacutembolo director es la coma

bull Especificacioacuten terceraSe utilizan las posibilidades de la notacioacuten BNF-Ampliada para prescindir de la especificacioacuten expliacuteci-ta de la palabra vaciacutea se aprovecha que JavaCC admite el operador + para indicar repeticiones de una o maacutes veces la alternativa vaciacutea estaacute impliacutecitamente contemplada en la posibilidad de cero repeti-ciones que contempla el operador (soacutelo se muestran las partes modificadas en la especificacioacuten)

void otrosNombres() ( lt nombre gt )+ | ( lt nombre gt )

El meacutetodo otrosNombres() generado hace las comprobaciones en el siguiente orden

- se comprueba si la pieza por adelantado es una coma (siacutembolo inicial de la primera alternativa) y en este caso se selecciona la primera regla- si no es una coma se comprueba si es un siacutembolo dos puntos (siacutembolo inicial de la segunda alter-nativa) y en este caso se selecciona la segunda regla- si no es un siacutembolo dos puntos se considera la palabra vaciacutea como siacutembolo inicial (siempre posi-ble impliacutecitamente) para la segunda alternativa en este caso para que no haya error la pieza por adelantado ha de ser un siacutembolo igual

Se trata pues de un analizador que funciona correctamente

bull Especificacioacuten cuarta

39

Se pone la misma especificacioacuten anterior pero cambiando el orden de consideracioacuten de la regla vaciacutea ahora se incluye impliacutecitamente en la primera alternativa (soacutelo se muestran las partes modificadas)

void otrosNombres() ( lt nombre gt ) | ( lt nombre gt )+

Para esta especificacioacuten el generador JavaCC emite un aviso en el que se indica que la segunda alter -nativa nunca se seleccionaraacute no obstante se genera un analizador que no funciona como se supone que deberiacutea hacerlo por ejemplo el anaacutelisis de la entrada

nyz mxa = 1

produce un error despueacutes de tratado el primer nombre se intenta aplicar la produccioacuten vaciacutea (corres-pondiente a la repeticioacuten de cero veces contemplada en la primera regla) por lo que no se encuentra el esperado siacutembolo igual

40

Page 18: JavaCC

- cuando se analiza un teacutermino en el que hay operadores multiplicativos se cuenta su nuacutemero y se graba el resultado (noacutetese que se cuentan los operadores de cada teacutermino pero no se acumula la cuenta de todos los operadores de la expresioacuten)

Para la realizacioacuten de estas tareas se antildeaden bloques de coacutedigo Java En la especificacioacuten que se describe a continuacioacuten puede apreciarse- para los siacutembolos no terminales expresion termino y factor se ha incluido en los blo-ques correspondientes la operacioacuten de grabacioacuten de su nombre que se ejecutaraacute cada vez que se efec-tuacutee una llamada al meacutetodo- en el bloque asociado a termino se tiene la declaracioacuten del contador de operadores (que seraacute una variable local del meacutetodo)- en la parte derecha de la regla que define la estructura de un teacutermino se han incluido dos acciones sintaacutecticas una para contar la cantidad de operadores (situada detraacutes de la pieza sintaacutectica del opera-dor) y otra para grabar la cantidad de operadores del teacutermino (situada al final de la parte derecha)- se ha definido el meacutetodo grabar para facilitar la escritura del coacutedigo Java

options Ignore_Case = true

PARSER_BEGIN (ExprMin) public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

private static void grabar(String nombre) Systemoutprintln ( -gt + nombre + n)

PARSER_END (ExprMin)

void unaExpresion()

expresion() ltEOFgt

void expresion() grabar(Expresion)

termino() ( + termino() )

void termino() int nAst = 0 grabar(Termino)

17

factor() ( nAst++ factor() ) if (nAst gt 0) Systemoutprintln (n Asteriscos + nAst + n)

void factor() grabar(Factor)

ltconstantegt | ltvariablegt | ( expresion() )

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP | t | n | r

Cuando se incluyen acciones sintaacutecticas la legibilidad del aspecto sintaacutectico de la especificacioacuten queda oscurecida para paliar ese inconveniente puede antildeadirse un comentario que muestre la regla sintaacutectica escueta sin las acciones por ejemplo podriacutea escribirse

void termino() int nAst = 0 grabar(Termino)

factor() ( factor() )

factor()

( nAst++ factor() )

if (nAst gt 0) Systemoutprintln (n Asteriscos + nAst + n)

loz Valor comunicado por un meacutetodoEn todo lo visto hasta ahora los meacutetodos de anaacutelisis sintaacutectico generados por JavaCC no devuelven valor alguno al terminar su tarea de anaacutelisis ya que en la especificacioacuten siempre se ha declarado el nombre de un meacutetodo precedido de void (son meacutetodos de tipo void) Pero en general un meacutetodo de anaacutelisis sintaacutec-tico puede ser de cualquier tipo (primitivo o no) por ello un meacutetodo puede devolver un valor de cualquier tipo (de cualquier clase) al meacutetodo desde el que se efectuoacute la llamada

Esta posibilidad de devolucioacuten de valores tiene una utilidad fundamental en la implementacioacuten de analiza-dores de lenguajes permite la comunicacioacuten de datos (atributos de las entidades analizadas) entre los dife -rentes meacutetodos que colaboran en el anaacutelisis del programa completo

En una especificacioacuten JavaCC la llamada a un meacutetodo ocurre cuando en la parte derecha de una regla estaacute el nombre del siacutembolo no terminal asociado al meacutetodo Cuando un meacutetodo devuelve un valor (tiene un tipo distinto de void) es habitual la recepcioacuten del valor devuelto en una variable en la propia especificacioacuten

18

se puede indicar el nombre de la variable a la que se asignaraacute el valor devuelto tras la llamada para ello se emplea una notacioacuten ideacutentica a la forma que tienen las sentencias de asignacioacuten en el lenguaje Java

dato = nombreSimb()

donde dato es el nombre de la variable a la que se asigna el valor devuelto por el meacutetodo llamado esa va -riable ha de estar oportunamente declarada del mismo tipo que dicho meacutetodo

∆ Ejemplo 2

En el mismo ejemplo de presentacioacuten se desea ademaacutes del anaacutelisis leacutexico-sintaacutectico de la entrada contar la cantidad de operadores que hay en la expresioacuten A continuacioacuten se expone una especificacioacuten JavaCC que es una posible solucioacuten soacutelo se muestran las reglas sintaacutecticas (el resto de la especificacioacuten es igual que en el ejemplo de presentacioacuten)

En la especificacioacuten que se propone puede apreciarse- los siacutembolos no terminales expresion termino y factor se han declarado de tipo int los meacutetodos correspondientes devuelven un valor de tipo entero- el valor devuelto por los meacutetodos sirve para propagar a traveacutes de la estructura sintaacutectica la cuenta de los operadores que hay en el texto analizado- por tratarse de meacutetodos que devuelven un valor es preciso incluir las correspondientes sentencias de devolucioacuten return

void unaExpresion() int nOper nOper = expresion() ltEOFgt Systemoutprintln (Cantidad de operadores = + nOper)

int expresion() int n i n = termino() ( + n++ i = termino() n = n + i )

return n

int termino() int n i n = factor() ( n++ i = factor() n = n + i )

return n

int factor() int n ltconstantegt return 0 | ltvariablegt return 0 | ( n = expresion() ) return n

La especificacioacuten de un factor tambieacuten podriacutea escribirse de esta otra manera

19

int factor() int n = 0

( ltconstantegt | ltvariablegt | ( n = expresion() ) )

return n

A continuacioacuten se expone un nuevo ejemplo para ilustrar la utilidad que proporciona el valor devuelto por un meacutetodo de anaacutelisis sintaacutectico para la propagacioacuten (ascendente) de atributos (propiedades de las entida-des analizadas)

∆ Ejemplo 3

Se pretende ampliar el analizador del ejemplo de presentacioacuten para que se emita un mensaje indicativo cuando la expresioacuten analizada sea una expresioacuten en la que soacutelo aparecen constantes en la solucioacuten que se muestra soacutelo se incluyen las reglas sintaacutecticas (en el resto de la especificacioacuten no hay modificacio-nes)

void unaExpresion() boolean esCte

expresion() ltEOFgt

esCte = expresion() ltEOFgt

if (esCte) Systemoutprintln(nExpresion constanten)

boolean expresion() boolean esCte esTambienCte

termino() ( + termino() )

esCte = termino() ( + esTambienCte = termino() esCte = esCte ampamp esTambienCte )

return esCte

boolean termino() boolean esCte esTambienCte

factor() ( factor() )

esCte = factor() ( esTambienCte = factor() esCte = esCte ampamp esTambienCte )

return esCte

20

boolean factor() boolean esCte

ltconstantegt return true | ltvariablegt return false | ( esCte = expresion() ) return esCte

bull Reconsideracioacuten sobre las producciones vaciacuteasAnteriormente se ha comentado que para representar una produccioacuten vaciacutea en una especificacioacuten JavaCC hay que hacer uso de los metasiacutembolos de opcionalidad para ilustrar esta idea se ha incluido como ejem-plo la especificacioacuten de una lista de paraacutemetros compuesta por cero o maacutes expresiones separadas entre siacute por el caraacutecter coma

void parametros() [ expresion() ( expresion() ) ]

Pero si se aprovecha la posibilidad de incorporar acciones sintaacutecticas se tiene una manera de especificar directamente producciones vaciacuteas para conseguirlo basta considerar una produccioacuten vaciacutea (no tiene nada en su parte derecha) acompantildeada de una accioacuten sintaacutectica cuyo bloque de coacutedigo esteacute vaciacuteo Seguacuten esta posibilidad la especificacioacuten anterior podriacutea tambieacuten escribirse asiacute

void parametros() expresion() ( expresion() ) |

En general la accioacuten sintaacutectica asociada a una produccioacuten vaciacutea no tiene por queacute ser un bloque vaciacuteo de coacutedigo por ejemplo si se quiere que el meacutetodo que analiza los paraacutemetros de una llamada devuelva la cantidad de paraacutemetros encontrados se podriacutea escribir la siguiente especificacioacuten

int parametros()

int cantidad

( expresion() cantidad = 1

( expresion() cantidad++ )

| cantidad = 0 )

return cantidad

y si no quisiera ponerse expliacutecitamente la alternativa de la produccioacuten vaciacutea podriacutea escribirse

int parametros() int cantidad = 0

21

[ expresion() cantidad++ ( expresion() cantidad++ ) ]

return cantidad

Comunicacioacuten entre los analizadores leacutexico y sintaacutecticoCada vez que el analizador sintaacutectico realiza una llamada al analizador lexicograacutefico recibe como resultado de la llamada una representacioacuten de la pieza sintaacutectica encontrada en el texto analizado En los analizadores generados por JavaCC la comunicacioacuten de la pieza se efectuacutea mediante un valor de clase Token Esta cla-se estaacute definida en el fichero Tokenjava que se obtiene en todo caso siempre con el mismo contenido cualquiera que sea la especificacioacuten proporcionada como entrada al generador

Entre los campos de la clase Token se encuentran los siguientes

public int kind

nuacutemero entero que sirve de representacioacuten interna de la pieza sintaacutectica estos nuacutemeros asociados a las piezas son asignados automaacuteticamente por JavaCC los valores considerados pueden consultar-se en el fichero generado de nombre Constantsjava

public String image

cadena que contiene de la secuencia de caracteres (lexema) que constituyen la pieza sintaacutectica co-municada

public int beginLine beginColumn endLine endColumn

posiciones ocupadas (nuacutemeros de liacutenea y columna en el fichero de entrada) por el comienzo y el fi-nal del lexema de la pieza sintaacutectica comunicada

La clase Token tiene otros campos y meacutetodos en el fichero Tokenjava se tienen comentarios des-criptivos sobre todos los campos y meacutetodos que forman parte de la clase todos los campos de la clase se declaran como public

Ya se ha comentado anteriormente que cuando un meacutetodo de anaacutelisis sintaacutectico (asociado a un siacutembolo no terminal) tiene tipo el valor devuelto tras una llamada se puede almacenar en una variable por si se desea utilizar en alguna accioacuten sintaacutectica para ello se escribe

valor = nombreSimbolo()

(siendo valor el nombre de la variable y nombreSimbolo el nombre del siacutembolo no terminal) De manera anaacuteloga el objeto de la clase Token representativo de la pieza sintaacutectica encontrada en la entrada por el analizador lexicograacutefico puede asignarse a una variable del mismo tipo para tener accesibles las ca -racteriacutesticas de la pieza por si se precisaran en alguna accioacuten sintaacutectica para ello en la parte de la especifi -cacioacuten sintaacutectica donde aparezca la pieza sintaacutectica se escribe

dato = lt nombrePieza gt

donde nombrePieza es el nombre de la pieza sintaacutectica y dato es el nombre de una variable de tipo Token en la que se anota el valor indicativo de las propiedades de la pieza sintaacutectica comunicada

Tambieacuten existe otra manera de acceder a las caracteriacutesticas de la pieza sintaacutectica que el analizador lexico -graacutefico comunica al analizador sintaacutectico en la clase del analizador sintaacutectico se tiene declarado el campo

[ static ] public Token token

22

cada vez que el analizador sintaacutectico recibe una pieza sintaacutectica comunicada por el analizador lexicograacutefico se deja anotada en el campo token y ese contenido no se altera hasta que sea proporcionada la siguiente pieza de la entrada

∆ Ejemplo 4

Se considera una ampliacioacuten del ejemplo de presentacioacuten ademaacutes del anaacutelisis de la entrada se pretende obtener una relacioacuten numerada de las variables y las constantes indicando el nuacutemero de liacutenea y el nuacuteme-ro de columna donde estaacuten situadas por ejemplo si en la entrada se tiene la expresioacuten (grabada en dos liacuteneas)

x (y + 11) + 22

en la salida se tendraacute

1- x linea 1 columna 1

2- y linea 1 columna 6

3- 11 linea 1 columna 10

4- 22 linea 2 columna 5

En la especificacioacuten que se propone como solucioacuten puede apreciarse- en la clase del analizador sintaacutectico se han incluido meacutetodos auxiliares en los que se usan los nom-bres image beginLine beginColumn que son campos de un objeto de clase Token

- en la especificacioacuten sintaacutectica de factor se emplea la variable pieza de tipo Token para reco-ger las caracteriacutesticas de las piezas sintaacutecticas detectadas- la variable numero hace de contador para numerar las constantes y variables

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin

static int numero = 0

private static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

private static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

private static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

23

PARSER_END (ExprMin)

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() Token pieza pieza = ltconstantegt numero++ grabarDatosPieza(numero pieza) | pieza = ltvariablegt numero++ grabarDatosPieza(numero pieza) | ( expresion() )

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP | n | t | r

Si se agrupan las dos acciones sintaacutecticas comunes la especificacioacuten de factor tambieacuten puede escri-birse de esta forma

void factor() Token pieza ( pieza = ltconstantegt | pieza = ltvariablegt

) numero++ grabarDatosPieza(numero pieza)

| ( expresion() )

24

Si se accede al campo token de la clase del analizador sintaacutectico se puede resolver este ejemplo pres-cindiendo de la variable pieza

void factor() ( ltconstantegt | ltvariablegt

) numero++ grabarDatosPieza(numero token)

| ( expresion() )

Tareas asociadas a las piezas sintaacutecticasYa se ha visto que es posible asociar bloques de coacutedigo Java a puntos de la estructura sintaacutectica para que se realicen ciertas operaciones cuando el analizador sintaacutectico pase por esa zona de la estructura Tambieacuten es posible asociar coacutedigo Java a las piezas sintaacutecticas para que el analizador lexicograacutefico efectuacutee determina-das tareas cuando detecte en la entrada analizada piezas que tengan asociado coacutedigo

loz Accioacuten ligada a una pieza sintaacutecticaSi se asocia un bloque de coacutedigo a una pieza sintaacutectica se consigue que cada vez que se detecta en la en-trada analizada la presencia de esa pieza se ejecute en ese preciso momento el coacutedigo del bloque asociado a la pieza Estas acciones se denominan ldquoacciones lexicograacuteficasrdquo (lexical actions) el coacutedigo del bloque se traslada literalmente al coacutedigo del analizador lexicograacutefico generado para que se ejecute oportunamente cuando se detecte la pieza ligada al bloque de coacutedigo La accioacuten lexicograacutefica se ejecuta inmediatamente antes que la pieza sea comunicada al analizador sintaacutectico

Se puede asociar una accioacuten lexicograacutefica a una pieza sintaacutectica declarada con TOKEN y tambieacuten a una se-cuencia de caracteres declarada con SKIP ademaacutes de eacutestas hay otras dos formas de declaraciones lexico-graacuteficas (no mencionadas en esta introduccioacuten) que tambieacuten pueden llevar una accioacuten asociada

En una especificacioacuten JavaCC la forma de una pieza sintaacutectica (o bien de una secuencia de caracteres) se define poniendo su descripcioacuten delimitada entre llaves detraacutes de la indicacioacuten TOKEN (o bien SKIP) si se quiere asociar una accioacuten lexicograacutefica a la pieza (o a la secuencia) hay que colocar el bloque de coacutedigo de la accioacuten a continuacioacuten de la descripcioacuten de la forma de la pieza (o de la secuencia) el bloque (delimi-tado por llaves) queda situado entre la descripcioacuten y la llave que cierra la especificacioacuten

La pieza sintaacutectica especial que representa el final del fichero (EOF) tambieacuten puede llevar asociada una ac-cioacuten lexicograacutefica para ello hay que poner

ltgt TOKEN lt EOF gt coacutedigo de la accioacuten asociada

para justificar la presencia de los caracteres ltgt delante de la palabra TOKEN hay que basarse en el con-cepto de ldquoestado lexicograacuteficordquo (cuya explicacioacuten queda fuera del alcance de esta introduccioacuten)

∆ Ejemplo 5

Se considera nuevamente el ejemplo de presentacioacuten como resultado del anaacutelisis de una expresioacuten se pretende obtener una relacioacuten de los pareacutentesis contenidos en la expresioacuten en el mismo orden en que se encuentran ademaacutes por cada salto de liacutenea del texto de entrada se grabaraacute en la salida el caraacutecter y

25

se pasaraacute a una nueva liacutenea tambieacuten se anotaraacute la frase Final del trabajo cuando se alcance el final del fichero de entrada Asiacute por ejemplo si la entrada analizada es

(a + b) (( x )) (y + 7)

los resultados obtenidos seraacuten

() (())()

Final del trabajo

En la especificacioacuten JavaCC que se propone como solucioacuten soacutelo se muestran las reglas sintaacutecticas y le -xicograacuteficas (en el resto no hay cambios) en esta solucioacuten puede apreciarse- las piezas sintaacutecticas correspondientes a los pareacutentesis se han definido de manera nominal para po-der asociarles una accioacuten lexicograacutefica- los caracteres incluidos en la pieza de tipo SKIP se han descompuesto en dos grupos ya que la ac-cioacuten lexicograacutefica incorporada soacutelo afecta a uno de ellos

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ltpAbrirgt expresion() ltpCerrargt

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

TOKEN

26

lt pAbrir ( gt Systemoutprint(()

TOKEN lt pCerrar ) gt Systemoutprint())

ltgtTOKEN lt EOF gt Systemoutprintln ( Final del trabajo )

SKIP n Systemoutprint( n)

SKIP | t | r

loz Bloque de declaraciones lexicograacuteficasCada definicioacuten de una pieza sintaacutectica puede llevar asociada una accioacuten lexicograacutefica que se ejecuta in-mediatamente despueacutes de detectar en la entrada un lexema que se ajusta a la definicioacuten de la pieza cada accioacuten lexicograacutefica es un bloque de coacutedigo Java que puede contener sus propias declaraciones Pero a ve-ces interesa tener declaraciones que sean compartidas por todas las acciones lexicograacuteficas con indepen-dencia de la pieza a la que esteacute asociada cada una de ellas

Para incluir declaraciones que puedan usarse desde el coacutedigo asociado a cualquiera de las piezas sintaacutecti -cas se dispone de una construccioacuten con el siguiente formato

TOKEN_MGR_DECLS

que consta de una palabra reservada seguida del siacutembolo dos puntos seguido de un bloque de coacutedigo Java (delimitado por llaves) esta declaracioacuten se puede colocar en cualquier parte de una especificacioacuten JavaCC si se incluye soacutelo puede ponerse una vez

Todas las declaraciones de variables o de meacutetodos puestas en el bloque de declaraciones lexicograacuteficas compartidas son accesibles desde el coacutedigo de cualquier accioacuten lexicograacutefica la justificacioacuten es sencilla el bloque de declaraciones se traslada al principio del coacutedigo del analizador lexicograacutefico generado y las ac-ciones lexicograacuteficas tambieacuten se trasladan a ese analizador

loz Declaraciones lexicograacuteficas predefinidasDe lo expuesto hasta ahora se aprecia que en una accioacuten lexicograacutefica asociada a una pieza sintaacutectica re-sultan accesibles

- las declaraciones de su propio bloque (si tiene declaraciones)- los componentes del bloque de declaraciones compartidas (si estaacute incluido en la especificacioacuten)

Ademaacutes de esto se tienen otras declaraciones pertenecientes a la clase TokenManager que constituye el analizador lexicograacutefico que tambieacuten son utilizables desde cualquiera de las acciones lexico-graacuteficas estas declaraciones pueden considerarse como ldquopredefinidasrdquo ya que son incorporadas automaacuteti-camente por el generador Entre estas declaraciones accesibles se encuentran las siguientes

[ static ] StringBuffer imagecontiene la cadena de caracteres representativa de la pieza sintaacutectica actual que se acaba de recono-cer en le entrada se trata de un campo de la clase y se declara como static cuando el analizador

27

se ha generado con la opcioacuten (por defecto) Static = true si se generase con la opcioacuten Sta-tic = false no quedariacutea declarada como static

Token matchedToken

variable de clase Token que representa la pieza sintaacutectica actual la que se acaba de encontrar en la entrada se trata de una variable local del meacutetodo al que se ha trasladado el coacutedigo de la accioacuten (y por ello accesible desde ese coacutedigo)

El campo image puede usarse tanto si la accioacuten lexicograacutefica estaacute asociada a una pieza declarada con TOKEN como si lo estaacute a una secuencia declarada con SKIP Sin embargo la variable matchedToken no puede usarse en una accioacuten asociada a un patroacuten especificado como SKIP el motivo es que lo declara-do como SKIP no constituye propiamente una pieza sintaacutectica ya que no se va a comunicar al analizador sintaacutectico

∆ Ejemplo 6

Se propone aquiacute una nueva solucioacuten para el ejemplo nordm 4 expuesto anteriormente

Esta nueva solucioacuten tal y como se expone aquiacute admite algunas mejoras ya que su pretensioacuten soacutelo es ser-vir de ilustracioacuten para algunos aspectos que se acaban de citar (maacutes adelante se propone otra solucioacuten al-ternativa)

En la especificacioacuten de la solucioacuten es posible apreciar esto- las acciones lexicograacuteficas pueden llevar sus propias declaraciones las variables nLineaVar nColumnaVar nLineaCte y nColumnaCte se declaran dentro de las acciones

- la variable matchedToken a la que se hace referencia en el coacutedigo de la accioacuten lexicograacutefica aso-ciada a una variable contiene la informacioacuten sobre la pieza sintaacutectica que se acaba de detectar una va-riable de la expresioacuten analizada anaacutelogamente con la referencia relativa a una constante- el nombre image que aparece en las acciones lexicograacuteficas se refiere al campo de la clase Expr-MinTokenManager (analizador lexicograacutefico) noacutetese que en efecto se comunica como un paraacuteme-tro de clase StringBuffer

- la variable numero se emplea para numerar las constantes y variables encontradas

- los nombres beginLine y beginColumn son campos (puacuteblicos) de la clase Token por ello se pueden aplicar a un objeto de esa clase

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

void unaExpresion()

28

expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n StringBuffer lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

TOKEN lt variable [a-z] gt

int nLineaVar nColumnaVar numero++ nLineaVar = matchedTokenbeginLine nColumnaVar = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaVar nColumnaVar)

TOKEN lt constante ( [0-9] ) + gt

int nLineaCte nColumnaCte

29

numero++ nLineaCte = matchedTokenbeginLine nColumnaCte = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaCte nColumnaCte)

SKIP | n | t | r

La especificacioacuten precedente se puede escribir de otra manera maacutes escueta como se indica a continua-cioacuten (soacutelo se muestran las modificaciones) sobre esta nueva especificacioacuten puede mencionarse- se han eliminado por innecesarias las variables declaradas dentro de las acciones lexicograacuteficas- el nombre image que aparece en el bloque de las declaraciones compartidas se refiere al campo de la clase Token noacutetese que en efecto se aplica a un objeto de esa clase y se comunica como un paraacute -metro de tipo String

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

TOKEN lt variable [a-z] gt

numero++ grabarDatosPieza(numero matchedToken)

TOKEN lt constante ( [0-9] ) + gt

numero++ grabarDatosPieza(numero matchedToken)

loz Accioacuten lexicograacutefica comuacutenSi hubiera que realizar la misma tarea tras la deteccioacuten de todas y cada una de las piezas sintaacutecticas una posibilidad incoacutemoda seriacutea repetir el mismo coacutedigo en todas las acciones lexicograacuteficas para evitar tanta reiteracioacuten se tiene la opcioacuten Common_Token_Action su valor por defecto es false pero si se le asigna el valor true es posible definir una ldquoaccioacuten lexicograacutefica comuacutenrdquo que se escribe una uacutenica vez y se aplica a todas las piezas sintaacutecticas

30

Cuando la opcioacuten Common_Token_Action se establece con el valor true es obligado incluir el blo-que de declaraciones lexicograacuteficas compartidas (TOKEN_MGR_DECLS) Ademaacutes en ese bloque se preci-sa la definicioacuten de un meacutetodo cuyo encabezamiento sea

[ static ] void CommonTokenAction(Token t)

el coacutedigo de este meacutetodo constituye la accioacuten lexicograacutefica comuacuten que se ejecutaraacute despueacutes de la deteccioacuten de cualquier pieza sintaacutectica el meacutetodo tiene un paraacutemetro de tipo Token mediante el que se puede co-municar la informacioacuten correspondiente a la pieza que se acaba de encontrar en la entrada analizada

Si una pieza sintaacutectica tiene asociada una accioacuten lexicograacutefica propia la accioacuten lexicograacutefica comuacuten (si es-taacute definida) se ejecutaraacute inmediatamente despueacutes de realizar la accioacuten propia

La accioacuten lexicograacutefica comuacuten se aplica a todas las piezas sintaacutecticas definidas como TOKEN bien sea no-minalmente (en las reglas lexicograacuteficas) o bien anoacutenimamente (incorporadas dentro de las reglas sintaacutecti-cas) sin embargo no se aplica cuando la secuencia acoplada se corresponde con una expresioacuten regular es-pecificada como SKIP (noacutetese que por el contrario una especificacioacuten SKIP siacute puede tener asociada una accioacuten lexicograacutefica propia)

El coacutedigo del meacutetodo CommonTokenAction se traslada como un componente maacutes del bloque de decla-raciones compartidas (TOKEN_MGR_DECLS) al principio de la clase del analizador lexicograacutefico

∆ Ejemplo 7

Se pretende ahora ampliar el ejemplo de presentacioacuten con la obtencioacuten de una relacioacuten numerada de los lexemas de las piezas sintaacutecticas encontradas en la entrada analizada en el caso de las variables y de las constantes antes del lexema se mostraraacute un indicativo (Var Const) por ejemplo si la expresioacuten de la entrada es

( a + 987 ) b

se grabaraacute la salida

( 1Var a 2+ 3Const 987 4) 5 6Var b 7 8

En la solucioacuten que se propone a continuacioacuten se muestran la definicioacuten de las opciones y las reglas sin-taacutecticas y lexicograacuteficas (en el resto no hay modificaciones) puede resaltarse lo siguiente- las llamadas al meacutetodo CommonTokenAction se incorporan automaacuteticamente no hay que incluir-las en la especificacioacuten como paraacutemetro de llamada a este meacutetodo siempre se emplea el objeto que re -presenta la pieza que se acaba de detectar- la accioacuten lexicograacutefica comuacuten realiza la actualizacioacuten del contador de piezas y la grabacioacuten del lexe-ma de la pieza detectada con su nuacutemero de orden- la accioacuten lexicograacutefica comuacuten tambieacuten se aplica a las piezas sintaacutecticas anoacutenimas (pareacutentesis y ope-radores) y a la pieza especial que representa el final del fichero (EOF)

- las piezas sintaacutecticas ltconstantegt y ltvariablegt tienen asociada una accioacuten lexicograacutefica propia que se ejecuta antes de la accioacuten lexicograacutefica comuacuten- los caracteres que se saltan (SKIP) no estaacuten afectados por la accioacuten lexicograacutefica comuacuten

options Ignore_Case = true Common_Token_Action = true

31

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarIndicativo(String indicativo) Systemoutprint(indicativo + )

static void grabarLexema (int num String lexema) Systemoutprint(lexema + + num + n)

static void CommonTokenAction(Token pieza) numero++ grabarLexema(numero piezaimage)

TOKEN lt variable [a-z] gt

grabarIndicativo(Var)

32

TOKEN lt constante ( [0-9] ) + gt

grabarIndicativo(Const)

SKIP | n | t | r

A modo de ejemplo de aplicacioacuten de la accioacuten lexicograacutefica comuacuten en lo que sigue se ofrece una solucioacuten para la grabacioacuten (en el fichero predefinido de salida) del texto analizado con las liacuteneas numeradas

∆ Ejemplo 8 [ Listado del texto analizado ]Se desea completar el ejemplo de presentacioacuten con la grabacioacuten de un listado con las liacuteneas numeradas del texto analizado (noacutetese que una expresioacuten puede escribirse ocupando varias liacuteneas) No se muestra la especificacioacuten sintaacutectica debido a que no hay ninguna modificacioacuten en ella Los aspectos resentildeables de la solucioacuten que se propone son- se usa la variable numLin como contador de liacuteneas

- en la variable linea se van yuxtaponiendo los lexemas de las piezas sintaacutecticas de una liacutenea esta yuxtaposicioacuten se realiza mediante la accioacuten lexicograacutefica comuacuten- las secuencias reconocidas con los patrones especificados como SKIP no implican la ejecucioacuten de la accioacuten lexicograacutefica comuacuten por ello su lexema (almacenado en la variable image) ha de yuxtaponer-se expliacutecitamente- hay que separar el patroacuten correspondiente al fin de liacutenea para detectar el momento en que ha de gra-barse la liacutenea completa cuyo final se ha alcanzado

options Ignore_Case = true Common_Token_Action = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(nnAnalisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

TOKEN_MGR_DECLS static int numLin = 0 static String linea =

33

static void CommonTokenAction(Token pieza) linea = linea + piezaimage

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP lt | t | r gt linea = linea + image

SKIP n numLin++ Systemoutprintln(numLin + + linea) linea =

A modo de recapitulacioacutenↄ Accioacuten lexicograacutefica Accioacuten sintaacutectica

bull accioacuten lexicograacutefica bloque de coacutedigo Java asociado a una pieza sintaacutectica nominal (TOKEN) o a una secuencia de caracteres (SKIP) se ejecuta cuando se detecta en la entrada la pieza o la secuencia corres-pondiente no se puede aplicar a piezas sintaacutecticas anoacutenimas (autodefinidas con comillas)bull accioacuten sintaacutectica bloque de coacutedigo Java asociado a un punto de la estructura sintaacutectica (el punto indica-do por el sitio de la produccioacuten donde se inserta el bloque) se ejecuta cuando el anaacutelisis de la entrada pa-sa por ese punto de la estructura

ↄ Valor de una pieza sintaacutectica comunicadaEl analizador lexicograacutefico comunica al analizador sintaacutectico una pieza a traveacutes de un valor de la clase Token (fichero Tokenjava) un objeto de la clase Token representa las caracteriacutesticas de la pieza comunicada En la clase Token entre otros se tienen los campos

String imagelexema de la pieza

int beginLine beginColumn endLine endColumnposicioacuten del lexema en el fichero analizado (indicada por los nuacutemeros de fila y de columna de su comienzo y de su terminacioacuten)

ↄ Valor asociado a un siacutembolo

34

En las producciones que especifican la sintaxis se pueden incluir asignaciones que sirven para dejar ano-tado el valor asociado a un siacutembolo

bull siacutembolo no terminal valor = nombreSimbolo()el meacutetodo de anaacutelisis asociado a nombreSimbolo se ha declarado de un cierto tipo (distinto del tipo void) valor ha de ser una variable del mismo tipo que el meacutetodo

bull siacutembolo terminal dato = lt nombrePieza gtel valor asignado es el valor de la clase Token representativo de la pieza sintaacutectica (nominal) dato ha de ser una variable de tipo Token

ↄ Miradas sobre la pieza sintaacutectica comunicadaLa pieza sintaacutectica comunicada puede mirarse desde cada uno de los dos analizadores

bull analizador sintaacutecticola pieza comunicada (desde el analizador lexicograacutefico) se tiene anotada en un campo de la clase del analizador sintaacutectico declarado como

(static) public Token token

este valor es accesible desde el coacutedigo de todos los meacutetodos de anaacutelisis sintaacutectico el contenido del campo variacutea cuando se recibe una nueva pieza sintaacutectica

bull analizador lexicograacuteficola pieza dispuesta para ser comunicada (al analizador sintaacutectico) se tiene anotada en una variable local declarada como

Token matchedToken

este valor es accesible para el coacutedigo de todas las acciones lexicograacuteficas

ↄ El lexema de la pieza comunicadaEl lexema de la pieza sintaacutectica comunicada se encuentra disponible en dos sitios distintos que compar-ten el mismo nombre pero que son de distinto tipo y se usan de distinta manera

bull String image

es un campo de la clase TOKEN puede consultarse en relacioacuten con un objeto de esa clase que esteacute disponible

bull StringBuffer image

es un campo de la clase del analizador lexicograacutefico ( ∙ ∙ ∙ ∙ TokenManagerjava) es accesible desde el coacutedigo de la clase y por lo tanto desde todas las acciones lexicograacuteficas (bloques de coacute-digo que estaacuten incorporados al analizador lexicograacutefico)

se trata de un valor disponible tanto para las piezas sintaacutecticas nominales (TOKEN) como para las secuencias que se saltan (SKIP)

ↄ Resumen relativo a la lexicografiacutea

TOKEN pieza sintaacutectica nominal∙ ∙ ∙ pieza sintaacutectica anoacutenimaSKIP secuencia de caracteres que no forman pieza

35

Precisiones sobre el analizador sintaacutectico generadoPuede decirse que en su funcionamiento maacutes sencillo cuando no se emplean las posibilidades de examen por adelantado (ldquolookaheadrdquo) JavaCC comprueba que las reglas sintaacutecticas cumplen la condicioacuten LL(1) y generan un analizador descendente-predictivo-recursivo Pero esta apreciacioacuten hay que matizarla ya que el analizador sintaacutectico generado no se ajusta en sentido estricto al modelo de analizador LL(1)

En el modelo de anaacutelisis LL(1) el orden del examen de las distintas alternativas de expansioacuten de un siacutembo-lo no terminal es indiferente ya que se conocen los siacutembolos directores de cada una de ellas (que resultan ser conjuntos disjuntos) en la implementacioacuten del subprograma de anaacutelisis asociado al siacutembolo no terminal ltNombreSimbgt cuyas producciones son

ltNombreSimbgt = α1

| α2

∙ ∙ ∙

| αn

se podriacutea preguntar en cualquier orden si la pieza actual pertenece al conjunto de siacutembolos directores de las distintas reglas Sin embargo en el analizador sintaacutectico generado por JavaCC la implementacioacuten del meacute-todo asociado al siacutembolo no terminal ltNombreSimbgt no sigue esa pauta

Si las anteriores reglas sintaacutecticas se transcriben en JavaCC de la forma

void nombreSimb () α1-jcc

| α2-jcc

∙ ∙ ∙

| αn-jcc

1

accioacutenlexicograacutefica

comuacuten

Commom -TokenAction2

accioacutenlexicograacutefica

propia

(accioacuten ligadaa una pieza)3

bloque dedeclaracioneslexicograacuteficas

TOKEN_MGR_DECLS4

declaracioneslexicograacuteficaspredefinidas

(disponibilidad

de uso)TOKENsisisisi∙ ∙ ∙sino--SKIPnosisisi

Las indicaciones si de las columnas 3 y 4 son consecuencia de las indicaciones de la columna 2

La accioacuten lexicograacutefica comuacuten se ejecuta despueacutes de la accioacuten lexicograacutefica propia

36

el meacutetodo de anaacutelisis generado procede de la siguiente manera

se van considerando las alternativas sucesivamente en el mismo orden en el que estaacuten especifica-das primero la de α1 (α1-jcc) despueacutes la de α2 (α2-jcc) etc

para cada alternativa seleccionada se comprueba si la pieza por adelantado coincide con alguno de los siacutembolos iniciales de la parte derecha y si hay coincidencia se prosigue el anaacutelisis con ella si no hay coincidencia se pasa a la alternativa siguiente

siguiendo con este orden si se llega al final de las alternativas sin haberse encontrado coincidencia alguna se produce un error sintaacutectico

En el analizador generado por JavaCC hay una peculiaridad que no se tiene en los analizadores LL(1) es-trictamente considerados la palabra vaciacutea siacute se considera como posible siacutembolo inicial de la parte derecha una regla (esto ocurre siempre que la parte derecha es anulable) De esta peculiaridad se deriva una conse-cuencia que ha de tenerse en cuenta al escribir la especificacioacuten dado que siempre es posible considerar la palabra vaciacutea como la siguiente pieza por adelantado si se llega a examinar la alternativa correspondiente a una produccioacuten anulable el analizador generado siempre seleccionaraacute esa alternativa con independencia de la siguiente pieza que esteacute presente en la entrada que queda por analizar

Asiacute pues si alguna de las producciones que definen un siacutembolo no terminal es anulable es importante el or-den en que se escriben las alternativas en una especificacioacuten sintaacutectica JavaCC el funcionamiento del ana-lizador generado siacute depende de la colocacioacuten elegida para las reglas

Para ilustrar estas peculiaridades del analizador sintaacutectico generado por JavaCC a continuacioacuten se expone un ejemplo

∆ Ejemplo 9

Se quiere obtener un analizador leacutexico-sintaacutectico de textos que se ajustan al siguiente formato una se-cuencia de uno o maacutes nombres seguida del siacutembolo igual seguido de un nuacutemero entero si la secuencia tiene maacutes de un nombre han de estar separados bien por una coma en todos los casos o bien por el siacutem-bolo dos puntos en todos los casos esto es son correctas las secuencias

uno dos tres = 123exclusivo = 0Abajo Arriba = 57

y son incorrectas las secuenciasuno dos tres = 123

exclusivo inclusive = 0

fuera dentro = 56

Una gramaacutetica que define las secuencias con este formato es

ltListagt = nombre ltOtrosNombresgt = numero

ltOtrosNombresgt = nombre nombre | nombre nombre | ε

se trata de una gramaacutetica que cumple la condicioacuten LL(1)- el siacutembolo director para la alternativa vaciacutea es el siacutembolo igual es evidente pues que las tres reglas de ltOtrosNombresgt tienen siacutembolos directores distintos

37

- la decisioacuten de abandonar la repeticioacuten del anaacutelisis de un nuevo nombre (en las dos primeras alterna-tivas de ltOtrosNombresgt) se puede tomar ante la presencia del siacutembolo igual

En la implementacioacuten de un analizador sintaacutectico LL(1) en sentido estricto la seleccioacuten de la alternativa vaciacutea para expandir el siacutembolo ltOtrosNombresgt se hariacutea tras comprobar expliacutecitamente que la pie-za sintaacutectica por adelantado es el siacutembolo igual

El funcionamiento del analizador generado por JavaCC depende del orden de colocacioacuten de las reglas A continuacioacuten se exponen varias posibilidades de especificacioacuten y se comenta el analizador generado en cada caso

bull Especificacioacuten primeraSe transcriben literalmente las reglas tal y como se han escrito antes en la notacioacuten BNF noacutetese que para poder codificar la parte derecha de la regla vaciacutea ha de incluirse una accioacuten sintaacutectica vaciacutea

PARSER_BEGIN (Igualdad)

public class Igualdad

public static void main (String[] argum) throws ParseException Igualdad analisis = new Igualdad(Systemin) analisissecuencia() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (Igualdad)

void secuencia() lista() ltEOFgt

void lista() lt nombre gt otrosNombres() = lt numero gt

void otrosNombres() lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt ) |

TOKEN lt nombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN lt numero ( [ 0 - 9 ] )+ gt

SKIP | r | n

38

El analizador generado funciona correctamente puesto que en el meacutetodo otrosNombres se selec-ciona la alternativa vaciacutea despueacutes de haber comprobado que la pieza por adelantado no es siacutembolo ini-cial de ninguna de las dos alternativas precedentes (no es una coma ni un siacutembolo dos puntos)

bull Especificacioacuten segundaSe considera la misma especificacioacuten anterior pero cambiando el orden de colocacioacuten de las reglas ahora la produccioacuten vaciacutea se coloca como primera alternativa (soacutelo se muestran las partes modifica-das)

void otrosNombres() | lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt )

Con la entrada de esta especificacioacuten el generador JavaCC produce un mensaje en el que se avisa de que las dos alternativas que estaacuten colocadas detraacutes de la alternativa vaciacutea nunca podraacuten seleccionarse aun asiacute se genera un analizador Pero es un analizador que funciona incorrectamente asiacute por ejemplo la entrada

mxa nyz = 57

se considera incorrecta despueacutes de visto el primer nombre se intenta aplicar la produccioacuten vaciacutea (se espera encontrar el siacutembolo igual) pero habriacutea que aplicar la regla cuyo siacutembolo director es la coma

bull Especificacioacuten terceraSe utilizan las posibilidades de la notacioacuten BNF-Ampliada para prescindir de la especificacioacuten expliacuteci-ta de la palabra vaciacutea se aprovecha que JavaCC admite el operador + para indicar repeticiones de una o maacutes veces la alternativa vaciacutea estaacute impliacutecitamente contemplada en la posibilidad de cero repeti-ciones que contempla el operador (soacutelo se muestran las partes modificadas en la especificacioacuten)

void otrosNombres() ( lt nombre gt )+ | ( lt nombre gt )

El meacutetodo otrosNombres() generado hace las comprobaciones en el siguiente orden

- se comprueba si la pieza por adelantado es una coma (siacutembolo inicial de la primera alternativa) y en este caso se selecciona la primera regla- si no es una coma se comprueba si es un siacutembolo dos puntos (siacutembolo inicial de la segunda alter-nativa) y en este caso se selecciona la segunda regla- si no es un siacutembolo dos puntos se considera la palabra vaciacutea como siacutembolo inicial (siempre posi-ble impliacutecitamente) para la segunda alternativa en este caso para que no haya error la pieza por adelantado ha de ser un siacutembolo igual

Se trata pues de un analizador que funciona correctamente

bull Especificacioacuten cuarta

39

Se pone la misma especificacioacuten anterior pero cambiando el orden de consideracioacuten de la regla vaciacutea ahora se incluye impliacutecitamente en la primera alternativa (soacutelo se muestran las partes modificadas)

void otrosNombres() ( lt nombre gt ) | ( lt nombre gt )+

Para esta especificacioacuten el generador JavaCC emite un aviso en el que se indica que la segunda alter -nativa nunca se seleccionaraacute no obstante se genera un analizador que no funciona como se supone que deberiacutea hacerlo por ejemplo el anaacutelisis de la entrada

nyz mxa = 1

produce un error despueacutes de tratado el primer nombre se intenta aplicar la produccioacuten vaciacutea (corres-pondiente a la repeticioacuten de cero veces contemplada en la primera regla) por lo que no se encuentra el esperado siacutembolo igual

40

Page 19: JavaCC

factor() ( nAst++ factor() ) if (nAst gt 0) Systemoutprintln (n Asteriscos + nAst + n)

void factor() grabar(Factor)

ltconstantegt | ltvariablegt | ( expresion() )

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP | t | n | r

Cuando se incluyen acciones sintaacutecticas la legibilidad del aspecto sintaacutectico de la especificacioacuten queda oscurecida para paliar ese inconveniente puede antildeadirse un comentario que muestre la regla sintaacutectica escueta sin las acciones por ejemplo podriacutea escribirse

void termino() int nAst = 0 grabar(Termino)

factor() ( factor() )

factor()

( nAst++ factor() )

if (nAst gt 0) Systemoutprintln (n Asteriscos + nAst + n)

loz Valor comunicado por un meacutetodoEn todo lo visto hasta ahora los meacutetodos de anaacutelisis sintaacutectico generados por JavaCC no devuelven valor alguno al terminar su tarea de anaacutelisis ya que en la especificacioacuten siempre se ha declarado el nombre de un meacutetodo precedido de void (son meacutetodos de tipo void) Pero en general un meacutetodo de anaacutelisis sintaacutec-tico puede ser de cualquier tipo (primitivo o no) por ello un meacutetodo puede devolver un valor de cualquier tipo (de cualquier clase) al meacutetodo desde el que se efectuoacute la llamada

Esta posibilidad de devolucioacuten de valores tiene una utilidad fundamental en la implementacioacuten de analiza-dores de lenguajes permite la comunicacioacuten de datos (atributos de las entidades analizadas) entre los dife -rentes meacutetodos que colaboran en el anaacutelisis del programa completo

En una especificacioacuten JavaCC la llamada a un meacutetodo ocurre cuando en la parte derecha de una regla estaacute el nombre del siacutembolo no terminal asociado al meacutetodo Cuando un meacutetodo devuelve un valor (tiene un tipo distinto de void) es habitual la recepcioacuten del valor devuelto en una variable en la propia especificacioacuten

18

se puede indicar el nombre de la variable a la que se asignaraacute el valor devuelto tras la llamada para ello se emplea una notacioacuten ideacutentica a la forma que tienen las sentencias de asignacioacuten en el lenguaje Java

dato = nombreSimb()

donde dato es el nombre de la variable a la que se asigna el valor devuelto por el meacutetodo llamado esa va -riable ha de estar oportunamente declarada del mismo tipo que dicho meacutetodo

∆ Ejemplo 2

En el mismo ejemplo de presentacioacuten se desea ademaacutes del anaacutelisis leacutexico-sintaacutectico de la entrada contar la cantidad de operadores que hay en la expresioacuten A continuacioacuten se expone una especificacioacuten JavaCC que es una posible solucioacuten soacutelo se muestran las reglas sintaacutecticas (el resto de la especificacioacuten es igual que en el ejemplo de presentacioacuten)

En la especificacioacuten que se propone puede apreciarse- los siacutembolos no terminales expresion termino y factor se han declarado de tipo int los meacutetodos correspondientes devuelven un valor de tipo entero- el valor devuelto por los meacutetodos sirve para propagar a traveacutes de la estructura sintaacutectica la cuenta de los operadores que hay en el texto analizado- por tratarse de meacutetodos que devuelven un valor es preciso incluir las correspondientes sentencias de devolucioacuten return

void unaExpresion() int nOper nOper = expresion() ltEOFgt Systemoutprintln (Cantidad de operadores = + nOper)

int expresion() int n i n = termino() ( + n++ i = termino() n = n + i )

return n

int termino() int n i n = factor() ( n++ i = factor() n = n + i )

return n

int factor() int n ltconstantegt return 0 | ltvariablegt return 0 | ( n = expresion() ) return n

La especificacioacuten de un factor tambieacuten podriacutea escribirse de esta otra manera

19

int factor() int n = 0

( ltconstantegt | ltvariablegt | ( n = expresion() ) )

return n

A continuacioacuten se expone un nuevo ejemplo para ilustrar la utilidad que proporciona el valor devuelto por un meacutetodo de anaacutelisis sintaacutectico para la propagacioacuten (ascendente) de atributos (propiedades de las entida-des analizadas)

∆ Ejemplo 3

Se pretende ampliar el analizador del ejemplo de presentacioacuten para que se emita un mensaje indicativo cuando la expresioacuten analizada sea una expresioacuten en la que soacutelo aparecen constantes en la solucioacuten que se muestra soacutelo se incluyen las reglas sintaacutecticas (en el resto de la especificacioacuten no hay modificacio-nes)

void unaExpresion() boolean esCte

expresion() ltEOFgt

esCte = expresion() ltEOFgt

if (esCte) Systemoutprintln(nExpresion constanten)

boolean expresion() boolean esCte esTambienCte

termino() ( + termino() )

esCte = termino() ( + esTambienCte = termino() esCte = esCte ampamp esTambienCte )

return esCte

boolean termino() boolean esCte esTambienCte

factor() ( factor() )

esCte = factor() ( esTambienCte = factor() esCte = esCte ampamp esTambienCte )

return esCte

20

boolean factor() boolean esCte

ltconstantegt return true | ltvariablegt return false | ( esCte = expresion() ) return esCte

bull Reconsideracioacuten sobre las producciones vaciacuteasAnteriormente se ha comentado que para representar una produccioacuten vaciacutea en una especificacioacuten JavaCC hay que hacer uso de los metasiacutembolos de opcionalidad para ilustrar esta idea se ha incluido como ejem-plo la especificacioacuten de una lista de paraacutemetros compuesta por cero o maacutes expresiones separadas entre siacute por el caraacutecter coma

void parametros() [ expresion() ( expresion() ) ]

Pero si se aprovecha la posibilidad de incorporar acciones sintaacutecticas se tiene una manera de especificar directamente producciones vaciacuteas para conseguirlo basta considerar una produccioacuten vaciacutea (no tiene nada en su parte derecha) acompantildeada de una accioacuten sintaacutectica cuyo bloque de coacutedigo esteacute vaciacuteo Seguacuten esta posibilidad la especificacioacuten anterior podriacutea tambieacuten escribirse asiacute

void parametros() expresion() ( expresion() ) |

En general la accioacuten sintaacutectica asociada a una produccioacuten vaciacutea no tiene por queacute ser un bloque vaciacuteo de coacutedigo por ejemplo si se quiere que el meacutetodo que analiza los paraacutemetros de una llamada devuelva la cantidad de paraacutemetros encontrados se podriacutea escribir la siguiente especificacioacuten

int parametros()

int cantidad

( expresion() cantidad = 1

( expresion() cantidad++ )

| cantidad = 0 )

return cantidad

y si no quisiera ponerse expliacutecitamente la alternativa de la produccioacuten vaciacutea podriacutea escribirse

int parametros() int cantidad = 0

21

[ expresion() cantidad++ ( expresion() cantidad++ ) ]

return cantidad

Comunicacioacuten entre los analizadores leacutexico y sintaacutecticoCada vez que el analizador sintaacutectico realiza una llamada al analizador lexicograacutefico recibe como resultado de la llamada una representacioacuten de la pieza sintaacutectica encontrada en el texto analizado En los analizadores generados por JavaCC la comunicacioacuten de la pieza se efectuacutea mediante un valor de clase Token Esta cla-se estaacute definida en el fichero Tokenjava que se obtiene en todo caso siempre con el mismo contenido cualquiera que sea la especificacioacuten proporcionada como entrada al generador

Entre los campos de la clase Token se encuentran los siguientes

public int kind

nuacutemero entero que sirve de representacioacuten interna de la pieza sintaacutectica estos nuacutemeros asociados a las piezas son asignados automaacuteticamente por JavaCC los valores considerados pueden consultar-se en el fichero generado de nombre Constantsjava

public String image

cadena que contiene de la secuencia de caracteres (lexema) que constituyen la pieza sintaacutectica co-municada

public int beginLine beginColumn endLine endColumn

posiciones ocupadas (nuacutemeros de liacutenea y columna en el fichero de entrada) por el comienzo y el fi-nal del lexema de la pieza sintaacutectica comunicada

La clase Token tiene otros campos y meacutetodos en el fichero Tokenjava se tienen comentarios des-criptivos sobre todos los campos y meacutetodos que forman parte de la clase todos los campos de la clase se declaran como public

Ya se ha comentado anteriormente que cuando un meacutetodo de anaacutelisis sintaacutectico (asociado a un siacutembolo no terminal) tiene tipo el valor devuelto tras una llamada se puede almacenar en una variable por si se desea utilizar en alguna accioacuten sintaacutectica para ello se escribe

valor = nombreSimbolo()

(siendo valor el nombre de la variable y nombreSimbolo el nombre del siacutembolo no terminal) De manera anaacuteloga el objeto de la clase Token representativo de la pieza sintaacutectica encontrada en la entrada por el analizador lexicograacutefico puede asignarse a una variable del mismo tipo para tener accesibles las ca -racteriacutesticas de la pieza por si se precisaran en alguna accioacuten sintaacutectica para ello en la parte de la especifi -cacioacuten sintaacutectica donde aparezca la pieza sintaacutectica se escribe

dato = lt nombrePieza gt

donde nombrePieza es el nombre de la pieza sintaacutectica y dato es el nombre de una variable de tipo Token en la que se anota el valor indicativo de las propiedades de la pieza sintaacutectica comunicada

Tambieacuten existe otra manera de acceder a las caracteriacutesticas de la pieza sintaacutectica que el analizador lexico -graacutefico comunica al analizador sintaacutectico en la clase del analizador sintaacutectico se tiene declarado el campo

[ static ] public Token token

22

cada vez que el analizador sintaacutectico recibe una pieza sintaacutectica comunicada por el analizador lexicograacutefico se deja anotada en el campo token y ese contenido no se altera hasta que sea proporcionada la siguiente pieza de la entrada

∆ Ejemplo 4

Se considera una ampliacioacuten del ejemplo de presentacioacuten ademaacutes del anaacutelisis de la entrada se pretende obtener una relacioacuten numerada de las variables y las constantes indicando el nuacutemero de liacutenea y el nuacuteme-ro de columna donde estaacuten situadas por ejemplo si en la entrada se tiene la expresioacuten (grabada en dos liacuteneas)

x (y + 11) + 22

en la salida se tendraacute

1- x linea 1 columna 1

2- y linea 1 columna 6

3- 11 linea 1 columna 10

4- 22 linea 2 columna 5

En la especificacioacuten que se propone como solucioacuten puede apreciarse- en la clase del analizador sintaacutectico se han incluido meacutetodos auxiliares en los que se usan los nom-bres image beginLine beginColumn que son campos de un objeto de clase Token

- en la especificacioacuten sintaacutectica de factor se emplea la variable pieza de tipo Token para reco-ger las caracteriacutesticas de las piezas sintaacutecticas detectadas- la variable numero hace de contador para numerar las constantes y variables

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin

static int numero = 0

private static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

private static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

private static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

23

PARSER_END (ExprMin)

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() Token pieza pieza = ltconstantegt numero++ grabarDatosPieza(numero pieza) | pieza = ltvariablegt numero++ grabarDatosPieza(numero pieza) | ( expresion() )

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP | n | t | r

Si se agrupan las dos acciones sintaacutecticas comunes la especificacioacuten de factor tambieacuten puede escri-birse de esta forma

void factor() Token pieza ( pieza = ltconstantegt | pieza = ltvariablegt

) numero++ grabarDatosPieza(numero pieza)

| ( expresion() )

24

Si se accede al campo token de la clase del analizador sintaacutectico se puede resolver este ejemplo pres-cindiendo de la variable pieza

void factor() ( ltconstantegt | ltvariablegt

) numero++ grabarDatosPieza(numero token)

| ( expresion() )

Tareas asociadas a las piezas sintaacutecticasYa se ha visto que es posible asociar bloques de coacutedigo Java a puntos de la estructura sintaacutectica para que se realicen ciertas operaciones cuando el analizador sintaacutectico pase por esa zona de la estructura Tambieacuten es posible asociar coacutedigo Java a las piezas sintaacutecticas para que el analizador lexicograacutefico efectuacutee determina-das tareas cuando detecte en la entrada analizada piezas que tengan asociado coacutedigo

loz Accioacuten ligada a una pieza sintaacutecticaSi se asocia un bloque de coacutedigo a una pieza sintaacutectica se consigue que cada vez que se detecta en la en-trada analizada la presencia de esa pieza se ejecute en ese preciso momento el coacutedigo del bloque asociado a la pieza Estas acciones se denominan ldquoacciones lexicograacuteficasrdquo (lexical actions) el coacutedigo del bloque se traslada literalmente al coacutedigo del analizador lexicograacutefico generado para que se ejecute oportunamente cuando se detecte la pieza ligada al bloque de coacutedigo La accioacuten lexicograacutefica se ejecuta inmediatamente antes que la pieza sea comunicada al analizador sintaacutectico

Se puede asociar una accioacuten lexicograacutefica a una pieza sintaacutectica declarada con TOKEN y tambieacuten a una se-cuencia de caracteres declarada con SKIP ademaacutes de eacutestas hay otras dos formas de declaraciones lexico-graacuteficas (no mencionadas en esta introduccioacuten) que tambieacuten pueden llevar una accioacuten asociada

En una especificacioacuten JavaCC la forma de una pieza sintaacutectica (o bien de una secuencia de caracteres) se define poniendo su descripcioacuten delimitada entre llaves detraacutes de la indicacioacuten TOKEN (o bien SKIP) si se quiere asociar una accioacuten lexicograacutefica a la pieza (o a la secuencia) hay que colocar el bloque de coacutedigo de la accioacuten a continuacioacuten de la descripcioacuten de la forma de la pieza (o de la secuencia) el bloque (delimi-tado por llaves) queda situado entre la descripcioacuten y la llave que cierra la especificacioacuten

La pieza sintaacutectica especial que representa el final del fichero (EOF) tambieacuten puede llevar asociada una ac-cioacuten lexicograacutefica para ello hay que poner

ltgt TOKEN lt EOF gt coacutedigo de la accioacuten asociada

para justificar la presencia de los caracteres ltgt delante de la palabra TOKEN hay que basarse en el con-cepto de ldquoestado lexicograacuteficordquo (cuya explicacioacuten queda fuera del alcance de esta introduccioacuten)

∆ Ejemplo 5

Se considera nuevamente el ejemplo de presentacioacuten como resultado del anaacutelisis de una expresioacuten se pretende obtener una relacioacuten de los pareacutentesis contenidos en la expresioacuten en el mismo orden en que se encuentran ademaacutes por cada salto de liacutenea del texto de entrada se grabaraacute en la salida el caraacutecter y

25

se pasaraacute a una nueva liacutenea tambieacuten se anotaraacute la frase Final del trabajo cuando se alcance el final del fichero de entrada Asiacute por ejemplo si la entrada analizada es

(a + b) (( x )) (y + 7)

los resultados obtenidos seraacuten

() (())()

Final del trabajo

En la especificacioacuten JavaCC que se propone como solucioacuten soacutelo se muestran las reglas sintaacutecticas y le -xicograacuteficas (en el resto no hay cambios) en esta solucioacuten puede apreciarse- las piezas sintaacutecticas correspondientes a los pareacutentesis se han definido de manera nominal para po-der asociarles una accioacuten lexicograacutefica- los caracteres incluidos en la pieza de tipo SKIP se han descompuesto en dos grupos ya que la ac-cioacuten lexicograacutefica incorporada soacutelo afecta a uno de ellos

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ltpAbrirgt expresion() ltpCerrargt

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

TOKEN

26

lt pAbrir ( gt Systemoutprint(()

TOKEN lt pCerrar ) gt Systemoutprint())

ltgtTOKEN lt EOF gt Systemoutprintln ( Final del trabajo )

SKIP n Systemoutprint( n)

SKIP | t | r

loz Bloque de declaraciones lexicograacuteficasCada definicioacuten de una pieza sintaacutectica puede llevar asociada una accioacuten lexicograacutefica que se ejecuta in-mediatamente despueacutes de detectar en la entrada un lexema que se ajusta a la definicioacuten de la pieza cada accioacuten lexicograacutefica es un bloque de coacutedigo Java que puede contener sus propias declaraciones Pero a ve-ces interesa tener declaraciones que sean compartidas por todas las acciones lexicograacuteficas con indepen-dencia de la pieza a la que esteacute asociada cada una de ellas

Para incluir declaraciones que puedan usarse desde el coacutedigo asociado a cualquiera de las piezas sintaacutecti -cas se dispone de una construccioacuten con el siguiente formato

TOKEN_MGR_DECLS

que consta de una palabra reservada seguida del siacutembolo dos puntos seguido de un bloque de coacutedigo Java (delimitado por llaves) esta declaracioacuten se puede colocar en cualquier parte de una especificacioacuten JavaCC si se incluye soacutelo puede ponerse una vez

Todas las declaraciones de variables o de meacutetodos puestas en el bloque de declaraciones lexicograacuteficas compartidas son accesibles desde el coacutedigo de cualquier accioacuten lexicograacutefica la justificacioacuten es sencilla el bloque de declaraciones se traslada al principio del coacutedigo del analizador lexicograacutefico generado y las ac-ciones lexicograacuteficas tambieacuten se trasladan a ese analizador

loz Declaraciones lexicograacuteficas predefinidasDe lo expuesto hasta ahora se aprecia que en una accioacuten lexicograacutefica asociada a una pieza sintaacutectica re-sultan accesibles

- las declaraciones de su propio bloque (si tiene declaraciones)- los componentes del bloque de declaraciones compartidas (si estaacute incluido en la especificacioacuten)

Ademaacutes de esto se tienen otras declaraciones pertenecientes a la clase TokenManager que constituye el analizador lexicograacutefico que tambieacuten son utilizables desde cualquiera de las acciones lexico-graacuteficas estas declaraciones pueden considerarse como ldquopredefinidasrdquo ya que son incorporadas automaacuteti-camente por el generador Entre estas declaraciones accesibles se encuentran las siguientes

[ static ] StringBuffer imagecontiene la cadena de caracteres representativa de la pieza sintaacutectica actual que se acaba de recono-cer en le entrada se trata de un campo de la clase y se declara como static cuando el analizador

27

se ha generado con la opcioacuten (por defecto) Static = true si se generase con la opcioacuten Sta-tic = false no quedariacutea declarada como static

Token matchedToken

variable de clase Token que representa la pieza sintaacutectica actual la que se acaba de encontrar en la entrada se trata de una variable local del meacutetodo al que se ha trasladado el coacutedigo de la accioacuten (y por ello accesible desde ese coacutedigo)

El campo image puede usarse tanto si la accioacuten lexicograacutefica estaacute asociada a una pieza declarada con TOKEN como si lo estaacute a una secuencia declarada con SKIP Sin embargo la variable matchedToken no puede usarse en una accioacuten asociada a un patroacuten especificado como SKIP el motivo es que lo declara-do como SKIP no constituye propiamente una pieza sintaacutectica ya que no se va a comunicar al analizador sintaacutectico

∆ Ejemplo 6

Se propone aquiacute una nueva solucioacuten para el ejemplo nordm 4 expuesto anteriormente

Esta nueva solucioacuten tal y como se expone aquiacute admite algunas mejoras ya que su pretensioacuten soacutelo es ser-vir de ilustracioacuten para algunos aspectos que se acaban de citar (maacutes adelante se propone otra solucioacuten al-ternativa)

En la especificacioacuten de la solucioacuten es posible apreciar esto- las acciones lexicograacuteficas pueden llevar sus propias declaraciones las variables nLineaVar nColumnaVar nLineaCte y nColumnaCte se declaran dentro de las acciones

- la variable matchedToken a la que se hace referencia en el coacutedigo de la accioacuten lexicograacutefica aso-ciada a una variable contiene la informacioacuten sobre la pieza sintaacutectica que se acaba de detectar una va-riable de la expresioacuten analizada anaacutelogamente con la referencia relativa a una constante- el nombre image que aparece en las acciones lexicograacuteficas se refiere al campo de la clase Expr-MinTokenManager (analizador lexicograacutefico) noacutetese que en efecto se comunica como un paraacuteme-tro de clase StringBuffer

- la variable numero se emplea para numerar las constantes y variables encontradas

- los nombres beginLine y beginColumn son campos (puacuteblicos) de la clase Token por ello se pueden aplicar a un objeto de esa clase

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

void unaExpresion()

28

expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n StringBuffer lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

TOKEN lt variable [a-z] gt

int nLineaVar nColumnaVar numero++ nLineaVar = matchedTokenbeginLine nColumnaVar = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaVar nColumnaVar)

TOKEN lt constante ( [0-9] ) + gt

int nLineaCte nColumnaCte

29

numero++ nLineaCte = matchedTokenbeginLine nColumnaCte = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaCte nColumnaCte)

SKIP | n | t | r

La especificacioacuten precedente se puede escribir de otra manera maacutes escueta como se indica a continua-cioacuten (soacutelo se muestran las modificaciones) sobre esta nueva especificacioacuten puede mencionarse- se han eliminado por innecesarias las variables declaradas dentro de las acciones lexicograacuteficas- el nombre image que aparece en el bloque de las declaraciones compartidas se refiere al campo de la clase Token noacutetese que en efecto se aplica a un objeto de esa clase y se comunica como un paraacute -metro de tipo String

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

TOKEN lt variable [a-z] gt

numero++ grabarDatosPieza(numero matchedToken)

TOKEN lt constante ( [0-9] ) + gt

numero++ grabarDatosPieza(numero matchedToken)

loz Accioacuten lexicograacutefica comuacutenSi hubiera que realizar la misma tarea tras la deteccioacuten de todas y cada una de las piezas sintaacutecticas una posibilidad incoacutemoda seriacutea repetir el mismo coacutedigo en todas las acciones lexicograacuteficas para evitar tanta reiteracioacuten se tiene la opcioacuten Common_Token_Action su valor por defecto es false pero si se le asigna el valor true es posible definir una ldquoaccioacuten lexicograacutefica comuacutenrdquo que se escribe una uacutenica vez y se aplica a todas las piezas sintaacutecticas

30

Cuando la opcioacuten Common_Token_Action se establece con el valor true es obligado incluir el blo-que de declaraciones lexicograacuteficas compartidas (TOKEN_MGR_DECLS) Ademaacutes en ese bloque se preci-sa la definicioacuten de un meacutetodo cuyo encabezamiento sea

[ static ] void CommonTokenAction(Token t)

el coacutedigo de este meacutetodo constituye la accioacuten lexicograacutefica comuacuten que se ejecutaraacute despueacutes de la deteccioacuten de cualquier pieza sintaacutectica el meacutetodo tiene un paraacutemetro de tipo Token mediante el que se puede co-municar la informacioacuten correspondiente a la pieza que se acaba de encontrar en la entrada analizada

Si una pieza sintaacutectica tiene asociada una accioacuten lexicograacutefica propia la accioacuten lexicograacutefica comuacuten (si es-taacute definida) se ejecutaraacute inmediatamente despueacutes de realizar la accioacuten propia

La accioacuten lexicograacutefica comuacuten se aplica a todas las piezas sintaacutecticas definidas como TOKEN bien sea no-minalmente (en las reglas lexicograacuteficas) o bien anoacutenimamente (incorporadas dentro de las reglas sintaacutecti-cas) sin embargo no se aplica cuando la secuencia acoplada se corresponde con una expresioacuten regular es-pecificada como SKIP (noacutetese que por el contrario una especificacioacuten SKIP siacute puede tener asociada una accioacuten lexicograacutefica propia)

El coacutedigo del meacutetodo CommonTokenAction se traslada como un componente maacutes del bloque de decla-raciones compartidas (TOKEN_MGR_DECLS) al principio de la clase del analizador lexicograacutefico

∆ Ejemplo 7

Se pretende ahora ampliar el ejemplo de presentacioacuten con la obtencioacuten de una relacioacuten numerada de los lexemas de las piezas sintaacutecticas encontradas en la entrada analizada en el caso de las variables y de las constantes antes del lexema se mostraraacute un indicativo (Var Const) por ejemplo si la expresioacuten de la entrada es

( a + 987 ) b

se grabaraacute la salida

( 1Var a 2+ 3Const 987 4) 5 6Var b 7 8

En la solucioacuten que se propone a continuacioacuten se muestran la definicioacuten de las opciones y las reglas sin-taacutecticas y lexicograacuteficas (en el resto no hay modificaciones) puede resaltarse lo siguiente- las llamadas al meacutetodo CommonTokenAction se incorporan automaacuteticamente no hay que incluir-las en la especificacioacuten como paraacutemetro de llamada a este meacutetodo siempre se emplea el objeto que re -presenta la pieza que se acaba de detectar- la accioacuten lexicograacutefica comuacuten realiza la actualizacioacuten del contador de piezas y la grabacioacuten del lexe-ma de la pieza detectada con su nuacutemero de orden- la accioacuten lexicograacutefica comuacuten tambieacuten se aplica a las piezas sintaacutecticas anoacutenimas (pareacutentesis y ope-radores) y a la pieza especial que representa el final del fichero (EOF)

- las piezas sintaacutecticas ltconstantegt y ltvariablegt tienen asociada una accioacuten lexicograacutefica propia que se ejecuta antes de la accioacuten lexicograacutefica comuacuten- los caracteres que se saltan (SKIP) no estaacuten afectados por la accioacuten lexicograacutefica comuacuten

options Ignore_Case = true Common_Token_Action = true

31

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarIndicativo(String indicativo) Systemoutprint(indicativo + )

static void grabarLexema (int num String lexema) Systemoutprint(lexema + + num + n)

static void CommonTokenAction(Token pieza) numero++ grabarLexema(numero piezaimage)

TOKEN lt variable [a-z] gt

grabarIndicativo(Var)

32

TOKEN lt constante ( [0-9] ) + gt

grabarIndicativo(Const)

SKIP | n | t | r

A modo de ejemplo de aplicacioacuten de la accioacuten lexicograacutefica comuacuten en lo que sigue se ofrece una solucioacuten para la grabacioacuten (en el fichero predefinido de salida) del texto analizado con las liacuteneas numeradas

∆ Ejemplo 8 [ Listado del texto analizado ]Se desea completar el ejemplo de presentacioacuten con la grabacioacuten de un listado con las liacuteneas numeradas del texto analizado (noacutetese que una expresioacuten puede escribirse ocupando varias liacuteneas) No se muestra la especificacioacuten sintaacutectica debido a que no hay ninguna modificacioacuten en ella Los aspectos resentildeables de la solucioacuten que se propone son- se usa la variable numLin como contador de liacuteneas

- en la variable linea se van yuxtaponiendo los lexemas de las piezas sintaacutecticas de una liacutenea esta yuxtaposicioacuten se realiza mediante la accioacuten lexicograacutefica comuacuten- las secuencias reconocidas con los patrones especificados como SKIP no implican la ejecucioacuten de la accioacuten lexicograacutefica comuacuten por ello su lexema (almacenado en la variable image) ha de yuxtaponer-se expliacutecitamente- hay que separar el patroacuten correspondiente al fin de liacutenea para detectar el momento en que ha de gra-barse la liacutenea completa cuyo final se ha alcanzado

options Ignore_Case = true Common_Token_Action = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(nnAnalisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

TOKEN_MGR_DECLS static int numLin = 0 static String linea =

33

static void CommonTokenAction(Token pieza) linea = linea + piezaimage

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP lt | t | r gt linea = linea + image

SKIP n numLin++ Systemoutprintln(numLin + + linea) linea =

A modo de recapitulacioacutenↄ Accioacuten lexicograacutefica Accioacuten sintaacutectica

bull accioacuten lexicograacutefica bloque de coacutedigo Java asociado a una pieza sintaacutectica nominal (TOKEN) o a una secuencia de caracteres (SKIP) se ejecuta cuando se detecta en la entrada la pieza o la secuencia corres-pondiente no se puede aplicar a piezas sintaacutecticas anoacutenimas (autodefinidas con comillas)bull accioacuten sintaacutectica bloque de coacutedigo Java asociado a un punto de la estructura sintaacutectica (el punto indica-do por el sitio de la produccioacuten donde se inserta el bloque) se ejecuta cuando el anaacutelisis de la entrada pa-sa por ese punto de la estructura

ↄ Valor de una pieza sintaacutectica comunicadaEl analizador lexicograacutefico comunica al analizador sintaacutectico una pieza a traveacutes de un valor de la clase Token (fichero Tokenjava) un objeto de la clase Token representa las caracteriacutesticas de la pieza comunicada En la clase Token entre otros se tienen los campos

String imagelexema de la pieza

int beginLine beginColumn endLine endColumnposicioacuten del lexema en el fichero analizado (indicada por los nuacutemeros de fila y de columna de su comienzo y de su terminacioacuten)

ↄ Valor asociado a un siacutembolo

34

En las producciones que especifican la sintaxis se pueden incluir asignaciones que sirven para dejar ano-tado el valor asociado a un siacutembolo

bull siacutembolo no terminal valor = nombreSimbolo()el meacutetodo de anaacutelisis asociado a nombreSimbolo se ha declarado de un cierto tipo (distinto del tipo void) valor ha de ser una variable del mismo tipo que el meacutetodo

bull siacutembolo terminal dato = lt nombrePieza gtel valor asignado es el valor de la clase Token representativo de la pieza sintaacutectica (nominal) dato ha de ser una variable de tipo Token

ↄ Miradas sobre la pieza sintaacutectica comunicadaLa pieza sintaacutectica comunicada puede mirarse desde cada uno de los dos analizadores

bull analizador sintaacutecticola pieza comunicada (desde el analizador lexicograacutefico) se tiene anotada en un campo de la clase del analizador sintaacutectico declarado como

(static) public Token token

este valor es accesible desde el coacutedigo de todos los meacutetodos de anaacutelisis sintaacutectico el contenido del campo variacutea cuando se recibe una nueva pieza sintaacutectica

bull analizador lexicograacuteficola pieza dispuesta para ser comunicada (al analizador sintaacutectico) se tiene anotada en una variable local declarada como

Token matchedToken

este valor es accesible para el coacutedigo de todas las acciones lexicograacuteficas

ↄ El lexema de la pieza comunicadaEl lexema de la pieza sintaacutectica comunicada se encuentra disponible en dos sitios distintos que compar-ten el mismo nombre pero que son de distinto tipo y se usan de distinta manera

bull String image

es un campo de la clase TOKEN puede consultarse en relacioacuten con un objeto de esa clase que esteacute disponible

bull StringBuffer image

es un campo de la clase del analizador lexicograacutefico ( ∙ ∙ ∙ ∙ TokenManagerjava) es accesible desde el coacutedigo de la clase y por lo tanto desde todas las acciones lexicograacuteficas (bloques de coacute-digo que estaacuten incorporados al analizador lexicograacutefico)

se trata de un valor disponible tanto para las piezas sintaacutecticas nominales (TOKEN) como para las secuencias que se saltan (SKIP)

ↄ Resumen relativo a la lexicografiacutea

TOKEN pieza sintaacutectica nominal∙ ∙ ∙ pieza sintaacutectica anoacutenimaSKIP secuencia de caracteres que no forman pieza

35

Precisiones sobre el analizador sintaacutectico generadoPuede decirse que en su funcionamiento maacutes sencillo cuando no se emplean las posibilidades de examen por adelantado (ldquolookaheadrdquo) JavaCC comprueba que las reglas sintaacutecticas cumplen la condicioacuten LL(1) y generan un analizador descendente-predictivo-recursivo Pero esta apreciacioacuten hay que matizarla ya que el analizador sintaacutectico generado no se ajusta en sentido estricto al modelo de analizador LL(1)

En el modelo de anaacutelisis LL(1) el orden del examen de las distintas alternativas de expansioacuten de un siacutembo-lo no terminal es indiferente ya que se conocen los siacutembolos directores de cada una de ellas (que resultan ser conjuntos disjuntos) en la implementacioacuten del subprograma de anaacutelisis asociado al siacutembolo no terminal ltNombreSimbgt cuyas producciones son

ltNombreSimbgt = α1

| α2

∙ ∙ ∙

| αn

se podriacutea preguntar en cualquier orden si la pieza actual pertenece al conjunto de siacutembolos directores de las distintas reglas Sin embargo en el analizador sintaacutectico generado por JavaCC la implementacioacuten del meacute-todo asociado al siacutembolo no terminal ltNombreSimbgt no sigue esa pauta

Si las anteriores reglas sintaacutecticas se transcriben en JavaCC de la forma

void nombreSimb () α1-jcc

| α2-jcc

∙ ∙ ∙

| αn-jcc

1

accioacutenlexicograacutefica

comuacuten

Commom -TokenAction2

accioacutenlexicograacutefica

propia

(accioacuten ligadaa una pieza)3

bloque dedeclaracioneslexicograacuteficas

TOKEN_MGR_DECLS4

declaracioneslexicograacuteficaspredefinidas

(disponibilidad

de uso)TOKENsisisisi∙ ∙ ∙sino--SKIPnosisisi

Las indicaciones si de las columnas 3 y 4 son consecuencia de las indicaciones de la columna 2

La accioacuten lexicograacutefica comuacuten se ejecuta despueacutes de la accioacuten lexicograacutefica propia

36

el meacutetodo de anaacutelisis generado procede de la siguiente manera

se van considerando las alternativas sucesivamente en el mismo orden en el que estaacuten especifica-das primero la de α1 (α1-jcc) despueacutes la de α2 (α2-jcc) etc

para cada alternativa seleccionada se comprueba si la pieza por adelantado coincide con alguno de los siacutembolos iniciales de la parte derecha y si hay coincidencia se prosigue el anaacutelisis con ella si no hay coincidencia se pasa a la alternativa siguiente

siguiendo con este orden si se llega al final de las alternativas sin haberse encontrado coincidencia alguna se produce un error sintaacutectico

En el analizador generado por JavaCC hay una peculiaridad que no se tiene en los analizadores LL(1) es-trictamente considerados la palabra vaciacutea siacute se considera como posible siacutembolo inicial de la parte derecha una regla (esto ocurre siempre que la parte derecha es anulable) De esta peculiaridad se deriva una conse-cuencia que ha de tenerse en cuenta al escribir la especificacioacuten dado que siempre es posible considerar la palabra vaciacutea como la siguiente pieza por adelantado si se llega a examinar la alternativa correspondiente a una produccioacuten anulable el analizador generado siempre seleccionaraacute esa alternativa con independencia de la siguiente pieza que esteacute presente en la entrada que queda por analizar

Asiacute pues si alguna de las producciones que definen un siacutembolo no terminal es anulable es importante el or-den en que se escriben las alternativas en una especificacioacuten sintaacutectica JavaCC el funcionamiento del ana-lizador generado siacute depende de la colocacioacuten elegida para las reglas

Para ilustrar estas peculiaridades del analizador sintaacutectico generado por JavaCC a continuacioacuten se expone un ejemplo

∆ Ejemplo 9

Se quiere obtener un analizador leacutexico-sintaacutectico de textos que se ajustan al siguiente formato una se-cuencia de uno o maacutes nombres seguida del siacutembolo igual seguido de un nuacutemero entero si la secuencia tiene maacutes de un nombre han de estar separados bien por una coma en todos los casos o bien por el siacutem-bolo dos puntos en todos los casos esto es son correctas las secuencias

uno dos tres = 123exclusivo = 0Abajo Arriba = 57

y son incorrectas las secuenciasuno dos tres = 123

exclusivo inclusive = 0

fuera dentro = 56

Una gramaacutetica que define las secuencias con este formato es

ltListagt = nombre ltOtrosNombresgt = numero

ltOtrosNombresgt = nombre nombre | nombre nombre | ε

se trata de una gramaacutetica que cumple la condicioacuten LL(1)- el siacutembolo director para la alternativa vaciacutea es el siacutembolo igual es evidente pues que las tres reglas de ltOtrosNombresgt tienen siacutembolos directores distintos

37

- la decisioacuten de abandonar la repeticioacuten del anaacutelisis de un nuevo nombre (en las dos primeras alterna-tivas de ltOtrosNombresgt) se puede tomar ante la presencia del siacutembolo igual

En la implementacioacuten de un analizador sintaacutectico LL(1) en sentido estricto la seleccioacuten de la alternativa vaciacutea para expandir el siacutembolo ltOtrosNombresgt se hariacutea tras comprobar expliacutecitamente que la pie-za sintaacutectica por adelantado es el siacutembolo igual

El funcionamiento del analizador generado por JavaCC depende del orden de colocacioacuten de las reglas A continuacioacuten se exponen varias posibilidades de especificacioacuten y se comenta el analizador generado en cada caso

bull Especificacioacuten primeraSe transcriben literalmente las reglas tal y como se han escrito antes en la notacioacuten BNF noacutetese que para poder codificar la parte derecha de la regla vaciacutea ha de incluirse una accioacuten sintaacutectica vaciacutea

PARSER_BEGIN (Igualdad)

public class Igualdad

public static void main (String[] argum) throws ParseException Igualdad analisis = new Igualdad(Systemin) analisissecuencia() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (Igualdad)

void secuencia() lista() ltEOFgt

void lista() lt nombre gt otrosNombres() = lt numero gt

void otrosNombres() lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt ) |

TOKEN lt nombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN lt numero ( [ 0 - 9 ] )+ gt

SKIP | r | n

38

El analizador generado funciona correctamente puesto que en el meacutetodo otrosNombres se selec-ciona la alternativa vaciacutea despueacutes de haber comprobado que la pieza por adelantado no es siacutembolo ini-cial de ninguna de las dos alternativas precedentes (no es una coma ni un siacutembolo dos puntos)

bull Especificacioacuten segundaSe considera la misma especificacioacuten anterior pero cambiando el orden de colocacioacuten de las reglas ahora la produccioacuten vaciacutea se coloca como primera alternativa (soacutelo se muestran las partes modifica-das)

void otrosNombres() | lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt )

Con la entrada de esta especificacioacuten el generador JavaCC produce un mensaje en el que se avisa de que las dos alternativas que estaacuten colocadas detraacutes de la alternativa vaciacutea nunca podraacuten seleccionarse aun asiacute se genera un analizador Pero es un analizador que funciona incorrectamente asiacute por ejemplo la entrada

mxa nyz = 57

se considera incorrecta despueacutes de visto el primer nombre se intenta aplicar la produccioacuten vaciacutea (se espera encontrar el siacutembolo igual) pero habriacutea que aplicar la regla cuyo siacutembolo director es la coma

bull Especificacioacuten terceraSe utilizan las posibilidades de la notacioacuten BNF-Ampliada para prescindir de la especificacioacuten expliacuteci-ta de la palabra vaciacutea se aprovecha que JavaCC admite el operador + para indicar repeticiones de una o maacutes veces la alternativa vaciacutea estaacute impliacutecitamente contemplada en la posibilidad de cero repeti-ciones que contempla el operador (soacutelo se muestran las partes modificadas en la especificacioacuten)

void otrosNombres() ( lt nombre gt )+ | ( lt nombre gt )

El meacutetodo otrosNombres() generado hace las comprobaciones en el siguiente orden

- se comprueba si la pieza por adelantado es una coma (siacutembolo inicial de la primera alternativa) y en este caso se selecciona la primera regla- si no es una coma se comprueba si es un siacutembolo dos puntos (siacutembolo inicial de la segunda alter-nativa) y en este caso se selecciona la segunda regla- si no es un siacutembolo dos puntos se considera la palabra vaciacutea como siacutembolo inicial (siempre posi-ble impliacutecitamente) para la segunda alternativa en este caso para que no haya error la pieza por adelantado ha de ser un siacutembolo igual

Se trata pues de un analizador que funciona correctamente

bull Especificacioacuten cuarta

39

Se pone la misma especificacioacuten anterior pero cambiando el orden de consideracioacuten de la regla vaciacutea ahora se incluye impliacutecitamente en la primera alternativa (soacutelo se muestran las partes modificadas)

void otrosNombres() ( lt nombre gt ) | ( lt nombre gt )+

Para esta especificacioacuten el generador JavaCC emite un aviso en el que se indica que la segunda alter -nativa nunca se seleccionaraacute no obstante se genera un analizador que no funciona como se supone que deberiacutea hacerlo por ejemplo el anaacutelisis de la entrada

nyz mxa = 1

produce un error despueacutes de tratado el primer nombre se intenta aplicar la produccioacuten vaciacutea (corres-pondiente a la repeticioacuten de cero veces contemplada en la primera regla) por lo que no se encuentra el esperado siacutembolo igual

40

Page 20: JavaCC

se puede indicar el nombre de la variable a la que se asignaraacute el valor devuelto tras la llamada para ello se emplea una notacioacuten ideacutentica a la forma que tienen las sentencias de asignacioacuten en el lenguaje Java

dato = nombreSimb()

donde dato es el nombre de la variable a la que se asigna el valor devuelto por el meacutetodo llamado esa va -riable ha de estar oportunamente declarada del mismo tipo que dicho meacutetodo

∆ Ejemplo 2

En el mismo ejemplo de presentacioacuten se desea ademaacutes del anaacutelisis leacutexico-sintaacutectico de la entrada contar la cantidad de operadores que hay en la expresioacuten A continuacioacuten se expone una especificacioacuten JavaCC que es una posible solucioacuten soacutelo se muestran las reglas sintaacutecticas (el resto de la especificacioacuten es igual que en el ejemplo de presentacioacuten)

En la especificacioacuten que se propone puede apreciarse- los siacutembolos no terminales expresion termino y factor se han declarado de tipo int los meacutetodos correspondientes devuelven un valor de tipo entero- el valor devuelto por los meacutetodos sirve para propagar a traveacutes de la estructura sintaacutectica la cuenta de los operadores que hay en el texto analizado- por tratarse de meacutetodos que devuelven un valor es preciso incluir las correspondientes sentencias de devolucioacuten return

void unaExpresion() int nOper nOper = expresion() ltEOFgt Systemoutprintln (Cantidad de operadores = + nOper)

int expresion() int n i n = termino() ( + n++ i = termino() n = n + i )

return n

int termino() int n i n = factor() ( n++ i = factor() n = n + i )

return n

int factor() int n ltconstantegt return 0 | ltvariablegt return 0 | ( n = expresion() ) return n

La especificacioacuten de un factor tambieacuten podriacutea escribirse de esta otra manera

19

int factor() int n = 0

( ltconstantegt | ltvariablegt | ( n = expresion() ) )

return n

A continuacioacuten se expone un nuevo ejemplo para ilustrar la utilidad que proporciona el valor devuelto por un meacutetodo de anaacutelisis sintaacutectico para la propagacioacuten (ascendente) de atributos (propiedades de las entida-des analizadas)

∆ Ejemplo 3

Se pretende ampliar el analizador del ejemplo de presentacioacuten para que se emita un mensaje indicativo cuando la expresioacuten analizada sea una expresioacuten en la que soacutelo aparecen constantes en la solucioacuten que se muestra soacutelo se incluyen las reglas sintaacutecticas (en el resto de la especificacioacuten no hay modificacio-nes)

void unaExpresion() boolean esCte

expresion() ltEOFgt

esCte = expresion() ltEOFgt

if (esCte) Systemoutprintln(nExpresion constanten)

boolean expresion() boolean esCte esTambienCte

termino() ( + termino() )

esCte = termino() ( + esTambienCte = termino() esCte = esCte ampamp esTambienCte )

return esCte

boolean termino() boolean esCte esTambienCte

factor() ( factor() )

esCte = factor() ( esTambienCte = factor() esCte = esCte ampamp esTambienCte )

return esCte

20

boolean factor() boolean esCte

ltconstantegt return true | ltvariablegt return false | ( esCte = expresion() ) return esCte

bull Reconsideracioacuten sobre las producciones vaciacuteasAnteriormente se ha comentado que para representar una produccioacuten vaciacutea en una especificacioacuten JavaCC hay que hacer uso de los metasiacutembolos de opcionalidad para ilustrar esta idea se ha incluido como ejem-plo la especificacioacuten de una lista de paraacutemetros compuesta por cero o maacutes expresiones separadas entre siacute por el caraacutecter coma

void parametros() [ expresion() ( expresion() ) ]

Pero si se aprovecha la posibilidad de incorporar acciones sintaacutecticas se tiene una manera de especificar directamente producciones vaciacuteas para conseguirlo basta considerar una produccioacuten vaciacutea (no tiene nada en su parte derecha) acompantildeada de una accioacuten sintaacutectica cuyo bloque de coacutedigo esteacute vaciacuteo Seguacuten esta posibilidad la especificacioacuten anterior podriacutea tambieacuten escribirse asiacute

void parametros() expresion() ( expresion() ) |

En general la accioacuten sintaacutectica asociada a una produccioacuten vaciacutea no tiene por queacute ser un bloque vaciacuteo de coacutedigo por ejemplo si se quiere que el meacutetodo que analiza los paraacutemetros de una llamada devuelva la cantidad de paraacutemetros encontrados se podriacutea escribir la siguiente especificacioacuten

int parametros()

int cantidad

( expresion() cantidad = 1

( expresion() cantidad++ )

| cantidad = 0 )

return cantidad

y si no quisiera ponerse expliacutecitamente la alternativa de la produccioacuten vaciacutea podriacutea escribirse

int parametros() int cantidad = 0

21

[ expresion() cantidad++ ( expresion() cantidad++ ) ]

return cantidad

Comunicacioacuten entre los analizadores leacutexico y sintaacutecticoCada vez que el analizador sintaacutectico realiza una llamada al analizador lexicograacutefico recibe como resultado de la llamada una representacioacuten de la pieza sintaacutectica encontrada en el texto analizado En los analizadores generados por JavaCC la comunicacioacuten de la pieza se efectuacutea mediante un valor de clase Token Esta cla-se estaacute definida en el fichero Tokenjava que se obtiene en todo caso siempre con el mismo contenido cualquiera que sea la especificacioacuten proporcionada como entrada al generador

Entre los campos de la clase Token se encuentran los siguientes

public int kind

nuacutemero entero que sirve de representacioacuten interna de la pieza sintaacutectica estos nuacutemeros asociados a las piezas son asignados automaacuteticamente por JavaCC los valores considerados pueden consultar-se en el fichero generado de nombre Constantsjava

public String image

cadena que contiene de la secuencia de caracteres (lexema) que constituyen la pieza sintaacutectica co-municada

public int beginLine beginColumn endLine endColumn

posiciones ocupadas (nuacutemeros de liacutenea y columna en el fichero de entrada) por el comienzo y el fi-nal del lexema de la pieza sintaacutectica comunicada

La clase Token tiene otros campos y meacutetodos en el fichero Tokenjava se tienen comentarios des-criptivos sobre todos los campos y meacutetodos que forman parte de la clase todos los campos de la clase se declaran como public

Ya se ha comentado anteriormente que cuando un meacutetodo de anaacutelisis sintaacutectico (asociado a un siacutembolo no terminal) tiene tipo el valor devuelto tras una llamada se puede almacenar en una variable por si se desea utilizar en alguna accioacuten sintaacutectica para ello se escribe

valor = nombreSimbolo()

(siendo valor el nombre de la variable y nombreSimbolo el nombre del siacutembolo no terminal) De manera anaacuteloga el objeto de la clase Token representativo de la pieza sintaacutectica encontrada en la entrada por el analizador lexicograacutefico puede asignarse a una variable del mismo tipo para tener accesibles las ca -racteriacutesticas de la pieza por si se precisaran en alguna accioacuten sintaacutectica para ello en la parte de la especifi -cacioacuten sintaacutectica donde aparezca la pieza sintaacutectica se escribe

dato = lt nombrePieza gt

donde nombrePieza es el nombre de la pieza sintaacutectica y dato es el nombre de una variable de tipo Token en la que se anota el valor indicativo de las propiedades de la pieza sintaacutectica comunicada

Tambieacuten existe otra manera de acceder a las caracteriacutesticas de la pieza sintaacutectica que el analizador lexico -graacutefico comunica al analizador sintaacutectico en la clase del analizador sintaacutectico se tiene declarado el campo

[ static ] public Token token

22

cada vez que el analizador sintaacutectico recibe una pieza sintaacutectica comunicada por el analizador lexicograacutefico se deja anotada en el campo token y ese contenido no se altera hasta que sea proporcionada la siguiente pieza de la entrada

∆ Ejemplo 4

Se considera una ampliacioacuten del ejemplo de presentacioacuten ademaacutes del anaacutelisis de la entrada se pretende obtener una relacioacuten numerada de las variables y las constantes indicando el nuacutemero de liacutenea y el nuacuteme-ro de columna donde estaacuten situadas por ejemplo si en la entrada se tiene la expresioacuten (grabada en dos liacuteneas)

x (y + 11) + 22

en la salida se tendraacute

1- x linea 1 columna 1

2- y linea 1 columna 6

3- 11 linea 1 columna 10

4- 22 linea 2 columna 5

En la especificacioacuten que se propone como solucioacuten puede apreciarse- en la clase del analizador sintaacutectico se han incluido meacutetodos auxiliares en los que se usan los nom-bres image beginLine beginColumn que son campos de un objeto de clase Token

- en la especificacioacuten sintaacutectica de factor se emplea la variable pieza de tipo Token para reco-ger las caracteriacutesticas de las piezas sintaacutecticas detectadas- la variable numero hace de contador para numerar las constantes y variables

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin

static int numero = 0

private static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

private static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

private static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

23

PARSER_END (ExprMin)

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() Token pieza pieza = ltconstantegt numero++ grabarDatosPieza(numero pieza) | pieza = ltvariablegt numero++ grabarDatosPieza(numero pieza) | ( expresion() )

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP | n | t | r

Si se agrupan las dos acciones sintaacutecticas comunes la especificacioacuten de factor tambieacuten puede escri-birse de esta forma

void factor() Token pieza ( pieza = ltconstantegt | pieza = ltvariablegt

) numero++ grabarDatosPieza(numero pieza)

| ( expresion() )

24

Si se accede al campo token de la clase del analizador sintaacutectico se puede resolver este ejemplo pres-cindiendo de la variable pieza

void factor() ( ltconstantegt | ltvariablegt

) numero++ grabarDatosPieza(numero token)

| ( expresion() )

Tareas asociadas a las piezas sintaacutecticasYa se ha visto que es posible asociar bloques de coacutedigo Java a puntos de la estructura sintaacutectica para que se realicen ciertas operaciones cuando el analizador sintaacutectico pase por esa zona de la estructura Tambieacuten es posible asociar coacutedigo Java a las piezas sintaacutecticas para que el analizador lexicograacutefico efectuacutee determina-das tareas cuando detecte en la entrada analizada piezas que tengan asociado coacutedigo

loz Accioacuten ligada a una pieza sintaacutecticaSi se asocia un bloque de coacutedigo a una pieza sintaacutectica se consigue que cada vez que se detecta en la en-trada analizada la presencia de esa pieza se ejecute en ese preciso momento el coacutedigo del bloque asociado a la pieza Estas acciones se denominan ldquoacciones lexicograacuteficasrdquo (lexical actions) el coacutedigo del bloque se traslada literalmente al coacutedigo del analizador lexicograacutefico generado para que se ejecute oportunamente cuando se detecte la pieza ligada al bloque de coacutedigo La accioacuten lexicograacutefica se ejecuta inmediatamente antes que la pieza sea comunicada al analizador sintaacutectico

Se puede asociar una accioacuten lexicograacutefica a una pieza sintaacutectica declarada con TOKEN y tambieacuten a una se-cuencia de caracteres declarada con SKIP ademaacutes de eacutestas hay otras dos formas de declaraciones lexico-graacuteficas (no mencionadas en esta introduccioacuten) que tambieacuten pueden llevar una accioacuten asociada

En una especificacioacuten JavaCC la forma de una pieza sintaacutectica (o bien de una secuencia de caracteres) se define poniendo su descripcioacuten delimitada entre llaves detraacutes de la indicacioacuten TOKEN (o bien SKIP) si se quiere asociar una accioacuten lexicograacutefica a la pieza (o a la secuencia) hay que colocar el bloque de coacutedigo de la accioacuten a continuacioacuten de la descripcioacuten de la forma de la pieza (o de la secuencia) el bloque (delimi-tado por llaves) queda situado entre la descripcioacuten y la llave que cierra la especificacioacuten

La pieza sintaacutectica especial que representa el final del fichero (EOF) tambieacuten puede llevar asociada una ac-cioacuten lexicograacutefica para ello hay que poner

ltgt TOKEN lt EOF gt coacutedigo de la accioacuten asociada

para justificar la presencia de los caracteres ltgt delante de la palabra TOKEN hay que basarse en el con-cepto de ldquoestado lexicograacuteficordquo (cuya explicacioacuten queda fuera del alcance de esta introduccioacuten)

∆ Ejemplo 5

Se considera nuevamente el ejemplo de presentacioacuten como resultado del anaacutelisis de una expresioacuten se pretende obtener una relacioacuten de los pareacutentesis contenidos en la expresioacuten en el mismo orden en que se encuentran ademaacutes por cada salto de liacutenea del texto de entrada se grabaraacute en la salida el caraacutecter y

25

se pasaraacute a una nueva liacutenea tambieacuten se anotaraacute la frase Final del trabajo cuando se alcance el final del fichero de entrada Asiacute por ejemplo si la entrada analizada es

(a + b) (( x )) (y + 7)

los resultados obtenidos seraacuten

() (())()

Final del trabajo

En la especificacioacuten JavaCC que se propone como solucioacuten soacutelo se muestran las reglas sintaacutecticas y le -xicograacuteficas (en el resto no hay cambios) en esta solucioacuten puede apreciarse- las piezas sintaacutecticas correspondientes a los pareacutentesis se han definido de manera nominal para po-der asociarles una accioacuten lexicograacutefica- los caracteres incluidos en la pieza de tipo SKIP se han descompuesto en dos grupos ya que la ac-cioacuten lexicograacutefica incorporada soacutelo afecta a uno de ellos

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ltpAbrirgt expresion() ltpCerrargt

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

TOKEN

26

lt pAbrir ( gt Systemoutprint(()

TOKEN lt pCerrar ) gt Systemoutprint())

ltgtTOKEN lt EOF gt Systemoutprintln ( Final del trabajo )

SKIP n Systemoutprint( n)

SKIP | t | r

loz Bloque de declaraciones lexicograacuteficasCada definicioacuten de una pieza sintaacutectica puede llevar asociada una accioacuten lexicograacutefica que se ejecuta in-mediatamente despueacutes de detectar en la entrada un lexema que se ajusta a la definicioacuten de la pieza cada accioacuten lexicograacutefica es un bloque de coacutedigo Java que puede contener sus propias declaraciones Pero a ve-ces interesa tener declaraciones que sean compartidas por todas las acciones lexicograacuteficas con indepen-dencia de la pieza a la que esteacute asociada cada una de ellas

Para incluir declaraciones que puedan usarse desde el coacutedigo asociado a cualquiera de las piezas sintaacutecti -cas se dispone de una construccioacuten con el siguiente formato

TOKEN_MGR_DECLS

que consta de una palabra reservada seguida del siacutembolo dos puntos seguido de un bloque de coacutedigo Java (delimitado por llaves) esta declaracioacuten se puede colocar en cualquier parte de una especificacioacuten JavaCC si se incluye soacutelo puede ponerse una vez

Todas las declaraciones de variables o de meacutetodos puestas en el bloque de declaraciones lexicograacuteficas compartidas son accesibles desde el coacutedigo de cualquier accioacuten lexicograacutefica la justificacioacuten es sencilla el bloque de declaraciones se traslada al principio del coacutedigo del analizador lexicograacutefico generado y las ac-ciones lexicograacuteficas tambieacuten se trasladan a ese analizador

loz Declaraciones lexicograacuteficas predefinidasDe lo expuesto hasta ahora se aprecia que en una accioacuten lexicograacutefica asociada a una pieza sintaacutectica re-sultan accesibles

- las declaraciones de su propio bloque (si tiene declaraciones)- los componentes del bloque de declaraciones compartidas (si estaacute incluido en la especificacioacuten)

Ademaacutes de esto se tienen otras declaraciones pertenecientes a la clase TokenManager que constituye el analizador lexicograacutefico que tambieacuten son utilizables desde cualquiera de las acciones lexico-graacuteficas estas declaraciones pueden considerarse como ldquopredefinidasrdquo ya que son incorporadas automaacuteti-camente por el generador Entre estas declaraciones accesibles se encuentran las siguientes

[ static ] StringBuffer imagecontiene la cadena de caracteres representativa de la pieza sintaacutectica actual que se acaba de recono-cer en le entrada se trata de un campo de la clase y se declara como static cuando el analizador

27

se ha generado con la opcioacuten (por defecto) Static = true si se generase con la opcioacuten Sta-tic = false no quedariacutea declarada como static

Token matchedToken

variable de clase Token que representa la pieza sintaacutectica actual la que se acaba de encontrar en la entrada se trata de una variable local del meacutetodo al que se ha trasladado el coacutedigo de la accioacuten (y por ello accesible desde ese coacutedigo)

El campo image puede usarse tanto si la accioacuten lexicograacutefica estaacute asociada a una pieza declarada con TOKEN como si lo estaacute a una secuencia declarada con SKIP Sin embargo la variable matchedToken no puede usarse en una accioacuten asociada a un patroacuten especificado como SKIP el motivo es que lo declara-do como SKIP no constituye propiamente una pieza sintaacutectica ya que no se va a comunicar al analizador sintaacutectico

∆ Ejemplo 6

Se propone aquiacute una nueva solucioacuten para el ejemplo nordm 4 expuesto anteriormente

Esta nueva solucioacuten tal y como se expone aquiacute admite algunas mejoras ya que su pretensioacuten soacutelo es ser-vir de ilustracioacuten para algunos aspectos que se acaban de citar (maacutes adelante se propone otra solucioacuten al-ternativa)

En la especificacioacuten de la solucioacuten es posible apreciar esto- las acciones lexicograacuteficas pueden llevar sus propias declaraciones las variables nLineaVar nColumnaVar nLineaCte y nColumnaCte se declaran dentro de las acciones

- la variable matchedToken a la que se hace referencia en el coacutedigo de la accioacuten lexicograacutefica aso-ciada a una variable contiene la informacioacuten sobre la pieza sintaacutectica que se acaba de detectar una va-riable de la expresioacuten analizada anaacutelogamente con la referencia relativa a una constante- el nombre image que aparece en las acciones lexicograacuteficas se refiere al campo de la clase Expr-MinTokenManager (analizador lexicograacutefico) noacutetese que en efecto se comunica como un paraacuteme-tro de clase StringBuffer

- la variable numero se emplea para numerar las constantes y variables encontradas

- los nombres beginLine y beginColumn son campos (puacuteblicos) de la clase Token por ello se pueden aplicar a un objeto de esa clase

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

void unaExpresion()

28

expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n StringBuffer lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

TOKEN lt variable [a-z] gt

int nLineaVar nColumnaVar numero++ nLineaVar = matchedTokenbeginLine nColumnaVar = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaVar nColumnaVar)

TOKEN lt constante ( [0-9] ) + gt

int nLineaCte nColumnaCte

29

numero++ nLineaCte = matchedTokenbeginLine nColumnaCte = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaCte nColumnaCte)

SKIP | n | t | r

La especificacioacuten precedente se puede escribir de otra manera maacutes escueta como se indica a continua-cioacuten (soacutelo se muestran las modificaciones) sobre esta nueva especificacioacuten puede mencionarse- se han eliminado por innecesarias las variables declaradas dentro de las acciones lexicograacuteficas- el nombre image que aparece en el bloque de las declaraciones compartidas se refiere al campo de la clase Token noacutetese que en efecto se aplica a un objeto de esa clase y se comunica como un paraacute -metro de tipo String

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

TOKEN lt variable [a-z] gt

numero++ grabarDatosPieza(numero matchedToken)

TOKEN lt constante ( [0-9] ) + gt

numero++ grabarDatosPieza(numero matchedToken)

loz Accioacuten lexicograacutefica comuacutenSi hubiera que realizar la misma tarea tras la deteccioacuten de todas y cada una de las piezas sintaacutecticas una posibilidad incoacutemoda seriacutea repetir el mismo coacutedigo en todas las acciones lexicograacuteficas para evitar tanta reiteracioacuten se tiene la opcioacuten Common_Token_Action su valor por defecto es false pero si se le asigna el valor true es posible definir una ldquoaccioacuten lexicograacutefica comuacutenrdquo que se escribe una uacutenica vez y se aplica a todas las piezas sintaacutecticas

30

Cuando la opcioacuten Common_Token_Action se establece con el valor true es obligado incluir el blo-que de declaraciones lexicograacuteficas compartidas (TOKEN_MGR_DECLS) Ademaacutes en ese bloque se preci-sa la definicioacuten de un meacutetodo cuyo encabezamiento sea

[ static ] void CommonTokenAction(Token t)

el coacutedigo de este meacutetodo constituye la accioacuten lexicograacutefica comuacuten que se ejecutaraacute despueacutes de la deteccioacuten de cualquier pieza sintaacutectica el meacutetodo tiene un paraacutemetro de tipo Token mediante el que se puede co-municar la informacioacuten correspondiente a la pieza que se acaba de encontrar en la entrada analizada

Si una pieza sintaacutectica tiene asociada una accioacuten lexicograacutefica propia la accioacuten lexicograacutefica comuacuten (si es-taacute definida) se ejecutaraacute inmediatamente despueacutes de realizar la accioacuten propia

La accioacuten lexicograacutefica comuacuten se aplica a todas las piezas sintaacutecticas definidas como TOKEN bien sea no-minalmente (en las reglas lexicograacuteficas) o bien anoacutenimamente (incorporadas dentro de las reglas sintaacutecti-cas) sin embargo no se aplica cuando la secuencia acoplada se corresponde con una expresioacuten regular es-pecificada como SKIP (noacutetese que por el contrario una especificacioacuten SKIP siacute puede tener asociada una accioacuten lexicograacutefica propia)

El coacutedigo del meacutetodo CommonTokenAction se traslada como un componente maacutes del bloque de decla-raciones compartidas (TOKEN_MGR_DECLS) al principio de la clase del analizador lexicograacutefico

∆ Ejemplo 7

Se pretende ahora ampliar el ejemplo de presentacioacuten con la obtencioacuten de una relacioacuten numerada de los lexemas de las piezas sintaacutecticas encontradas en la entrada analizada en el caso de las variables y de las constantes antes del lexema se mostraraacute un indicativo (Var Const) por ejemplo si la expresioacuten de la entrada es

( a + 987 ) b

se grabaraacute la salida

( 1Var a 2+ 3Const 987 4) 5 6Var b 7 8

En la solucioacuten que se propone a continuacioacuten se muestran la definicioacuten de las opciones y las reglas sin-taacutecticas y lexicograacuteficas (en el resto no hay modificaciones) puede resaltarse lo siguiente- las llamadas al meacutetodo CommonTokenAction se incorporan automaacuteticamente no hay que incluir-las en la especificacioacuten como paraacutemetro de llamada a este meacutetodo siempre se emplea el objeto que re -presenta la pieza que se acaba de detectar- la accioacuten lexicograacutefica comuacuten realiza la actualizacioacuten del contador de piezas y la grabacioacuten del lexe-ma de la pieza detectada con su nuacutemero de orden- la accioacuten lexicograacutefica comuacuten tambieacuten se aplica a las piezas sintaacutecticas anoacutenimas (pareacutentesis y ope-radores) y a la pieza especial que representa el final del fichero (EOF)

- las piezas sintaacutecticas ltconstantegt y ltvariablegt tienen asociada una accioacuten lexicograacutefica propia que se ejecuta antes de la accioacuten lexicograacutefica comuacuten- los caracteres que se saltan (SKIP) no estaacuten afectados por la accioacuten lexicograacutefica comuacuten

options Ignore_Case = true Common_Token_Action = true

31

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarIndicativo(String indicativo) Systemoutprint(indicativo + )

static void grabarLexema (int num String lexema) Systemoutprint(lexema + + num + n)

static void CommonTokenAction(Token pieza) numero++ grabarLexema(numero piezaimage)

TOKEN lt variable [a-z] gt

grabarIndicativo(Var)

32

TOKEN lt constante ( [0-9] ) + gt

grabarIndicativo(Const)

SKIP | n | t | r

A modo de ejemplo de aplicacioacuten de la accioacuten lexicograacutefica comuacuten en lo que sigue se ofrece una solucioacuten para la grabacioacuten (en el fichero predefinido de salida) del texto analizado con las liacuteneas numeradas

∆ Ejemplo 8 [ Listado del texto analizado ]Se desea completar el ejemplo de presentacioacuten con la grabacioacuten de un listado con las liacuteneas numeradas del texto analizado (noacutetese que una expresioacuten puede escribirse ocupando varias liacuteneas) No se muestra la especificacioacuten sintaacutectica debido a que no hay ninguna modificacioacuten en ella Los aspectos resentildeables de la solucioacuten que se propone son- se usa la variable numLin como contador de liacuteneas

- en la variable linea se van yuxtaponiendo los lexemas de las piezas sintaacutecticas de una liacutenea esta yuxtaposicioacuten se realiza mediante la accioacuten lexicograacutefica comuacuten- las secuencias reconocidas con los patrones especificados como SKIP no implican la ejecucioacuten de la accioacuten lexicograacutefica comuacuten por ello su lexema (almacenado en la variable image) ha de yuxtaponer-se expliacutecitamente- hay que separar el patroacuten correspondiente al fin de liacutenea para detectar el momento en que ha de gra-barse la liacutenea completa cuyo final se ha alcanzado

options Ignore_Case = true Common_Token_Action = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(nnAnalisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

TOKEN_MGR_DECLS static int numLin = 0 static String linea =

33

static void CommonTokenAction(Token pieza) linea = linea + piezaimage

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP lt | t | r gt linea = linea + image

SKIP n numLin++ Systemoutprintln(numLin + + linea) linea =

A modo de recapitulacioacutenↄ Accioacuten lexicograacutefica Accioacuten sintaacutectica

bull accioacuten lexicograacutefica bloque de coacutedigo Java asociado a una pieza sintaacutectica nominal (TOKEN) o a una secuencia de caracteres (SKIP) se ejecuta cuando se detecta en la entrada la pieza o la secuencia corres-pondiente no se puede aplicar a piezas sintaacutecticas anoacutenimas (autodefinidas con comillas)bull accioacuten sintaacutectica bloque de coacutedigo Java asociado a un punto de la estructura sintaacutectica (el punto indica-do por el sitio de la produccioacuten donde se inserta el bloque) se ejecuta cuando el anaacutelisis de la entrada pa-sa por ese punto de la estructura

ↄ Valor de una pieza sintaacutectica comunicadaEl analizador lexicograacutefico comunica al analizador sintaacutectico una pieza a traveacutes de un valor de la clase Token (fichero Tokenjava) un objeto de la clase Token representa las caracteriacutesticas de la pieza comunicada En la clase Token entre otros se tienen los campos

String imagelexema de la pieza

int beginLine beginColumn endLine endColumnposicioacuten del lexema en el fichero analizado (indicada por los nuacutemeros de fila y de columna de su comienzo y de su terminacioacuten)

ↄ Valor asociado a un siacutembolo

34

En las producciones que especifican la sintaxis se pueden incluir asignaciones que sirven para dejar ano-tado el valor asociado a un siacutembolo

bull siacutembolo no terminal valor = nombreSimbolo()el meacutetodo de anaacutelisis asociado a nombreSimbolo se ha declarado de un cierto tipo (distinto del tipo void) valor ha de ser una variable del mismo tipo que el meacutetodo

bull siacutembolo terminal dato = lt nombrePieza gtel valor asignado es el valor de la clase Token representativo de la pieza sintaacutectica (nominal) dato ha de ser una variable de tipo Token

ↄ Miradas sobre la pieza sintaacutectica comunicadaLa pieza sintaacutectica comunicada puede mirarse desde cada uno de los dos analizadores

bull analizador sintaacutecticola pieza comunicada (desde el analizador lexicograacutefico) se tiene anotada en un campo de la clase del analizador sintaacutectico declarado como

(static) public Token token

este valor es accesible desde el coacutedigo de todos los meacutetodos de anaacutelisis sintaacutectico el contenido del campo variacutea cuando se recibe una nueva pieza sintaacutectica

bull analizador lexicograacuteficola pieza dispuesta para ser comunicada (al analizador sintaacutectico) se tiene anotada en una variable local declarada como

Token matchedToken

este valor es accesible para el coacutedigo de todas las acciones lexicograacuteficas

ↄ El lexema de la pieza comunicadaEl lexema de la pieza sintaacutectica comunicada se encuentra disponible en dos sitios distintos que compar-ten el mismo nombre pero que son de distinto tipo y se usan de distinta manera

bull String image

es un campo de la clase TOKEN puede consultarse en relacioacuten con un objeto de esa clase que esteacute disponible

bull StringBuffer image

es un campo de la clase del analizador lexicograacutefico ( ∙ ∙ ∙ ∙ TokenManagerjava) es accesible desde el coacutedigo de la clase y por lo tanto desde todas las acciones lexicograacuteficas (bloques de coacute-digo que estaacuten incorporados al analizador lexicograacutefico)

se trata de un valor disponible tanto para las piezas sintaacutecticas nominales (TOKEN) como para las secuencias que se saltan (SKIP)

ↄ Resumen relativo a la lexicografiacutea

TOKEN pieza sintaacutectica nominal∙ ∙ ∙ pieza sintaacutectica anoacutenimaSKIP secuencia de caracteres que no forman pieza

35

Precisiones sobre el analizador sintaacutectico generadoPuede decirse que en su funcionamiento maacutes sencillo cuando no se emplean las posibilidades de examen por adelantado (ldquolookaheadrdquo) JavaCC comprueba que las reglas sintaacutecticas cumplen la condicioacuten LL(1) y generan un analizador descendente-predictivo-recursivo Pero esta apreciacioacuten hay que matizarla ya que el analizador sintaacutectico generado no se ajusta en sentido estricto al modelo de analizador LL(1)

En el modelo de anaacutelisis LL(1) el orden del examen de las distintas alternativas de expansioacuten de un siacutembo-lo no terminal es indiferente ya que se conocen los siacutembolos directores de cada una de ellas (que resultan ser conjuntos disjuntos) en la implementacioacuten del subprograma de anaacutelisis asociado al siacutembolo no terminal ltNombreSimbgt cuyas producciones son

ltNombreSimbgt = α1

| α2

∙ ∙ ∙

| αn

se podriacutea preguntar en cualquier orden si la pieza actual pertenece al conjunto de siacutembolos directores de las distintas reglas Sin embargo en el analizador sintaacutectico generado por JavaCC la implementacioacuten del meacute-todo asociado al siacutembolo no terminal ltNombreSimbgt no sigue esa pauta

Si las anteriores reglas sintaacutecticas se transcriben en JavaCC de la forma

void nombreSimb () α1-jcc

| α2-jcc

∙ ∙ ∙

| αn-jcc

1

accioacutenlexicograacutefica

comuacuten

Commom -TokenAction2

accioacutenlexicograacutefica

propia

(accioacuten ligadaa una pieza)3

bloque dedeclaracioneslexicograacuteficas

TOKEN_MGR_DECLS4

declaracioneslexicograacuteficaspredefinidas

(disponibilidad

de uso)TOKENsisisisi∙ ∙ ∙sino--SKIPnosisisi

Las indicaciones si de las columnas 3 y 4 son consecuencia de las indicaciones de la columna 2

La accioacuten lexicograacutefica comuacuten se ejecuta despueacutes de la accioacuten lexicograacutefica propia

36

el meacutetodo de anaacutelisis generado procede de la siguiente manera

se van considerando las alternativas sucesivamente en el mismo orden en el que estaacuten especifica-das primero la de α1 (α1-jcc) despueacutes la de α2 (α2-jcc) etc

para cada alternativa seleccionada se comprueba si la pieza por adelantado coincide con alguno de los siacutembolos iniciales de la parte derecha y si hay coincidencia se prosigue el anaacutelisis con ella si no hay coincidencia se pasa a la alternativa siguiente

siguiendo con este orden si se llega al final de las alternativas sin haberse encontrado coincidencia alguna se produce un error sintaacutectico

En el analizador generado por JavaCC hay una peculiaridad que no se tiene en los analizadores LL(1) es-trictamente considerados la palabra vaciacutea siacute se considera como posible siacutembolo inicial de la parte derecha una regla (esto ocurre siempre que la parte derecha es anulable) De esta peculiaridad se deriva una conse-cuencia que ha de tenerse en cuenta al escribir la especificacioacuten dado que siempre es posible considerar la palabra vaciacutea como la siguiente pieza por adelantado si se llega a examinar la alternativa correspondiente a una produccioacuten anulable el analizador generado siempre seleccionaraacute esa alternativa con independencia de la siguiente pieza que esteacute presente en la entrada que queda por analizar

Asiacute pues si alguna de las producciones que definen un siacutembolo no terminal es anulable es importante el or-den en que se escriben las alternativas en una especificacioacuten sintaacutectica JavaCC el funcionamiento del ana-lizador generado siacute depende de la colocacioacuten elegida para las reglas

Para ilustrar estas peculiaridades del analizador sintaacutectico generado por JavaCC a continuacioacuten se expone un ejemplo

∆ Ejemplo 9

Se quiere obtener un analizador leacutexico-sintaacutectico de textos que se ajustan al siguiente formato una se-cuencia de uno o maacutes nombres seguida del siacutembolo igual seguido de un nuacutemero entero si la secuencia tiene maacutes de un nombre han de estar separados bien por una coma en todos los casos o bien por el siacutem-bolo dos puntos en todos los casos esto es son correctas las secuencias

uno dos tres = 123exclusivo = 0Abajo Arriba = 57

y son incorrectas las secuenciasuno dos tres = 123

exclusivo inclusive = 0

fuera dentro = 56

Una gramaacutetica que define las secuencias con este formato es

ltListagt = nombre ltOtrosNombresgt = numero

ltOtrosNombresgt = nombre nombre | nombre nombre | ε

se trata de una gramaacutetica que cumple la condicioacuten LL(1)- el siacutembolo director para la alternativa vaciacutea es el siacutembolo igual es evidente pues que las tres reglas de ltOtrosNombresgt tienen siacutembolos directores distintos

37

- la decisioacuten de abandonar la repeticioacuten del anaacutelisis de un nuevo nombre (en las dos primeras alterna-tivas de ltOtrosNombresgt) se puede tomar ante la presencia del siacutembolo igual

En la implementacioacuten de un analizador sintaacutectico LL(1) en sentido estricto la seleccioacuten de la alternativa vaciacutea para expandir el siacutembolo ltOtrosNombresgt se hariacutea tras comprobar expliacutecitamente que la pie-za sintaacutectica por adelantado es el siacutembolo igual

El funcionamiento del analizador generado por JavaCC depende del orden de colocacioacuten de las reglas A continuacioacuten se exponen varias posibilidades de especificacioacuten y se comenta el analizador generado en cada caso

bull Especificacioacuten primeraSe transcriben literalmente las reglas tal y como se han escrito antes en la notacioacuten BNF noacutetese que para poder codificar la parte derecha de la regla vaciacutea ha de incluirse una accioacuten sintaacutectica vaciacutea

PARSER_BEGIN (Igualdad)

public class Igualdad

public static void main (String[] argum) throws ParseException Igualdad analisis = new Igualdad(Systemin) analisissecuencia() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (Igualdad)

void secuencia() lista() ltEOFgt

void lista() lt nombre gt otrosNombres() = lt numero gt

void otrosNombres() lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt ) |

TOKEN lt nombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN lt numero ( [ 0 - 9 ] )+ gt

SKIP | r | n

38

El analizador generado funciona correctamente puesto que en el meacutetodo otrosNombres se selec-ciona la alternativa vaciacutea despueacutes de haber comprobado que la pieza por adelantado no es siacutembolo ini-cial de ninguna de las dos alternativas precedentes (no es una coma ni un siacutembolo dos puntos)

bull Especificacioacuten segundaSe considera la misma especificacioacuten anterior pero cambiando el orden de colocacioacuten de las reglas ahora la produccioacuten vaciacutea se coloca como primera alternativa (soacutelo se muestran las partes modifica-das)

void otrosNombres() | lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt )

Con la entrada de esta especificacioacuten el generador JavaCC produce un mensaje en el que se avisa de que las dos alternativas que estaacuten colocadas detraacutes de la alternativa vaciacutea nunca podraacuten seleccionarse aun asiacute se genera un analizador Pero es un analizador que funciona incorrectamente asiacute por ejemplo la entrada

mxa nyz = 57

se considera incorrecta despueacutes de visto el primer nombre se intenta aplicar la produccioacuten vaciacutea (se espera encontrar el siacutembolo igual) pero habriacutea que aplicar la regla cuyo siacutembolo director es la coma

bull Especificacioacuten terceraSe utilizan las posibilidades de la notacioacuten BNF-Ampliada para prescindir de la especificacioacuten expliacuteci-ta de la palabra vaciacutea se aprovecha que JavaCC admite el operador + para indicar repeticiones de una o maacutes veces la alternativa vaciacutea estaacute impliacutecitamente contemplada en la posibilidad de cero repeti-ciones que contempla el operador (soacutelo se muestran las partes modificadas en la especificacioacuten)

void otrosNombres() ( lt nombre gt )+ | ( lt nombre gt )

El meacutetodo otrosNombres() generado hace las comprobaciones en el siguiente orden

- se comprueba si la pieza por adelantado es una coma (siacutembolo inicial de la primera alternativa) y en este caso se selecciona la primera regla- si no es una coma se comprueba si es un siacutembolo dos puntos (siacutembolo inicial de la segunda alter-nativa) y en este caso se selecciona la segunda regla- si no es un siacutembolo dos puntos se considera la palabra vaciacutea como siacutembolo inicial (siempre posi-ble impliacutecitamente) para la segunda alternativa en este caso para que no haya error la pieza por adelantado ha de ser un siacutembolo igual

Se trata pues de un analizador que funciona correctamente

bull Especificacioacuten cuarta

39

Se pone la misma especificacioacuten anterior pero cambiando el orden de consideracioacuten de la regla vaciacutea ahora se incluye impliacutecitamente en la primera alternativa (soacutelo se muestran las partes modificadas)

void otrosNombres() ( lt nombre gt ) | ( lt nombre gt )+

Para esta especificacioacuten el generador JavaCC emite un aviso en el que se indica que la segunda alter -nativa nunca se seleccionaraacute no obstante se genera un analizador que no funciona como se supone que deberiacutea hacerlo por ejemplo el anaacutelisis de la entrada

nyz mxa = 1

produce un error despueacutes de tratado el primer nombre se intenta aplicar la produccioacuten vaciacutea (corres-pondiente a la repeticioacuten de cero veces contemplada en la primera regla) por lo que no se encuentra el esperado siacutembolo igual

40

Page 21: JavaCC

int factor() int n = 0

( ltconstantegt | ltvariablegt | ( n = expresion() ) )

return n

A continuacioacuten se expone un nuevo ejemplo para ilustrar la utilidad que proporciona el valor devuelto por un meacutetodo de anaacutelisis sintaacutectico para la propagacioacuten (ascendente) de atributos (propiedades de las entida-des analizadas)

∆ Ejemplo 3

Se pretende ampliar el analizador del ejemplo de presentacioacuten para que se emita un mensaje indicativo cuando la expresioacuten analizada sea una expresioacuten en la que soacutelo aparecen constantes en la solucioacuten que se muestra soacutelo se incluyen las reglas sintaacutecticas (en el resto de la especificacioacuten no hay modificacio-nes)

void unaExpresion() boolean esCte

expresion() ltEOFgt

esCte = expresion() ltEOFgt

if (esCte) Systemoutprintln(nExpresion constanten)

boolean expresion() boolean esCte esTambienCte

termino() ( + termino() )

esCte = termino() ( + esTambienCte = termino() esCte = esCte ampamp esTambienCte )

return esCte

boolean termino() boolean esCte esTambienCte

factor() ( factor() )

esCte = factor() ( esTambienCte = factor() esCte = esCte ampamp esTambienCte )

return esCte

20

boolean factor() boolean esCte

ltconstantegt return true | ltvariablegt return false | ( esCte = expresion() ) return esCte

bull Reconsideracioacuten sobre las producciones vaciacuteasAnteriormente se ha comentado que para representar una produccioacuten vaciacutea en una especificacioacuten JavaCC hay que hacer uso de los metasiacutembolos de opcionalidad para ilustrar esta idea se ha incluido como ejem-plo la especificacioacuten de una lista de paraacutemetros compuesta por cero o maacutes expresiones separadas entre siacute por el caraacutecter coma

void parametros() [ expresion() ( expresion() ) ]

Pero si se aprovecha la posibilidad de incorporar acciones sintaacutecticas se tiene una manera de especificar directamente producciones vaciacuteas para conseguirlo basta considerar una produccioacuten vaciacutea (no tiene nada en su parte derecha) acompantildeada de una accioacuten sintaacutectica cuyo bloque de coacutedigo esteacute vaciacuteo Seguacuten esta posibilidad la especificacioacuten anterior podriacutea tambieacuten escribirse asiacute

void parametros() expresion() ( expresion() ) |

En general la accioacuten sintaacutectica asociada a una produccioacuten vaciacutea no tiene por queacute ser un bloque vaciacuteo de coacutedigo por ejemplo si se quiere que el meacutetodo que analiza los paraacutemetros de una llamada devuelva la cantidad de paraacutemetros encontrados se podriacutea escribir la siguiente especificacioacuten

int parametros()

int cantidad

( expresion() cantidad = 1

( expresion() cantidad++ )

| cantidad = 0 )

return cantidad

y si no quisiera ponerse expliacutecitamente la alternativa de la produccioacuten vaciacutea podriacutea escribirse

int parametros() int cantidad = 0

21

[ expresion() cantidad++ ( expresion() cantidad++ ) ]

return cantidad

Comunicacioacuten entre los analizadores leacutexico y sintaacutecticoCada vez que el analizador sintaacutectico realiza una llamada al analizador lexicograacutefico recibe como resultado de la llamada una representacioacuten de la pieza sintaacutectica encontrada en el texto analizado En los analizadores generados por JavaCC la comunicacioacuten de la pieza se efectuacutea mediante un valor de clase Token Esta cla-se estaacute definida en el fichero Tokenjava que se obtiene en todo caso siempre con el mismo contenido cualquiera que sea la especificacioacuten proporcionada como entrada al generador

Entre los campos de la clase Token se encuentran los siguientes

public int kind

nuacutemero entero que sirve de representacioacuten interna de la pieza sintaacutectica estos nuacutemeros asociados a las piezas son asignados automaacuteticamente por JavaCC los valores considerados pueden consultar-se en el fichero generado de nombre Constantsjava

public String image

cadena que contiene de la secuencia de caracteres (lexema) que constituyen la pieza sintaacutectica co-municada

public int beginLine beginColumn endLine endColumn

posiciones ocupadas (nuacutemeros de liacutenea y columna en el fichero de entrada) por el comienzo y el fi-nal del lexema de la pieza sintaacutectica comunicada

La clase Token tiene otros campos y meacutetodos en el fichero Tokenjava se tienen comentarios des-criptivos sobre todos los campos y meacutetodos que forman parte de la clase todos los campos de la clase se declaran como public

Ya se ha comentado anteriormente que cuando un meacutetodo de anaacutelisis sintaacutectico (asociado a un siacutembolo no terminal) tiene tipo el valor devuelto tras una llamada se puede almacenar en una variable por si se desea utilizar en alguna accioacuten sintaacutectica para ello se escribe

valor = nombreSimbolo()

(siendo valor el nombre de la variable y nombreSimbolo el nombre del siacutembolo no terminal) De manera anaacuteloga el objeto de la clase Token representativo de la pieza sintaacutectica encontrada en la entrada por el analizador lexicograacutefico puede asignarse a una variable del mismo tipo para tener accesibles las ca -racteriacutesticas de la pieza por si se precisaran en alguna accioacuten sintaacutectica para ello en la parte de la especifi -cacioacuten sintaacutectica donde aparezca la pieza sintaacutectica se escribe

dato = lt nombrePieza gt

donde nombrePieza es el nombre de la pieza sintaacutectica y dato es el nombre de una variable de tipo Token en la que se anota el valor indicativo de las propiedades de la pieza sintaacutectica comunicada

Tambieacuten existe otra manera de acceder a las caracteriacutesticas de la pieza sintaacutectica que el analizador lexico -graacutefico comunica al analizador sintaacutectico en la clase del analizador sintaacutectico se tiene declarado el campo

[ static ] public Token token

22

cada vez que el analizador sintaacutectico recibe una pieza sintaacutectica comunicada por el analizador lexicograacutefico se deja anotada en el campo token y ese contenido no se altera hasta que sea proporcionada la siguiente pieza de la entrada

∆ Ejemplo 4

Se considera una ampliacioacuten del ejemplo de presentacioacuten ademaacutes del anaacutelisis de la entrada se pretende obtener una relacioacuten numerada de las variables y las constantes indicando el nuacutemero de liacutenea y el nuacuteme-ro de columna donde estaacuten situadas por ejemplo si en la entrada se tiene la expresioacuten (grabada en dos liacuteneas)

x (y + 11) + 22

en la salida se tendraacute

1- x linea 1 columna 1

2- y linea 1 columna 6

3- 11 linea 1 columna 10

4- 22 linea 2 columna 5

En la especificacioacuten que se propone como solucioacuten puede apreciarse- en la clase del analizador sintaacutectico se han incluido meacutetodos auxiliares en los que se usan los nom-bres image beginLine beginColumn que son campos de un objeto de clase Token

- en la especificacioacuten sintaacutectica de factor se emplea la variable pieza de tipo Token para reco-ger las caracteriacutesticas de las piezas sintaacutecticas detectadas- la variable numero hace de contador para numerar las constantes y variables

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin

static int numero = 0

private static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

private static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

private static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

23

PARSER_END (ExprMin)

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() Token pieza pieza = ltconstantegt numero++ grabarDatosPieza(numero pieza) | pieza = ltvariablegt numero++ grabarDatosPieza(numero pieza) | ( expresion() )

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP | n | t | r

Si se agrupan las dos acciones sintaacutecticas comunes la especificacioacuten de factor tambieacuten puede escri-birse de esta forma

void factor() Token pieza ( pieza = ltconstantegt | pieza = ltvariablegt

) numero++ grabarDatosPieza(numero pieza)

| ( expresion() )

24

Si se accede al campo token de la clase del analizador sintaacutectico se puede resolver este ejemplo pres-cindiendo de la variable pieza

void factor() ( ltconstantegt | ltvariablegt

) numero++ grabarDatosPieza(numero token)

| ( expresion() )

Tareas asociadas a las piezas sintaacutecticasYa se ha visto que es posible asociar bloques de coacutedigo Java a puntos de la estructura sintaacutectica para que se realicen ciertas operaciones cuando el analizador sintaacutectico pase por esa zona de la estructura Tambieacuten es posible asociar coacutedigo Java a las piezas sintaacutecticas para que el analizador lexicograacutefico efectuacutee determina-das tareas cuando detecte en la entrada analizada piezas que tengan asociado coacutedigo

loz Accioacuten ligada a una pieza sintaacutecticaSi se asocia un bloque de coacutedigo a una pieza sintaacutectica se consigue que cada vez que se detecta en la en-trada analizada la presencia de esa pieza se ejecute en ese preciso momento el coacutedigo del bloque asociado a la pieza Estas acciones se denominan ldquoacciones lexicograacuteficasrdquo (lexical actions) el coacutedigo del bloque se traslada literalmente al coacutedigo del analizador lexicograacutefico generado para que se ejecute oportunamente cuando se detecte la pieza ligada al bloque de coacutedigo La accioacuten lexicograacutefica se ejecuta inmediatamente antes que la pieza sea comunicada al analizador sintaacutectico

Se puede asociar una accioacuten lexicograacutefica a una pieza sintaacutectica declarada con TOKEN y tambieacuten a una se-cuencia de caracteres declarada con SKIP ademaacutes de eacutestas hay otras dos formas de declaraciones lexico-graacuteficas (no mencionadas en esta introduccioacuten) que tambieacuten pueden llevar una accioacuten asociada

En una especificacioacuten JavaCC la forma de una pieza sintaacutectica (o bien de una secuencia de caracteres) se define poniendo su descripcioacuten delimitada entre llaves detraacutes de la indicacioacuten TOKEN (o bien SKIP) si se quiere asociar una accioacuten lexicograacutefica a la pieza (o a la secuencia) hay que colocar el bloque de coacutedigo de la accioacuten a continuacioacuten de la descripcioacuten de la forma de la pieza (o de la secuencia) el bloque (delimi-tado por llaves) queda situado entre la descripcioacuten y la llave que cierra la especificacioacuten

La pieza sintaacutectica especial que representa el final del fichero (EOF) tambieacuten puede llevar asociada una ac-cioacuten lexicograacutefica para ello hay que poner

ltgt TOKEN lt EOF gt coacutedigo de la accioacuten asociada

para justificar la presencia de los caracteres ltgt delante de la palabra TOKEN hay que basarse en el con-cepto de ldquoestado lexicograacuteficordquo (cuya explicacioacuten queda fuera del alcance de esta introduccioacuten)

∆ Ejemplo 5

Se considera nuevamente el ejemplo de presentacioacuten como resultado del anaacutelisis de una expresioacuten se pretende obtener una relacioacuten de los pareacutentesis contenidos en la expresioacuten en el mismo orden en que se encuentran ademaacutes por cada salto de liacutenea del texto de entrada se grabaraacute en la salida el caraacutecter y

25

se pasaraacute a una nueva liacutenea tambieacuten se anotaraacute la frase Final del trabajo cuando se alcance el final del fichero de entrada Asiacute por ejemplo si la entrada analizada es

(a + b) (( x )) (y + 7)

los resultados obtenidos seraacuten

() (())()

Final del trabajo

En la especificacioacuten JavaCC que se propone como solucioacuten soacutelo se muestran las reglas sintaacutecticas y le -xicograacuteficas (en el resto no hay cambios) en esta solucioacuten puede apreciarse- las piezas sintaacutecticas correspondientes a los pareacutentesis se han definido de manera nominal para po-der asociarles una accioacuten lexicograacutefica- los caracteres incluidos en la pieza de tipo SKIP se han descompuesto en dos grupos ya que la ac-cioacuten lexicograacutefica incorporada soacutelo afecta a uno de ellos

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ltpAbrirgt expresion() ltpCerrargt

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

TOKEN

26

lt pAbrir ( gt Systemoutprint(()

TOKEN lt pCerrar ) gt Systemoutprint())

ltgtTOKEN lt EOF gt Systemoutprintln ( Final del trabajo )

SKIP n Systemoutprint( n)

SKIP | t | r

loz Bloque de declaraciones lexicograacuteficasCada definicioacuten de una pieza sintaacutectica puede llevar asociada una accioacuten lexicograacutefica que se ejecuta in-mediatamente despueacutes de detectar en la entrada un lexema que se ajusta a la definicioacuten de la pieza cada accioacuten lexicograacutefica es un bloque de coacutedigo Java que puede contener sus propias declaraciones Pero a ve-ces interesa tener declaraciones que sean compartidas por todas las acciones lexicograacuteficas con indepen-dencia de la pieza a la que esteacute asociada cada una de ellas

Para incluir declaraciones que puedan usarse desde el coacutedigo asociado a cualquiera de las piezas sintaacutecti -cas se dispone de una construccioacuten con el siguiente formato

TOKEN_MGR_DECLS

que consta de una palabra reservada seguida del siacutembolo dos puntos seguido de un bloque de coacutedigo Java (delimitado por llaves) esta declaracioacuten se puede colocar en cualquier parte de una especificacioacuten JavaCC si se incluye soacutelo puede ponerse una vez

Todas las declaraciones de variables o de meacutetodos puestas en el bloque de declaraciones lexicograacuteficas compartidas son accesibles desde el coacutedigo de cualquier accioacuten lexicograacutefica la justificacioacuten es sencilla el bloque de declaraciones se traslada al principio del coacutedigo del analizador lexicograacutefico generado y las ac-ciones lexicograacuteficas tambieacuten se trasladan a ese analizador

loz Declaraciones lexicograacuteficas predefinidasDe lo expuesto hasta ahora se aprecia que en una accioacuten lexicograacutefica asociada a una pieza sintaacutectica re-sultan accesibles

- las declaraciones de su propio bloque (si tiene declaraciones)- los componentes del bloque de declaraciones compartidas (si estaacute incluido en la especificacioacuten)

Ademaacutes de esto se tienen otras declaraciones pertenecientes a la clase TokenManager que constituye el analizador lexicograacutefico que tambieacuten son utilizables desde cualquiera de las acciones lexico-graacuteficas estas declaraciones pueden considerarse como ldquopredefinidasrdquo ya que son incorporadas automaacuteti-camente por el generador Entre estas declaraciones accesibles se encuentran las siguientes

[ static ] StringBuffer imagecontiene la cadena de caracteres representativa de la pieza sintaacutectica actual que se acaba de recono-cer en le entrada se trata de un campo de la clase y se declara como static cuando el analizador

27

se ha generado con la opcioacuten (por defecto) Static = true si se generase con la opcioacuten Sta-tic = false no quedariacutea declarada como static

Token matchedToken

variable de clase Token que representa la pieza sintaacutectica actual la que se acaba de encontrar en la entrada se trata de una variable local del meacutetodo al que se ha trasladado el coacutedigo de la accioacuten (y por ello accesible desde ese coacutedigo)

El campo image puede usarse tanto si la accioacuten lexicograacutefica estaacute asociada a una pieza declarada con TOKEN como si lo estaacute a una secuencia declarada con SKIP Sin embargo la variable matchedToken no puede usarse en una accioacuten asociada a un patroacuten especificado como SKIP el motivo es que lo declara-do como SKIP no constituye propiamente una pieza sintaacutectica ya que no se va a comunicar al analizador sintaacutectico

∆ Ejemplo 6

Se propone aquiacute una nueva solucioacuten para el ejemplo nordm 4 expuesto anteriormente

Esta nueva solucioacuten tal y como se expone aquiacute admite algunas mejoras ya que su pretensioacuten soacutelo es ser-vir de ilustracioacuten para algunos aspectos que se acaban de citar (maacutes adelante se propone otra solucioacuten al-ternativa)

En la especificacioacuten de la solucioacuten es posible apreciar esto- las acciones lexicograacuteficas pueden llevar sus propias declaraciones las variables nLineaVar nColumnaVar nLineaCte y nColumnaCte se declaran dentro de las acciones

- la variable matchedToken a la que se hace referencia en el coacutedigo de la accioacuten lexicograacutefica aso-ciada a una variable contiene la informacioacuten sobre la pieza sintaacutectica que se acaba de detectar una va-riable de la expresioacuten analizada anaacutelogamente con la referencia relativa a una constante- el nombre image que aparece en las acciones lexicograacuteficas se refiere al campo de la clase Expr-MinTokenManager (analizador lexicograacutefico) noacutetese que en efecto se comunica como un paraacuteme-tro de clase StringBuffer

- la variable numero se emplea para numerar las constantes y variables encontradas

- los nombres beginLine y beginColumn son campos (puacuteblicos) de la clase Token por ello se pueden aplicar a un objeto de esa clase

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

void unaExpresion()

28

expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n StringBuffer lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

TOKEN lt variable [a-z] gt

int nLineaVar nColumnaVar numero++ nLineaVar = matchedTokenbeginLine nColumnaVar = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaVar nColumnaVar)

TOKEN lt constante ( [0-9] ) + gt

int nLineaCte nColumnaCte

29

numero++ nLineaCte = matchedTokenbeginLine nColumnaCte = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaCte nColumnaCte)

SKIP | n | t | r

La especificacioacuten precedente se puede escribir de otra manera maacutes escueta como se indica a continua-cioacuten (soacutelo se muestran las modificaciones) sobre esta nueva especificacioacuten puede mencionarse- se han eliminado por innecesarias las variables declaradas dentro de las acciones lexicograacuteficas- el nombre image que aparece en el bloque de las declaraciones compartidas se refiere al campo de la clase Token noacutetese que en efecto se aplica a un objeto de esa clase y se comunica como un paraacute -metro de tipo String

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

TOKEN lt variable [a-z] gt

numero++ grabarDatosPieza(numero matchedToken)

TOKEN lt constante ( [0-9] ) + gt

numero++ grabarDatosPieza(numero matchedToken)

loz Accioacuten lexicograacutefica comuacutenSi hubiera que realizar la misma tarea tras la deteccioacuten de todas y cada una de las piezas sintaacutecticas una posibilidad incoacutemoda seriacutea repetir el mismo coacutedigo en todas las acciones lexicograacuteficas para evitar tanta reiteracioacuten se tiene la opcioacuten Common_Token_Action su valor por defecto es false pero si se le asigna el valor true es posible definir una ldquoaccioacuten lexicograacutefica comuacutenrdquo que se escribe una uacutenica vez y se aplica a todas las piezas sintaacutecticas

30

Cuando la opcioacuten Common_Token_Action se establece con el valor true es obligado incluir el blo-que de declaraciones lexicograacuteficas compartidas (TOKEN_MGR_DECLS) Ademaacutes en ese bloque se preci-sa la definicioacuten de un meacutetodo cuyo encabezamiento sea

[ static ] void CommonTokenAction(Token t)

el coacutedigo de este meacutetodo constituye la accioacuten lexicograacutefica comuacuten que se ejecutaraacute despueacutes de la deteccioacuten de cualquier pieza sintaacutectica el meacutetodo tiene un paraacutemetro de tipo Token mediante el que se puede co-municar la informacioacuten correspondiente a la pieza que se acaba de encontrar en la entrada analizada

Si una pieza sintaacutectica tiene asociada una accioacuten lexicograacutefica propia la accioacuten lexicograacutefica comuacuten (si es-taacute definida) se ejecutaraacute inmediatamente despueacutes de realizar la accioacuten propia

La accioacuten lexicograacutefica comuacuten se aplica a todas las piezas sintaacutecticas definidas como TOKEN bien sea no-minalmente (en las reglas lexicograacuteficas) o bien anoacutenimamente (incorporadas dentro de las reglas sintaacutecti-cas) sin embargo no se aplica cuando la secuencia acoplada se corresponde con una expresioacuten regular es-pecificada como SKIP (noacutetese que por el contrario una especificacioacuten SKIP siacute puede tener asociada una accioacuten lexicograacutefica propia)

El coacutedigo del meacutetodo CommonTokenAction se traslada como un componente maacutes del bloque de decla-raciones compartidas (TOKEN_MGR_DECLS) al principio de la clase del analizador lexicograacutefico

∆ Ejemplo 7

Se pretende ahora ampliar el ejemplo de presentacioacuten con la obtencioacuten de una relacioacuten numerada de los lexemas de las piezas sintaacutecticas encontradas en la entrada analizada en el caso de las variables y de las constantes antes del lexema se mostraraacute un indicativo (Var Const) por ejemplo si la expresioacuten de la entrada es

( a + 987 ) b

se grabaraacute la salida

( 1Var a 2+ 3Const 987 4) 5 6Var b 7 8

En la solucioacuten que se propone a continuacioacuten se muestran la definicioacuten de las opciones y las reglas sin-taacutecticas y lexicograacuteficas (en el resto no hay modificaciones) puede resaltarse lo siguiente- las llamadas al meacutetodo CommonTokenAction se incorporan automaacuteticamente no hay que incluir-las en la especificacioacuten como paraacutemetro de llamada a este meacutetodo siempre se emplea el objeto que re -presenta la pieza que se acaba de detectar- la accioacuten lexicograacutefica comuacuten realiza la actualizacioacuten del contador de piezas y la grabacioacuten del lexe-ma de la pieza detectada con su nuacutemero de orden- la accioacuten lexicograacutefica comuacuten tambieacuten se aplica a las piezas sintaacutecticas anoacutenimas (pareacutentesis y ope-radores) y a la pieza especial que representa el final del fichero (EOF)

- las piezas sintaacutecticas ltconstantegt y ltvariablegt tienen asociada una accioacuten lexicograacutefica propia que se ejecuta antes de la accioacuten lexicograacutefica comuacuten- los caracteres que se saltan (SKIP) no estaacuten afectados por la accioacuten lexicograacutefica comuacuten

options Ignore_Case = true Common_Token_Action = true

31

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarIndicativo(String indicativo) Systemoutprint(indicativo + )

static void grabarLexema (int num String lexema) Systemoutprint(lexema + + num + n)

static void CommonTokenAction(Token pieza) numero++ grabarLexema(numero piezaimage)

TOKEN lt variable [a-z] gt

grabarIndicativo(Var)

32

TOKEN lt constante ( [0-9] ) + gt

grabarIndicativo(Const)

SKIP | n | t | r

A modo de ejemplo de aplicacioacuten de la accioacuten lexicograacutefica comuacuten en lo que sigue se ofrece una solucioacuten para la grabacioacuten (en el fichero predefinido de salida) del texto analizado con las liacuteneas numeradas

∆ Ejemplo 8 [ Listado del texto analizado ]Se desea completar el ejemplo de presentacioacuten con la grabacioacuten de un listado con las liacuteneas numeradas del texto analizado (noacutetese que una expresioacuten puede escribirse ocupando varias liacuteneas) No se muestra la especificacioacuten sintaacutectica debido a que no hay ninguna modificacioacuten en ella Los aspectos resentildeables de la solucioacuten que se propone son- se usa la variable numLin como contador de liacuteneas

- en la variable linea se van yuxtaponiendo los lexemas de las piezas sintaacutecticas de una liacutenea esta yuxtaposicioacuten se realiza mediante la accioacuten lexicograacutefica comuacuten- las secuencias reconocidas con los patrones especificados como SKIP no implican la ejecucioacuten de la accioacuten lexicograacutefica comuacuten por ello su lexema (almacenado en la variable image) ha de yuxtaponer-se expliacutecitamente- hay que separar el patroacuten correspondiente al fin de liacutenea para detectar el momento en que ha de gra-barse la liacutenea completa cuyo final se ha alcanzado

options Ignore_Case = true Common_Token_Action = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(nnAnalisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

TOKEN_MGR_DECLS static int numLin = 0 static String linea =

33

static void CommonTokenAction(Token pieza) linea = linea + piezaimage

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP lt | t | r gt linea = linea + image

SKIP n numLin++ Systemoutprintln(numLin + + linea) linea =

A modo de recapitulacioacutenↄ Accioacuten lexicograacutefica Accioacuten sintaacutectica

bull accioacuten lexicograacutefica bloque de coacutedigo Java asociado a una pieza sintaacutectica nominal (TOKEN) o a una secuencia de caracteres (SKIP) se ejecuta cuando se detecta en la entrada la pieza o la secuencia corres-pondiente no se puede aplicar a piezas sintaacutecticas anoacutenimas (autodefinidas con comillas)bull accioacuten sintaacutectica bloque de coacutedigo Java asociado a un punto de la estructura sintaacutectica (el punto indica-do por el sitio de la produccioacuten donde se inserta el bloque) se ejecuta cuando el anaacutelisis de la entrada pa-sa por ese punto de la estructura

ↄ Valor de una pieza sintaacutectica comunicadaEl analizador lexicograacutefico comunica al analizador sintaacutectico una pieza a traveacutes de un valor de la clase Token (fichero Tokenjava) un objeto de la clase Token representa las caracteriacutesticas de la pieza comunicada En la clase Token entre otros se tienen los campos

String imagelexema de la pieza

int beginLine beginColumn endLine endColumnposicioacuten del lexema en el fichero analizado (indicada por los nuacutemeros de fila y de columna de su comienzo y de su terminacioacuten)

ↄ Valor asociado a un siacutembolo

34

En las producciones que especifican la sintaxis se pueden incluir asignaciones que sirven para dejar ano-tado el valor asociado a un siacutembolo

bull siacutembolo no terminal valor = nombreSimbolo()el meacutetodo de anaacutelisis asociado a nombreSimbolo se ha declarado de un cierto tipo (distinto del tipo void) valor ha de ser una variable del mismo tipo que el meacutetodo

bull siacutembolo terminal dato = lt nombrePieza gtel valor asignado es el valor de la clase Token representativo de la pieza sintaacutectica (nominal) dato ha de ser una variable de tipo Token

ↄ Miradas sobre la pieza sintaacutectica comunicadaLa pieza sintaacutectica comunicada puede mirarse desde cada uno de los dos analizadores

bull analizador sintaacutecticola pieza comunicada (desde el analizador lexicograacutefico) se tiene anotada en un campo de la clase del analizador sintaacutectico declarado como

(static) public Token token

este valor es accesible desde el coacutedigo de todos los meacutetodos de anaacutelisis sintaacutectico el contenido del campo variacutea cuando se recibe una nueva pieza sintaacutectica

bull analizador lexicograacuteficola pieza dispuesta para ser comunicada (al analizador sintaacutectico) se tiene anotada en una variable local declarada como

Token matchedToken

este valor es accesible para el coacutedigo de todas las acciones lexicograacuteficas

ↄ El lexema de la pieza comunicadaEl lexema de la pieza sintaacutectica comunicada se encuentra disponible en dos sitios distintos que compar-ten el mismo nombre pero que son de distinto tipo y se usan de distinta manera

bull String image

es un campo de la clase TOKEN puede consultarse en relacioacuten con un objeto de esa clase que esteacute disponible

bull StringBuffer image

es un campo de la clase del analizador lexicograacutefico ( ∙ ∙ ∙ ∙ TokenManagerjava) es accesible desde el coacutedigo de la clase y por lo tanto desde todas las acciones lexicograacuteficas (bloques de coacute-digo que estaacuten incorporados al analizador lexicograacutefico)

se trata de un valor disponible tanto para las piezas sintaacutecticas nominales (TOKEN) como para las secuencias que se saltan (SKIP)

ↄ Resumen relativo a la lexicografiacutea

TOKEN pieza sintaacutectica nominal∙ ∙ ∙ pieza sintaacutectica anoacutenimaSKIP secuencia de caracteres que no forman pieza

35

Precisiones sobre el analizador sintaacutectico generadoPuede decirse que en su funcionamiento maacutes sencillo cuando no se emplean las posibilidades de examen por adelantado (ldquolookaheadrdquo) JavaCC comprueba que las reglas sintaacutecticas cumplen la condicioacuten LL(1) y generan un analizador descendente-predictivo-recursivo Pero esta apreciacioacuten hay que matizarla ya que el analizador sintaacutectico generado no se ajusta en sentido estricto al modelo de analizador LL(1)

En el modelo de anaacutelisis LL(1) el orden del examen de las distintas alternativas de expansioacuten de un siacutembo-lo no terminal es indiferente ya que se conocen los siacutembolos directores de cada una de ellas (que resultan ser conjuntos disjuntos) en la implementacioacuten del subprograma de anaacutelisis asociado al siacutembolo no terminal ltNombreSimbgt cuyas producciones son

ltNombreSimbgt = α1

| α2

∙ ∙ ∙

| αn

se podriacutea preguntar en cualquier orden si la pieza actual pertenece al conjunto de siacutembolos directores de las distintas reglas Sin embargo en el analizador sintaacutectico generado por JavaCC la implementacioacuten del meacute-todo asociado al siacutembolo no terminal ltNombreSimbgt no sigue esa pauta

Si las anteriores reglas sintaacutecticas se transcriben en JavaCC de la forma

void nombreSimb () α1-jcc

| α2-jcc

∙ ∙ ∙

| αn-jcc

1

accioacutenlexicograacutefica

comuacuten

Commom -TokenAction2

accioacutenlexicograacutefica

propia

(accioacuten ligadaa una pieza)3

bloque dedeclaracioneslexicograacuteficas

TOKEN_MGR_DECLS4

declaracioneslexicograacuteficaspredefinidas

(disponibilidad

de uso)TOKENsisisisi∙ ∙ ∙sino--SKIPnosisisi

Las indicaciones si de las columnas 3 y 4 son consecuencia de las indicaciones de la columna 2

La accioacuten lexicograacutefica comuacuten se ejecuta despueacutes de la accioacuten lexicograacutefica propia

36

el meacutetodo de anaacutelisis generado procede de la siguiente manera

se van considerando las alternativas sucesivamente en el mismo orden en el que estaacuten especifica-das primero la de α1 (α1-jcc) despueacutes la de α2 (α2-jcc) etc

para cada alternativa seleccionada se comprueba si la pieza por adelantado coincide con alguno de los siacutembolos iniciales de la parte derecha y si hay coincidencia se prosigue el anaacutelisis con ella si no hay coincidencia se pasa a la alternativa siguiente

siguiendo con este orden si se llega al final de las alternativas sin haberse encontrado coincidencia alguna se produce un error sintaacutectico

En el analizador generado por JavaCC hay una peculiaridad que no se tiene en los analizadores LL(1) es-trictamente considerados la palabra vaciacutea siacute se considera como posible siacutembolo inicial de la parte derecha una regla (esto ocurre siempre que la parte derecha es anulable) De esta peculiaridad se deriva una conse-cuencia que ha de tenerse en cuenta al escribir la especificacioacuten dado que siempre es posible considerar la palabra vaciacutea como la siguiente pieza por adelantado si se llega a examinar la alternativa correspondiente a una produccioacuten anulable el analizador generado siempre seleccionaraacute esa alternativa con independencia de la siguiente pieza que esteacute presente en la entrada que queda por analizar

Asiacute pues si alguna de las producciones que definen un siacutembolo no terminal es anulable es importante el or-den en que se escriben las alternativas en una especificacioacuten sintaacutectica JavaCC el funcionamiento del ana-lizador generado siacute depende de la colocacioacuten elegida para las reglas

Para ilustrar estas peculiaridades del analizador sintaacutectico generado por JavaCC a continuacioacuten se expone un ejemplo

∆ Ejemplo 9

Se quiere obtener un analizador leacutexico-sintaacutectico de textos que se ajustan al siguiente formato una se-cuencia de uno o maacutes nombres seguida del siacutembolo igual seguido de un nuacutemero entero si la secuencia tiene maacutes de un nombre han de estar separados bien por una coma en todos los casos o bien por el siacutem-bolo dos puntos en todos los casos esto es son correctas las secuencias

uno dos tres = 123exclusivo = 0Abajo Arriba = 57

y son incorrectas las secuenciasuno dos tres = 123

exclusivo inclusive = 0

fuera dentro = 56

Una gramaacutetica que define las secuencias con este formato es

ltListagt = nombre ltOtrosNombresgt = numero

ltOtrosNombresgt = nombre nombre | nombre nombre | ε

se trata de una gramaacutetica que cumple la condicioacuten LL(1)- el siacutembolo director para la alternativa vaciacutea es el siacutembolo igual es evidente pues que las tres reglas de ltOtrosNombresgt tienen siacutembolos directores distintos

37

- la decisioacuten de abandonar la repeticioacuten del anaacutelisis de un nuevo nombre (en las dos primeras alterna-tivas de ltOtrosNombresgt) se puede tomar ante la presencia del siacutembolo igual

En la implementacioacuten de un analizador sintaacutectico LL(1) en sentido estricto la seleccioacuten de la alternativa vaciacutea para expandir el siacutembolo ltOtrosNombresgt se hariacutea tras comprobar expliacutecitamente que la pie-za sintaacutectica por adelantado es el siacutembolo igual

El funcionamiento del analizador generado por JavaCC depende del orden de colocacioacuten de las reglas A continuacioacuten se exponen varias posibilidades de especificacioacuten y se comenta el analizador generado en cada caso

bull Especificacioacuten primeraSe transcriben literalmente las reglas tal y como se han escrito antes en la notacioacuten BNF noacutetese que para poder codificar la parte derecha de la regla vaciacutea ha de incluirse una accioacuten sintaacutectica vaciacutea

PARSER_BEGIN (Igualdad)

public class Igualdad

public static void main (String[] argum) throws ParseException Igualdad analisis = new Igualdad(Systemin) analisissecuencia() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (Igualdad)

void secuencia() lista() ltEOFgt

void lista() lt nombre gt otrosNombres() = lt numero gt

void otrosNombres() lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt ) |

TOKEN lt nombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN lt numero ( [ 0 - 9 ] )+ gt

SKIP | r | n

38

El analizador generado funciona correctamente puesto que en el meacutetodo otrosNombres se selec-ciona la alternativa vaciacutea despueacutes de haber comprobado que la pieza por adelantado no es siacutembolo ini-cial de ninguna de las dos alternativas precedentes (no es una coma ni un siacutembolo dos puntos)

bull Especificacioacuten segundaSe considera la misma especificacioacuten anterior pero cambiando el orden de colocacioacuten de las reglas ahora la produccioacuten vaciacutea se coloca como primera alternativa (soacutelo se muestran las partes modifica-das)

void otrosNombres() | lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt )

Con la entrada de esta especificacioacuten el generador JavaCC produce un mensaje en el que se avisa de que las dos alternativas que estaacuten colocadas detraacutes de la alternativa vaciacutea nunca podraacuten seleccionarse aun asiacute se genera un analizador Pero es un analizador que funciona incorrectamente asiacute por ejemplo la entrada

mxa nyz = 57

se considera incorrecta despueacutes de visto el primer nombre se intenta aplicar la produccioacuten vaciacutea (se espera encontrar el siacutembolo igual) pero habriacutea que aplicar la regla cuyo siacutembolo director es la coma

bull Especificacioacuten terceraSe utilizan las posibilidades de la notacioacuten BNF-Ampliada para prescindir de la especificacioacuten expliacuteci-ta de la palabra vaciacutea se aprovecha que JavaCC admite el operador + para indicar repeticiones de una o maacutes veces la alternativa vaciacutea estaacute impliacutecitamente contemplada en la posibilidad de cero repeti-ciones que contempla el operador (soacutelo se muestran las partes modificadas en la especificacioacuten)

void otrosNombres() ( lt nombre gt )+ | ( lt nombre gt )

El meacutetodo otrosNombres() generado hace las comprobaciones en el siguiente orden

- se comprueba si la pieza por adelantado es una coma (siacutembolo inicial de la primera alternativa) y en este caso se selecciona la primera regla- si no es una coma se comprueba si es un siacutembolo dos puntos (siacutembolo inicial de la segunda alter-nativa) y en este caso se selecciona la segunda regla- si no es un siacutembolo dos puntos se considera la palabra vaciacutea como siacutembolo inicial (siempre posi-ble impliacutecitamente) para la segunda alternativa en este caso para que no haya error la pieza por adelantado ha de ser un siacutembolo igual

Se trata pues de un analizador que funciona correctamente

bull Especificacioacuten cuarta

39

Se pone la misma especificacioacuten anterior pero cambiando el orden de consideracioacuten de la regla vaciacutea ahora se incluye impliacutecitamente en la primera alternativa (soacutelo se muestran las partes modificadas)

void otrosNombres() ( lt nombre gt ) | ( lt nombre gt )+

Para esta especificacioacuten el generador JavaCC emite un aviso en el que se indica que la segunda alter -nativa nunca se seleccionaraacute no obstante se genera un analizador que no funciona como se supone que deberiacutea hacerlo por ejemplo el anaacutelisis de la entrada

nyz mxa = 1

produce un error despueacutes de tratado el primer nombre se intenta aplicar la produccioacuten vaciacutea (corres-pondiente a la repeticioacuten de cero veces contemplada en la primera regla) por lo que no se encuentra el esperado siacutembolo igual

40

Page 22: JavaCC

boolean factor() boolean esCte

ltconstantegt return true | ltvariablegt return false | ( esCte = expresion() ) return esCte

bull Reconsideracioacuten sobre las producciones vaciacuteasAnteriormente se ha comentado que para representar una produccioacuten vaciacutea en una especificacioacuten JavaCC hay que hacer uso de los metasiacutembolos de opcionalidad para ilustrar esta idea se ha incluido como ejem-plo la especificacioacuten de una lista de paraacutemetros compuesta por cero o maacutes expresiones separadas entre siacute por el caraacutecter coma

void parametros() [ expresion() ( expresion() ) ]

Pero si se aprovecha la posibilidad de incorporar acciones sintaacutecticas se tiene una manera de especificar directamente producciones vaciacuteas para conseguirlo basta considerar una produccioacuten vaciacutea (no tiene nada en su parte derecha) acompantildeada de una accioacuten sintaacutectica cuyo bloque de coacutedigo esteacute vaciacuteo Seguacuten esta posibilidad la especificacioacuten anterior podriacutea tambieacuten escribirse asiacute

void parametros() expresion() ( expresion() ) |

En general la accioacuten sintaacutectica asociada a una produccioacuten vaciacutea no tiene por queacute ser un bloque vaciacuteo de coacutedigo por ejemplo si se quiere que el meacutetodo que analiza los paraacutemetros de una llamada devuelva la cantidad de paraacutemetros encontrados se podriacutea escribir la siguiente especificacioacuten

int parametros()

int cantidad

( expresion() cantidad = 1

( expresion() cantidad++ )

| cantidad = 0 )

return cantidad

y si no quisiera ponerse expliacutecitamente la alternativa de la produccioacuten vaciacutea podriacutea escribirse

int parametros() int cantidad = 0

21

[ expresion() cantidad++ ( expresion() cantidad++ ) ]

return cantidad

Comunicacioacuten entre los analizadores leacutexico y sintaacutecticoCada vez que el analizador sintaacutectico realiza una llamada al analizador lexicograacutefico recibe como resultado de la llamada una representacioacuten de la pieza sintaacutectica encontrada en el texto analizado En los analizadores generados por JavaCC la comunicacioacuten de la pieza se efectuacutea mediante un valor de clase Token Esta cla-se estaacute definida en el fichero Tokenjava que se obtiene en todo caso siempre con el mismo contenido cualquiera que sea la especificacioacuten proporcionada como entrada al generador

Entre los campos de la clase Token se encuentran los siguientes

public int kind

nuacutemero entero que sirve de representacioacuten interna de la pieza sintaacutectica estos nuacutemeros asociados a las piezas son asignados automaacuteticamente por JavaCC los valores considerados pueden consultar-se en el fichero generado de nombre Constantsjava

public String image

cadena que contiene de la secuencia de caracteres (lexema) que constituyen la pieza sintaacutectica co-municada

public int beginLine beginColumn endLine endColumn

posiciones ocupadas (nuacutemeros de liacutenea y columna en el fichero de entrada) por el comienzo y el fi-nal del lexema de la pieza sintaacutectica comunicada

La clase Token tiene otros campos y meacutetodos en el fichero Tokenjava se tienen comentarios des-criptivos sobre todos los campos y meacutetodos que forman parte de la clase todos los campos de la clase se declaran como public

Ya se ha comentado anteriormente que cuando un meacutetodo de anaacutelisis sintaacutectico (asociado a un siacutembolo no terminal) tiene tipo el valor devuelto tras una llamada se puede almacenar en una variable por si se desea utilizar en alguna accioacuten sintaacutectica para ello se escribe

valor = nombreSimbolo()

(siendo valor el nombre de la variable y nombreSimbolo el nombre del siacutembolo no terminal) De manera anaacuteloga el objeto de la clase Token representativo de la pieza sintaacutectica encontrada en la entrada por el analizador lexicograacutefico puede asignarse a una variable del mismo tipo para tener accesibles las ca -racteriacutesticas de la pieza por si se precisaran en alguna accioacuten sintaacutectica para ello en la parte de la especifi -cacioacuten sintaacutectica donde aparezca la pieza sintaacutectica se escribe

dato = lt nombrePieza gt

donde nombrePieza es el nombre de la pieza sintaacutectica y dato es el nombre de una variable de tipo Token en la que se anota el valor indicativo de las propiedades de la pieza sintaacutectica comunicada

Tambieacuten existe otra manera de acceder a las caracteriacutesticas de la pieza sintaacutectica que el analizador lexico -graacutefico comunica al analizador sintaacutectico en la clase del analizador sintaacutectico se tiene declarado el campo

[ static ] public Token token

22

cada vez que el analizador sintaacutectico recibe una pieza sintaacutectica comunicada por el analizador lexicograacutefico se deja anotada en el campo token y ese contenido no se altera hasta que sea proporcionada la siguiente pieza de la entrada

∆ Ejemplo 4

Se considera una ampliacioacuten del ejemplo de presentacioacuten ademaacutes del anaacutelisis de la entrada se pretende obtener una relacioacuten numerada de las variables y las constantes indicando el nuacutemero de liacutenea y el nuacuteme-ro de columna donde estaacuten situadas por ejemplo si en la entrada se tiene la expresioacuten (grabada en dos liacuteneas)

x (y + 11) + 22

en la salida se tendraacute

1- x linea 1 columna 1

2- y linea 1 columna 6

3- 11 linea 1 columna 10

4- 22 linea 2 columna 5

En la especificacioacuten que se propone como solucioacuten puede apreciarse- en la clase del analizador sintaacutectico se han incluido meacutetodos auxiliares en los que se usan los nom-bres image beginLine beginColumn que son campos de un objeto de clase Token

- en la especificacioacuten sintaacutectica de factor se emplea la variable pieza de tipo Token para reco-ger las caracteriacutesticas de las piezas sintaacutecticas detectadas- la variable numero hace de contador para numerar las constantes y variables

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin

static int numero = 0

private static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

private static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

private static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

23

PARSER_END (ExprMin)

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() Token pieza pieza = ltconstantegt numero++ grabarDatosPieza(numero pieza) | pieza = ltvariablegt numero++ grabarDatosPieza(numero pieza) | ( expresion() )

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP | n | t | r

Si se agrupan las dos acciones sintaacutecticas comunes la especificacioacuten de factor tambieacuten puede escri-birse de esta forma

void factor() Token pieza ( pieza = ltconstantegt | pieza = ltvariablegt

) numero++ grabarDatosPieza(numero pieza)

| ( expresion() )

24

Si se accede al campo token de la clase del analizador sintaacutectico se puede resolver este ejemplo pres-cindiendo de la variable pieza

void factor() ( ltconstantegt | ltvariablegt

) numero++ grabarDatosPieza(numero token)

| ( expresion() )

Tareas asociadas a las piezas sintaacutecticasYa se ha visto que es posible asociar bloques de coacutedigo Java a puntos de la estructura sintaacutectica para que se realicen ciertas operaciones cuando el analizador sintaacutectico pase por esa zona de la estructura Tambieacuten es posible asociar coacutedigo Java a las piezas sintaacutecticas para que el analizador lexicograacutefico efectuacutee determina-das tareas cuando detecte en la entrada analizada piezas que tengan asociado coacutedigo

loz Accioacuten ligada a una pieza sintaacutecticaSi se asocia un bloque de coacutedigo a una pieza sintaacutectica se consigue que cada vez que se detecta en la en-trada analizada la presencia de esa pieza se ejecute en ese preciso momento el coacutedigo del bloque asociado a la pieza Estas acciones se denominan ldquoacciones lexicograacuteficasrdquo (lexical actions) el coacutedigo del bloque se traslada literalmente al coacutedigo del analizador lexicograacutefico generado para que se ejecute oportunamente cuando se detecte la pieza ligada al bloque de coacutedigo La accioacuten lexicograacutefica se ejecuta inmediatamente antes que la pieza sea comunicada al analizador sintaacutectico

Se puede asociar una accioacuten lexicograacutefica a una pieza sintaacutectica declarada con TOKEN y tambieacuten a una se-cuencia de caracteres declarada con SKIP ademaacutes de eacutestas hay otras dos formas de declaraciones lexico-graacuteficas (no mencionadas en esta introduccioacuten) que tambieacuten pueden llevar una accioacuten asociada

En una especificacioacuten JavaCC la forma de una pieza sintaacutectica (o bien de una secuencia de caracteres) se define poniendo su descripcioacuten delimitada entre llaves detraacutes de la indicacioacuten TOKEN (o bien SKIP) si se quiere asociar una accioacuten lexicograacutefica a la pieza (o a la secuencia) hay que colocar el bloque de coacutedigo de la accioacuten a continuacioacuten de la descripcioacuten de la forma de la pieza (o de la secuencia) el bloque (delimi-tado por llaves) queda situado entre la descripcioacuten y la llave que cierra la especificacioacuten

La pieza sintaacutectica especial que representa el final del fichero (EOF) tambieacuten puede llevar asociada una ac-cioacuten lexicograacutefica para ello hay que poner

ltgt TOKEN lt EOF gt coacutedigo de la accioacuten asociada

para justificar la presencia de los caracteres ltgt delante de la palabra TOKEN hay que basarse en el con-cepto de ldquoestado lexicograacuteficordquo (cuya explicacioacuten queda fuera del alcance de esta introduccioacuten)

∆ Ejemplo 5

Se considera nuevamente el ejemplo de presentacioacuten como resultado del anaacutelisis de una expresioacuten se pretende obtener una relacioacuten de los pareacutentesis contenidos en la expresioacuten en el mismo orden en que se encuentran ademaacutes por cada salto de liacutenea del texto de entrada se grabaraacute en la salida el caraacutecter y

25

se pasaraacute a una nueva liacutenea tambieacuten se anotaraacute la frase Final del trabajo cuando se alcance el final del fichero de entrada Asiacute por ejemplo si la entrada analizada es

(a + b) (( x )) (y + 7)

los resultados obtenidos seraacuten

() (())()

Final del trabajo

En la especificacioacuten JavaCC que se propone como solucioacuten soacutelo se muestran las reglas sintaacutecticas y le -xicograacuteficas (en el resto no hay cambios) en esta solucioacuten puede apreciarse- las piezas sintaacutecticas correspondientes a los pareacutentesis se han definido de manera nominal para po-der asociarles una accioacuten lexicograacutefica- los caracteres incluidos en la pieza de tipo SKIP se han descompuesto en dos grupos ya que la ac-cioacuten lexicograacutefica incorporada soacutelo afecta a uno de ellos

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ltpAbrirgt expresion() ltpCerrargt

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

TOKEN

26

lt pAbrir ( gt Systemoutprint(()

TOKEN lt pCerrar ) gt Systemoutprint())

ltgtTOKEN lt EOF gt Systemoutprintln ( Final del trabajo )

SKIP n Systemoutprint( n)

SKIP | t | r

loz Bloque de declaraciones lexicograacuteficasCada definicioacuten de una pieza sintaacutectica puede llevar asociada una accioacuten lexicograacutefica que se ejecuta in-mediatamente despueacutes de detectar en la entrada un lexema que se ajusta a la definicioacuten de la pieza cada accioacuten lexicograacutefica es un bloque de coacutedigo Java que puede contener sus propias declaraciones Pero a ve-ces interesa tener declaraciones que sean compartidas por todas las acciones lexicograacuteficas con indepen-dencia de la pieza a la que esteacute asociada cada una de ellas

Para incluir declaraciones que puedan usarse desde el coacutedigo asociado a cualquiera de las piezas sintaacutecti -cas se dispone de una construccioacuten con el siguiente formato

TOKEN_MGR_DECLS

que consta de una palabra reservada seguida del siacutembolo dos puntos seguido de un bloque de coacutedigo Java (delimitado por llaves) esta declaracioacuten se puede colocar en cualquier parte de una especificacioacuten JavaCC si se incluye soacutelo puede ponerse una vez

Todas las declaraciones de variables o de meacutetodos puestas en el bloque de declaraciones lexicograacuteficas compartidas son accesibles desde el coacutedigo de cualquier accioacuten lexicograacutefica la justificacioacuten es sencilla el bloque de declaraciones se traslada al principio del coacutedigo del analizador lexicograacutefico generado y las ac-ciones lexicograacuteficas tambieacuten se trasladan a ese analizador

loz Declaraciones lexicograacuteficas predefinidasDe lo expuesto hasta ahora se aprecia que en una accioacuten lexicograacutefica asociada a una pieza sintaacutectica re-sultan accesibles

- las declaraciones de su propio bloque (si tiene declaraciones)- los componentes del bloque de declaraciones compartidas (si estaacute incluido en la especificacioacuten)

Ademaacutes de esto se tienen otras declaraciones pertenecientes a la clase TokenManager que constituye el analizador lexicograacutefico que tambieacuten son utilizables desde cualquiera de las acciones lexico-graacuteficas estas declaraciones pueden considerarse como ldquopredefinidasrdquo ya que son incorporadas automaacuteti-camente por el generador Entre estas declaraciones accesibles se encuentran las siguientes

[ static ] StringBuffer imagecontiene la cadena de caracteres representativa de la pieza sintaacutectica actual que se acaba de recono-cer en le entrada se trata de un campo de la clase y se declara como static cuando el analizador

27

se ha generado con la opcioacuten (por defecto) Static = true si se generase con la opcioacuten Sta-tic = false no quedariacutea declarada como static

Token matchedToken

variable de clase Token que representa la pieza sintaacutectica actual la que se acaba de encontrar en la entrada se trata de una variable local del meacutetodo al que se ha trasladado el coacutedigo de la accioacuten (y por ello accesible desde ese coacutedigo)

El campo image puede usarse tanto si la accioacuten lexicograacutefica estaacute asociada a una pieza declarada con TOKEN como si lo estaacute a una secuencia declarada con SKIP Sin embargo la variable matchedToken no puede usarse en una accioacuten asociada a un patroacuten especificado como SKIP el motivo es que lo declara-do como SKIP no constituye propiamente una pieza sintaacutectica ya que no se va a comunicar al analizador sintaacutectico

∆ Ejemplo 6

Se propone aquiacute una nueva solucioacuten para el ejemplo nordm 4 expuesto anteriormente

Esta nueva solucioacuten tal y como se expone aquiacute admite algunas mejoras ya que su pretensioacuten soacutelo es ser-vir de ilustracioacuten para algunos aspectos que se acaban de citar (maacutes adelante se propone otra solucioacuten al-ternativa)

En la especificacioacuten de la solucioacuten es posible apreciar esto- las acciones lexicograacuteficas pueden llevar sus propias declaraciones las variables nLineaVar nColumnaVar nLineaCte y nColumnaCte se declaran dentro de las acciones

- la variable matchedToken a la que se hace referencia en el coacutedigo de la accioacuten lexicograacutefica aso-ciada a una variable contiene la informacioacuten sobre la pieza sintaacutectica que se acaba de detectar una va-riable de la expresioacuten analizada anaacutelogamente con la referencia relativa a una constante- el nombre image que aparece en las acciones lexicograacuteficas se refiere al campo de la clase Expr-MinTokenManager (analizador lexicograacutefico) noacutetese que en efecto se comunica como un paraacuteme-tro de clase StringBuffer

- la variable numero se emplea para numerar las constantes y variables encontradas

- los nombres beginLine y beginColumn son campos (puacuteblicos) de la clase Token por ello se pueden aplicar a un objeto de esa clase

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

void unaExpresion()

28

expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n StringBuffer lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

TOKEN lt variable [a-z] gt

int nLineaVar nColumnaVar numero++ nLineaVar = matchedTokenbeginLine nColumnaVar = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaVar nColumnaVar)

TOKEN lt constante ( [0-9] ) + gt

int nLineaCte nColumnaCte

29

numero++ nLineaCte = matchedTokenbeginLine nColumnaCte = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaCte nColumnaCte)

SKIP | n | t | r

La especificacioacuten precedente se puede escribir de otra manera maacutes escueta como se indica a continua-cioacuten (soacutelo se muestran las modificaciones) sobre esta nueva especificacioacuten puede mencionarse- se han eliminado por innecesarias las variables declaradas dentro de las acciones lexicograacuteficas- el nombre image que aparece en el bloque de las declaraciones compartidas se refiere al campo de la clase Token noacutetese que en efecto se aplica a un objeto de esa clase y se comunica como un paraacute -metro de tipo String

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

TOKEN lt variable [a-z] gt

numero++ grabarDatosPieza(numero matchedToken)

TOKEN lt constante ( [0-9] ) + gt

numero++ grabarDatosPieza(numero matchedToken)

loz Accioacuten lexicograacutefica comuacutenSi hubiera que realizar la misma tarea tras la deteccioacuten de todas y cada una de las piezas sintaacutecticas una posibilidad incoacutemoda seriacutea repetir el mismo coacutedigo en todas las acciones lexicograacuteficas para evitar tanta reiteracioacuten se tiene la opcioacuten Common_Token_Action su valor por defecto es false pero si se le asigna el valor true es posible definir una ldquoaccioacuten lexicograacutefica comuacutenrdquo que se escribe una uacutenica vez y se aplica a todas las piezas sintaacutecticas

30

Cuando la opcioacuten Common_Token_Action se establece con el valor true es obligado incluir el blo-que de declaraciones lexicograacuteficas compartidas (TOKEN_MGR_DECLS) Ademaacutes en ese bloque se preci-sa la definicioacuten de un meacutetodo cuyo encabezamiento sea

[ static ] void CommonTokenAction(Token t)

el coacutedigo de este meacutetodo constituye la accioacuten lexicograacutefica comuacuten que se ejecutaraacute despueacutes de la deteccioacuten de cualquier pieza sintaacutectica el meacutetodo tiene un paraacutemetro de tipo Token mediante el que se puede co-municar la informacioacuten correspondiente a la pieza que se acaba de encontrar en la entrada analizada

Si una pieza sintaacutectica tiene asociada una accioacuten lexicograacutefica propia la accioacuten lexicograacutefica comuacuten (si es-taacute definida) se ejecutaraacute inmediatamente despueacutes de realizar la accioacuten propia

La accioacuten lexicograacutefica comuacuten se aplica a todas las piezas sintaacutecticas definidas como TOKEN bien sea no-minalmente (en las reglas lexicograacuteficas) o bien anoacutenimamente (incorporadas dentro de las reglas sintaacutecti-cas) sin embargo no se aplica cuando la secuencia acoplada se corresponde con una expresioacuten regular es-pecificada como SKIP (noacutetese que por el contrario una especificacioacuten SKIP siacute puede tener asociada una accioacuten lexicograacutefica propia)

El coacutedigo del meacutetodo CommonTokenAction se traslada como un componente maacutes del bloque de decla-raciones compartidas (TOKEN_MGR_DECLS) al principio de la clase del analizador lexicograacutefico

∆ Ejemplo 7

Se pretende ahora ampliar el ejemplo de presentacioacuten con la obtencioacuten de una relacioacuten numerada de los lexemas de las piezas sintaacutecticas encontradas en la entrada analizada en el caso de las variables y de las constantes antes del lexema se mostraraacute un indicativo (Var Const) por ejemplo si la expresioacuten de la entrada es

( a + 987 ) b

se grabaraacute la salida

( 1Var a 2+ 3Const 987 4) 5 6Var b 7 8

En la solucioacuten que se propone a continuacioacuten se muestran la definicioacuten de las opciones y las reglas sin-taacutecticas y lexicograacuteficas (en el resto no hay modificaciones) puede resaltarse lo siguiente- las llamadas al meacutetodo CommonTokenAction se incorporan automaacuteticamente no hay que incluir-las en la especificacioacuten como paraacutemetro de llamada a este meacutetodo siempre se emplea el objeto que re -presenta la pieza que se acaba de detectar- la accioacuten lexicograacutefica comuacuten realiza la actualizacioacuten del contador de piezas y la grabacioacuten del lexe-ma de la pieza detectada con su nuacutemero de orden- la accioacuten lexicograacutefica comuacuten tambieacuten se aplica a las piezas sintaacutecticas anoacutenimas (pareacutentesis y ope-radores) y a la pieza especial que representa el final del fichero (EOF)

- las piezas sintaacutecticas ltconstantegt y ltvariablegt tienen asociada una accioacuten lexicograacutefica propia que se ejecuta antes de la accioacuten lexicograacutefica comuacuten- los caracteres que se saltan (SKIP) no estaacuten afectados por la accioacuten lexicograacutefica comuacuten

options Ignore_Case = true Common_Token_Action = true

31

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarIndicativo(String indicativo) Systemoutprint(indicativo + )

static void grabarLexema (int num String lexema) Systemoutprint(lexema + + num + n)

static void CommonTokenAction(Token pieza) numero++ grabarLexema(numero piezaimage)

TOKEN lt variable [a-z] gt

grabarIndicativo(Var)

32

TOKEN lt constante ( [0-9] ) + gt

grabarIndicativo(Const)

SKIP | n | t | r

A modo de ejemplo de aplicacioacuten de la accioacuten lexicograacutefica comuacuten en lo que sigue se ofrece una solucioacuten para la grabacioacuten (en el fichero predefinido de salida) del texto analizado con las liacuteneas numeradas

∆ Ejemplo 8 [ Listado del texto analizado ]Se desea completar el ejemplo de presentacioacuten con la grabacioacuten de un listado con las liacuteneas numeradas del texto analizado (noacutetese que una expresioacuten puede escribirse ocupando varias liacuteneas) No se muestra la especificacioacuten sintaacutectica debido a que no hay ninguna modificacioacuten en ella Los aspectos resentildeables de la solucioacuten que se propone son- se usa la variable numLin como contador de liacuteneas

- en la variable linea se van yuxtaponiendo los lexemas de las piezas sintaacutecticas de una liacutenea esta yuxtaposicioacuten se realiza mediante la accioacuten lexicograacutefica comuacuten- las secuencias reconocidas con los patrones especificados como SKIP no implican la ejecucioacuten de la accioacuten lexicograacutefica comuacuten por ello su lexema (almacenado en la variable image) ha de yuxtaponer-se expliacutecitamente- hay que separar el patroacuten correspondiente al fin de liacutenea para detectar el momento en que ha de gra-barse la liacutenea completa cuyo final se ha alcanzado

options Ignore_Case = true Common_Token_Action = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(nnAnalisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

TOKEN_MGR_DECLS static int numLin = 0 static String linea =

33

static void CommonTokenAction(Token pieza) linea = linea + piezaimage

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP lt | t | r gt linea = linea + image

SKIP n numLin++ Systemoutprintln(numLin + + linea) linea =

A modo de recapitulacioacutenↄ Accioacuten lexicograacutefica Accioacuten sintaacutectica

bull accioacuten lexicograacutefica bloque de coacutedigo Java asociado a una pieza sintaacutectica nominal (TOKEN) o a una secuencia de caracteres (SKIP) se ejecuta cuando se detecta en la entrada la pieza o la secuencia corres-pondiente no se puede aplicar a piezas sintaacutecticas anoacutenimas (autodefinidas con comillas)bull accioacuten sintaacutectica bloque de coacutedigo Java asociado a un punto de la estructura sintaacutectica (el punto indica-do por el sitio de la produccioacuten donde se inserta el bloque) se ejecuta cuando el anaacutelisis de la entrada pa-sa por ese punto de la estructura

ↄ Valor de una pieza sintaacutectica comunicadaEl analizador lexicograacutefico comunica al analizador sintaacutectico una pieza a traveacutes de un valor de la clase Token (fichero Tokenjava) un objeto de la clase Token representa las caracteriacutesticas de la pieza comunicada En la clase Token entre otros se tienen los campos

String imagelexema de la pieza

int beginLine beginColumn endLine endColumnposicioacuten del lexema en el fichero analizado (indicada por los nuacutemeros de fila y de columna de su comienzo y de su terminacioacuten)

ↄ Valor asociado a un siacutembolo

34

En las producciones que especifican la sintaxis se pueden incluir asignaciones que sirven para dejar ano-tado el valor asociado a un siacutembolo

bull siacutembolo no terminal valor = nombreSimbolo()el meacutetodo de anaacutelisis asociado a nombreSimbolo se ha declarado de un cierto tipo (distinto del tipo void) valor ha de ser una variable del mismo tipo que el meacutetodo

bull siacutembolo terminal dato = lt nombrePieza gtel valor asignado es el valor de la clase Token representativo de la pieza sintaacutectica (nominal) dato ha de ser una variable de tipo Token

ↄ Miradas sobre la pieza sintaacutectica comunicadaLa pieza sintaacutectica comunicada puede mirarse desde cada uno de los dos analizadores

bull analizador sintaacutecticola pieza comunicada (desde el analizador lexicograacutefico) se tiene anotada en un campo de la clase del analizador sintaacutectico declarado como

(static) public Token token

este valor es accesible desde el coacutedigo de todos los meacutetodos de anaacutelisis sintaacutectico el contenido del campo variacutea cuando se recibe una nueva pieza sintaacutectica

bull analizador lexicograacuteficola pieza dispuesta para ser comunicada (al analizador sintaacutectico) se tiene anotada en una variable local declarada como

Token matchedToken

este valor es accesible para el coacutedigo de todas las acciones lexicograacuteficas

ↄ El lexema de la pieza comunicadaEl lexema de la pieza sintaacutectica comunicada se encuentra disponible en dos sitios distintos que compar-ten el mismo nombre pero que son de distinto tipo y se usan de distinta manera

bull String image

es un campo de la clase TOKEN puede consultarse en relacioacuten con un objeto de esa clase que esteacute disponible

bull StringBuffer image

es un campo de la clase del analizador lexicograacutefico ( ∙ ∙ ∙ ∙ TokenManagerjava) es accesible desde el coacutedigo de la clase y por lo tanto desde todas las acciones lexicograacuteficas (bloques de coacute-digo que estaacuten incorporados al analizador lexicograacutefico)

se trata de un valor disponible tanto para las piezas sintaacutecticas nominales (TOKEN) como para las secuencias que se saltan (SKIP)

ↄ Resumen relativo a la lexicografiacutea

TOKEN pieza sintaacutectica nominal∙ ∙ ∙ pieza sintaacutectica anoacutenimaSKIP secuencia de caracteres que no forman pieza

35

Precisiones sobre el analizador sintaacutectico generadoPuede decirse que en su funcionamiento maacutes sencillo cuando no se emplean las posibilidades de examen por adelantado (ldquolookaheadrdquo) JavaCC comprueba que las reglas sintaacutecticas cumplen la condicioacuten LL(1) y generan un analizador descendente-predictivo-recursivo Pero esta apreciacioacuten hay que matizarla ya que el analizador sintaacutectico generado no se ajusta en sentido estricto al modelo de analizador LL(1)

En el modelo de anaacutelisis LL(1) el orden del examen de las distintas alternativas de expansioacuten de un siacutembo-lo no terminal es indiferente ya que se conocen los siacutembolos directores de cada una de ellas (que resultan ser conjuntos disjuntos) en la implementacioacuten del subprograma de anaacutelisis asociado al siacutembolo no terminal ltNombreSimbgt cuyas producciones son

ltNombreSimbgt = α1

| α2

∙ ∙ ∙

| αn

se podriacutea preguntar en cualquier orden si la pieza actual pertenece al conjunto de siacutembolos directores de las distintas reglas Sin embargo en el analizador sintaacutectico generado por JavaCC la implementacioacuten del meacute-todo asociado al siacutembolo no terminal ltNombreSimbgt no sigue esa pauta

Si las anteriores reglas sintaacutecticas se transcriben en JavaCC de la forma

void nombreSimb () α1-jcc

| α2-jcc

∙ ∙ ∙

| αn-jcc

1

accioacutenlexicograacutefica

comuacuten

Commom -TokenAction2

accioacutenlexicograacutefica

propia

(accioacuten ligadaa una pieza)3

bloque dedeclaracioneslexicograacuteficas

TOKEN_MGR_DECLS4

declaracioneslexicograacuteficaspredefinidas

(disponibilidad

de uso)TOKENsisisisi∙ ∙ ∙sino--SKIPnosisisi

Las indicaciones si de las columnas 3 y 4 son consecuencia de las indicaciones de la columna 2

La accioacuten lexicograacutefica comuacuten se ejecuta despueacutes de la accioacuten lexicograacutefica propia

36

el meacutetodo de anaacutelisis generado procede de la siguiente manera

se van considerando las alternativas sucesivamente en el mismo orden en el que estaacuten especifica-das primero la de α1 (α1-jcc) despueacutes la de α2 (α2-jcc) etc

para cada alternativa seleccionada se comprueba si la pieza por adelantado coincide con alguno de los siacutembolos iniciales de la parte derecha y si hay coincidencia se prosigue el anaacutelisis con ella si no hay coincidencia se pasa a la alternativa siguiente

siguiendo con este orden si se llega al final de las alternativas sin haberse encontrado coincidencia alguna se produce un error sintaacutectico

En el analizador generado por JavaCC hay una peculiaridad que no se tiene en los analizadores LL(1) es-trictamente considerados la palabra vaciacutea siacute se considera como posible siacutembolo inicial de la parte derecha una regla (esto ocurre siempre que la parte derecha es anulable) De esta peculiaridad se deriva una conse-cuencia que ha de tenerse en cuenta al escribir la especificacioacuten dado que siempre es posible considerar la palabra vaciacutea como la siguiente pieza por adelantado si se llega a examinar la alternativa correspondiente a una produccioacuten anulable el analizador generado siempre seleccionaraacute esa alternativa con independencia de la siguiente pieza que esteacute presente en la entrada que queda por analizar

Asiacute pues si alguna de las producciones que definen un siacutembolo no terminal es anulable es importante el or-den en que se escriben las alternativas en una especificacioacuten sintaacutectica JavaCC el funcionamiento del ana-lizador generado siacute depende de la colocacioacuten elegida para las reglas

Para ilustrar estas peculiaridades del analizador sintaacutectico generado por JavaCC a continuacioacuten se expone un ejemplo

∆ Ejemplo 9

Se quiere obtener un analizador leacutexico-sintaacutectico de textos que se ajustan al siguiente formato una se-cuencia de uno o maacutes nombres seguida del siacutembolo igual seguido de un nuacutemero entero si la secuencia tiene maacutes de un nombre han de estar separados bien por una coma en todos los casos o bien por el siacutem-bolo dos puntos en todos los casos esto es son correctas las secuencias

uno dos tres = 123exclusivo = 0Abajo Arriba = 57

y son incorrectas las secuenciasuno dos tres = 123

exclusivo inclusive = 0

fuera dentro = 56

Una gramaacutetica que define las secuencias con este formato es

ltListagt = nombre ltOtrosNombresgt = numero

ltOtrosNombresgt = nombre nombre | nombre nombre | ε

se trata de una gramaacutetica que cumple la condicioacuten LL(1)- el siacutembolo director para la alternativa vaciacutea es el siacutembolo igual es evidente pues que las tres reglas de ltOtrosNombresgt tienen siacutembolos directores distintos

37

- la decisioacuten de abandonar la repeticioacuten del anaacutelisis de un nuevo nombre (en las dos primeras alterna-tivas de ltOtrosNombresgt) se puede tomar ante la presencia del siacutembolo igual

En la implementacioacuten de un analizador sintaacutectico LL(1) en sentido estricto la seleccioacuten de la alternativa vaciacutea para expandir el siacutembolo ltOtrosNombresgt se hariacutea tras comprobar expliacutecitamente que la pie-za sintaacutectica por adelantado es el siacutembolo igual

El funcionamiento del analizador generado por JavaCC depende del orden de colocacioacuten de las reglas A continuacioacuten se exponen varias posibilidades de especificacioacuten y se comenta el analizador generado en cada caso

bull Especificacioacuten primeraSe transcriben literalmente las reglas tal y como se han escrito antes en la notacioacuten BNF noacutetese que para poder codificar la parte derecha de la regla vaciacutea ha de incluirse una accioacuten sintaacutectica vaciacutea

PARSER_BEGIN (Igualdad)

public class Igualdad

public static void main (String[] argum) throws ParseException Igualdad analisis = new Igualdad(Systemin) analisissecuencia() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (Igualdad)

void secuencia() lista() ltEOFgt

void lista() lt nombre gt otrosNombres() = lt numero gt

void otrosNombres() lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt ) |

TOKEN lt nombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN lt numero ( [ 0 - 9 ] )+ gt

SKIP | r | n

38

El analizador generado funciona correctamente puesto que en el meacutetodo otrosNombres se selec-ciona la alternativa vaciacutea despueacutes de haber comprobado que la pieza por adelantado no es siacutembolo ini-cial de ninguna de las dos alternativas precedentes (no es una coma ni un siacutembolo dos puntos)

bull Especificacioacuten segundaSe considera la misma especificacioacuten anterior pero cambiando el orden de colocacioacuten de las reglas ahora la produccioacuten vaciacutea se coloca como primera alternativa (soacutelo se muestran las partes modifica-das)

void otrosNombres() | lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt )

Con la entrada de esta especificacioacuten el generador JavaCC produce un mensaje en el que se avisa de que las dos alternativas que estaacuten colocadas detraacutes de la alternativa vaciacutea nunca podraacuten seleccionarse aun asiacute se genera un analizador Pero es un analizador que funciona incorrectamente asiacute por ejemplo la entrada

mxa nyz = 57

se considera incorrecta despueacutes de visto el primer nombre se intenta aplicar la produccioacuten vaciacutea (se espera encontrar el siacutembolo igual) pero habriacutea que aplicar la regla cuyo siacutembolo director es la coma

bull Especificacioacuten terceraSe utilizan las posibilidades de la notacioacuten BNF-Ampliada para prescindir de la especificacioacuten expliacuteci-ta de la palabra vaciacutea se aprovecha que JavaCC admite el operador + para indicar repeticiones de una o maacutes veces la alternativa vaciacutea estaacute impliacutecitamente contemplada en la posibilidad de cero repeti-ciones que contempla el operador (soacutelo se muestran las partes modificadas en la especificacioacuten)

void otrosNombres() ( lt nombre gt )+ | ( lt nombre gt )

El meacutetodo otrosNombres() generado hace las comprobaciones en el siguiente orden

- se comprueba si la pieza por adelantado es una coma (siacutembolo inicial de la primera alternativa) y en este caso se selecciona la primera regla- si no es una coma se comprueba si es un siacutembolo dos puntos (siacutembolo inicial de la segunda alter-nativa) y en este caso se selecciona la segunda regla- si no es un siacutembolo dos puntos se considera la palabra vaciacutea como siacutembolo inicial (siempre posi-ble impliacutecitamente) para la segunda alternativa en este caso para que no haya error la pieza por adelantado ha de ser un siacutembolo igual

Se trata pues de un analizador que funciona correctamente

bull Especificacioacuten cuarta

39

Se pone la misma especificacioacuten anterior pero cambiando el orden de consideracioacuten de la regla vaciacutea ahora se incluye impliacutecitamente en la primera alternativa (soacutelo se muestran las partes modificadas)

void otrosNombres() ( lt nombre gt ) | ( lt nombre gt )+

Para esta especificacioacuten el generador JavaCC emite un aviso en el que se indica que la segunda alter -nativa nunca se seleccionaraacute no obstante se genera un analizador que no funciona como se supone que deberiacutea hacerlo por ejemplo el anaacutelisis de la entrada

nyz mxa = 1

produce un error despueacutes de tratado el primer nombre se intenta aplicar la produccioacuten vaciacutea (corres-pondiente a la repeticioacuten de cero veces contemplada en la primera regla) por lo que no se encuentra el esperado siacutembolo igual

40

Page 23: JavaCC

[ expresion() cantidad++ ( expresion() cantidad++ ) ]

return cantidad

Comunicacioacuten entre los analizadores leacutexico y sintaacutecticoCada vez que el analizador sintaacutectico realiza una llamada al analizador lexicograacutefico recibe como resultado de la llamada una representacioacuten de la pieza sintaacutectica encontrada en el texto analizado En los analizadores generados por JavaCC la comunicacioacuten de la pieza se efectuacutea mediante un valor de clase Token Esta cla-se estaacute definida en el fichero Tokenjava que se obtiene en todo caso siempre con el mismo contenido cualquiera que sea la especificacioacuten proporcionada como entrada al generador

Entre los campos de la clase Token se encuentran los siguientes

public int kind

nuacutemero entero que sirve de representacioacuten interna de la pieza sintaacutectica estos nuacutemeros asociados a las piezas son asignados automaacuteticamente por JavaCC los valores considerados pueden consultar-se en el fichero generado de nombre Constantsjava

public String image

cadena que contiene de la secuencia de caracteres (lexema) que constituyen la pieza sintaacutectica co-municada

public int beginLine beginColumn endLine endColumn

posiciones ocupadas (nuacutemeros de liacutenea y columna en el fichero de entrada) por el comienzo y el fi-nal del lexema de la pieza sintaacutectica comunicada

La clase Token tiene otros campos y meacutetodos en el fichero Tokenjava se tienen comentarios des-criptivos sobre todos los campos y meacutetodos que forman parte de la clase todos los campos de la clase se declaran como public

Ya se ha comentado anteriormente que cuando un meacutetodo de anaacutelisis sintaacutectico (asociado a un siacutembolo no terminal) tiene tipo el valor devuelto tras una llamada se puede almacenar en una variable por si se desea utilizar en alguna accioacuten sintaacutectica para ello se escribe

valor = nombreSimbolo()

(siendo valor el nombre de la variable y nombreSimbolo el nombre del siacutembolo no terminal) De manera anaacuteloga el objeto de la clase Token representativo de la pieza sintaacutectica encontrada en la entrada por el analizador lexicograacutefico puede asignarse a una variable del mismo tipo para tener accesibles las ca -racteriacutesticas de la pieza por si se precisaran en alguna accioacuten sintaacutectica para ello en la parte de la especifi -cacioacuten sintaacutectica donde aparezca la pieza sintaacutectica se escribe

dato = lt nombrePieza gt

donde nombrePieza es el nombre de la pieza sintaacutectica y dato es el nombre de una variable de tipo Token en la que se anota el valor indicativo de las propiedades de la pieza sintaacutectica comunicada

Tambieacuten existe otra manera de acceder a las caracteriacutesticas de la pieza sintaacutectica que el analizador lexico -graacutefico comunica al analizador sintaacutectico en la clase del analizador sintaacutectico se tiene declarado el campo

[ static ] public Token token

22

cada vez que el analizador sintaacutectico recibe una pieza sintaacutectica comunicada por el analizador lexicograacutefico se deja anotada en el campo token y ese contenido no se altera hasta que sea proporcionada la siguiente pieza de la entrada

∆ Ejemplo 4

Se considera una ampliacioacuten del ejemplo de presentacioacuten ademaacutes del anaacutelisis de la entrada se pretende obtener una relacioacuten numerada de las variables y las constantes indicando el nuacutemero de liacutenea y el nuacuteme-ro de columna donde estaacuten situadas por ejemplo si en la entrada se tiene la expresioacuten (grabada en dos liacuteneas)

x (y + 11) + 22

en la salida se tendraacute

1- x linea 1 columna 1

2- y linea 1 columna 6

3- 11 linea 1 columna 10

4- 22 linea 2 columna 5

En la especificacioacuten que se propone como solucioacuten puede apreciarse- en la clase del analizador sintaacutectico se han incluido meacutetodos auxiliares en los que se usan los nom-bres image beginLine beginColumn que son campos de un objeto de clase Token

- en la especificacioacuten sintaacutectica de factor se emplea la variable pieza de tipo Token para reco-ger las caracteriacutesticas de las piezas sintaacutecticas detectadas- la variable numero hace de contador para numerar las constantes y variables

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin

static int numero = 0

private static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

private static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

private static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

23

PARSER_END (ExprMin)

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() Token pieza pieza = ltconstantegt numero++ grabarDatosPieza(numero pieza) | pieza = ltvariablegt numero++ grabarDatosPieza(numero pieza) | ( expresion() )

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP | n | t | r

Si se agrupan las dos acciones sintaacutecticas comunes la especificacioacuten de factor tambieacuten puede escri-birse de esta forma

void factor() Token pieza ( pieza = ltconstantegt | pieza = ltvariablegt

) numero++ grabarDatosPieza(numero pieza)

| ( expresion() )

24

Si se accede al campo token de la clase del analizador sintaacutectico se puede resolver este ejemplo pres-cindiendo de la variable pieza

void factor() ( ltconstantegt | ltvariablegt

) numero++ grabarDatosPieza(numero token)

| ( expresion() )

Tareas asociadas a las piezas sintaacutecticasYa se ha visto que es posible asociar bloques de coacutedigo Java a puntos de la estructura sintaacutectica para que se realicen ciertas operaciones cuando el analizador sintaacutectico pase por esa zona de la estructura Tambieacuten es posible asociar coacutedigo Java a las piezas sintaacutecticas para que el analizador lexicograacutefico efectuacutee determina-das tareas cuando detecte en la entrada analizada piezas que tengan asociado coacutedigo

loz Accioacuten ligada a una pieza sintaacutecticaSi se asocia un bloque de coacutedigo a una pieza sintaacutectica se consigue que cada vez que se detecta en la en-trada analizada la presencia de esa pieza se ejecute en ese preciso momento el coacutedigo del bloque asociado a la pieza Estas acciones se denominan ldquoacciones lexicograacuteficasrdquo (lexical actions) el coacutedigo del bloque se traslada literalmente al coacutedigo del analizador lexicograacutefico generado para que se ejecute oportunamente cuando se detecte la pieza ligada al bloque de coacutedigo La accioacuten lexicograacutefica se ejecuta inmediatamente antes que la pieza sea comunicada al analizador sintaacutectico

Se puede asociar una accioacuten lexicograacutefica a una pieza sintaacutectica declarada con TOKEN y tambieacuten a una se-cuencia de caracteres declarada con SKIP ademaacutes de eacutestas hay otras dos formas de declaraciones lexico-graacuteficas (no mencionadas en esta introduccioacuten) que tambieacuten pueden llevar una accioacuten asociada

En una especificacioacuten JavaCC la forma de una pieza sintaacutectica (o bien de una secuencia de caracteres) se define poniendo su descripcioacuten delimitada entre llaves detraacutes de la indicacioacuten TOKEN (o bien SKIP) si se quiere asociar una accioacuten lexicograacutefica a la pieza (o a la secuencia) hay que colocar el bloque de coacutedigo de la accioacuten a continuacioacuten de la descripcioacuten de la forma de la pieza (o de la secuencia) el bloque (delimi-tado por llaves) queda situado entre la descripcioacuten y la llave que cierra la especificacioacuten

La pieza sintaacutectica especial que representa el final del fichero (EOF) tambieacuten puede llevar asociada una ac-cioacuten lexicograacutefica para ello hay que poner

ltgt TOKEN lt EOF gt coacutedigo de la accioacuten asociada

para justificar la presencia de los caracteres ltgt delante de la palabra TOKEN hay que basarse en el con-cepto de ldquoestado lexicograacuteficordquo (cuya explicacioacuten queda fuera del alcance de esta introduccioacuten)

∆ Ejemplo 5

Se considera nuevamente el ejemplo de presentacioacuten como resultado del anaacutelisis de una expresioacuten se pretende obtener una relacioacuten de los pareacutentesis contenidos en la expresioacuten en el mismo orden en que se encuentran ademaacutes por cada salto de liacutenea del texto de entrada se grabaraacute en la salida el caraacutecter y

25

se pasaraacute a una nueva liacutenea tambieacuten se anotaraacute la frase Final del trabajo cuando se alcance el final del fichero de entrada Asiacute por ejemplo si la entrada analizada es

(a + b) (( x )) (y + 7)

los resultados obtenidos seraacuten

() (())()

Final del trabajo

En la especificacioacuten JavaCC que se propone como solucioacuten soacutelo se muestran las reglas sintaacutecticas y le -xicograacuteficas (en el resto no hay cambios) en esta solucioacuten puede apreciarse- las piezas sintaacutecticas correspondientes a los pareacutentesis se han definido de manera nominal para po-der asociarles una accioacuten lexicograacutefica- los caracteres incluidos en la pieza de tipo SKIP se han descompuesto en dos grupos ya que la ac-cioacuten lexicograacutefica incorporada soacutelo afecta a uno de ellos

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ltpAbrirgt expresion() ltpCerrargt

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

TOKEN

26

lt pAbrir ( gt Systemoutprint(()

TOKEN lt pCerrar ) gt Systemoutprint())

ltgtTOKEN lt EOF gt Systemoutprintln ( Final del trabajo )

SKIP n Systemoutprint( n)

SKIP | t | r

loz Bloque de declaraciones lexicograacuteficasCada definicioacuten de una pieza sintaacutectica puede llevar asociada una accioacuten lexicograacutefica que se ejecuta in-mediatamente despueacutes de detectar en la entrada un lexema que se ajusta a la definicioacuten de la pieza cada accioacuten lexicograacutefica es un bloque de coacutedigo Java que puede contener sus propias declaraciones Pero a ve-ces interesa tener declaraciones que sean compartidas por todas las acciones lexicograacuteficas con indepen-dencia de la pieza a la que esteacute asociada cada una de ellas

Para incluir declaraciones que puedan usarse desde el coacutedigo asociado a cualquiera de las piezas sintaacutecti -cas se dispone de una construccioacuten con el siguiente formato

TOKEN_MGR_DECLS

que consta de una palabra reservada seguida del siacutembolo dos puntos seguido de un bloque de coacutedigo Java (delimitado por llaves) esta declaracioacuten se puede colocar en cualquier parte de una especificacioacuten JavaCC si se incluye soacutelo puede ponerse una vez

Todas las declaraciones de variables o de meacutetodos puestas en el bloque de declaraciones lexicograacuteficas compartidas son accesibles desde el coacutedigo de cualquier accioacuten lexicograacutefica la justificacioacuten es sencilla el bloque de declaraciones se traslada al principio del coacutedigo del analizador lexicograacutefico generado y las ac-ciones lexicograacuteficas tambieacuten se trasladan a ese analizador

loz Declaraciones lexicograacuteficas predefinidasDe lo expuesto hasta ahora se aprecia que en una accioacuten lexicograacutefica asociada a una pieza sintaacutectica re-sultan accesibles

- las declaraciones de su propio bloque (si tiene declaraciones)- los componentes del bloque de declaraciones compartidas (si estaacute incluido en la especificacioacuten)

Ademaacutes de esto se tienen otras declaraciones pertenecientes a la clase TokenManager que constituye el analizador lexicograacutefico que tambieacuten son utilizables desde cualquiera de las acciones lexico-graacuteficas estas declaraciones pueden considerarse como ldquopredefinidasrdquo ya que son incorporadas automaacuteti-camente por el generador Entre estas declaraciones accesibles se encuentran las siguientes

[ static ] StringBuffer imagecontiene la cadena de caracteres representativa de la pieza sintaacutectica actual que se acaba de recono-cer en le entrada se trata de un campo de la clase y se declara como static cuando el analizador

27

se ha generado con la opcioacuten (por defecto) Static = true si se generase con la opcioacuten Sta-tic = false no quedariacutea declarada como static

Token matchedToken

variable de clase Token que representa la pieza sintaacutectica actual la que se acaba de encontrar en la entrada se trata de una variable local del meacutetodo al que se ha trasladado el coacutedigo de la accioacuten (y por ello accesible desde ese coacutedigo)

El campo image puede usarse tanto si la accioacuten lexicograacutefica estaacute asociada a una pieza declarada con TOKEN como si lo estaacute a una secuencia declarada con SKIP Sin embargo la variable matchedToken no puede usarse en una accioacuten asociada a un patroacuten especificado como SKIP el motivo es que lo declara-do como SKIP no constituye propiamente una pieza sintaacutectica ya que no se va a comunicar al analizador sintaacutectico

∆ Ejemplo 6

Se propone aquiacute una nueva solucioacuten para el ejemplo nordm 4 expuesto anteriormente

Esta nueva solucioacuten tal y como se expone aquiacute admite algunas mejoras ya que su pretensioacuten soacutelo es ser-vir de ilustracioacuten para algunos aspectos que se acaban de citar (maacutes adelante se propone otra solucioacuten al-ternativa)

En la especificacioacuten de la solucioacuten es posible apreciar esto- las acciones lexicograacuteficas pueden llevar sus propias declaraciones las variables nLineaVar nColumnaVar nLineaCte y nColumnaCte se declaran dentro de las acciones

- la variable matchedToken a la que se hace referencia en el coacutedigo de la accioacuten lexicograacutefica aso-ciada a una variable contiene la informacioacuten sobre la pieza sintaacutectica que se acaba de detectar una va-riable de la expresioacuten analizada anaacutelogamente con la referencia relativa a una constante- el nombre image que aparece en las acciones lexicograacuteficas se refiere al campo de la clase Expr-MinTokenManager (analizador lexicograacutefico) noacutetese que en efecto se comunica como un paraacuteme-tro de clase StringBuffer

- la variable numero se emplea para numerar las constantes y variables encontradas

- los nombres beginLine y beginColumn son campos (puacuteblicos) de la clase Token por ello se pueden aplicar a un objeto de esa clase

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

void unaExpresion()

28

expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n StringBuffer lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

TOKEN lt variable [a-z] gt

int nLineaVar nColumnaVar numero++ nLineaVar = matchedTokenbeginLine nColumnaVar = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaVar nColumnaVar)

TOKEN lt constante ( [0-9] ) + gt

int nLineaCte nColumnaCte

29

numero++ nLineaCte = matchedTokenbeginLine nColumnaCte = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaCte nColumnaCte)

SKIP | n | t | r

La especificacioacuten precedente se puede escribir de otra manera maacutes escueta como se indica a continua-cioacuten (soacutelo se muestran las modificaciones) sobre esta nueva especificacioacuten puede mencionarse- se han eliminado por innecesarias las variables declaradas dentro de las acciones lexicograacuteficas- el nombre image que aparece en el bloque de las declaraciones compartidas se refiere al campo de la clase Token noacutetese que en efecto se aplica a un objeto de esa clase y se comunica como un paraacute -metro de tipo String

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

TOKEN lt variable [a-z] gt

numero++ grabarDatosPieza(numero matchedToken)

TOKEN lt constante ( [0-9] ) + gt

numero++ grabarDatosPieza(numero matchedToken)

loz Accioacuten lexicograacutefica comuacutenSi hubiera que realizar la misma tarea tras la deteccioacuten de todas y cada una de las piezas sintaacutecticas una posibilidad incoacutemoda seriacutea repetir el mismo coacutedigo en todas las acciones lexicograacuteficas para evitar tanta reiteracioacuten se tiene la opcioacuten Common_Token_Action su valor por defecto es false pero si se le asigna el valor true es posible definir una ldquoaccioacuten lexicograacutefica comuacutenrdquo que se escribe una uacutenica vez y se aplica a todas las piezas sintaacutecticas

30

Cuando la opcioacuten Common_Token_Action se establece con el valor true es obligado incluir el blo-que de declaraciones lexicograacuteficas compartidas (TOKEN_MGR_DECLS) Ademaacutes en ese bloque se preci-sa la definicioacuten de un meacutetodo cuyo encabezamiento sea

[ static ] void CommonTokenAction(Token t)

el coacutedigo de este meacutetodo constituye la accioacuten lexicograacutefica comuacuten que se ejecutaraacute despueacutes de la deteccioacuten de cualquier pieza sintaacutectica el meacutetodo tiene un paraacutemetro de tipo Token mediante el que se puede co-municar la informacioacuten correspondiente a la pieza que se acaba de encontrar en la entrada analizada

Si una pieza sintaacutectica tiene asociada una accioacuten lexicograacutefica propia la accioacuten lexicograacutefica comuacuten (si es-taacute definida) se ejecutaraacute inmediatamente despueacutes de realizar la accioacuten propia

La accioacuten lexicograacutefica comuacuten se aplica a todas las piezas sintaacutecticas definidas como TOKEN bien sea no-minalmente (en las reglas lexicograacuteficas) o bien anoacutenimamente (incorporadas dentro de las reglas sintaacutecti-cas) sin embargo no se aplica cuando la secuencia acoplada se corresponde con una expresioacuten regular es-pecificada como SKIP (noacutetese que por el contrario una especificacioacuten SKIP siacute puede tener asociada una accioacuten lexicograacutefica propia)

El coacutedigo del meacutetodo CommonTokenAction se traslada como un componente maacutes del bloque de decla-raciones compartidas (TOKEN_MGR_DECLS) al principio de la clase del analizador lexicograacutefico

∆ Ejemplo 7

Se pretende ahora ampliar el ejemplo de presentacioacuten con la obtencioacuten de una relacioacuten numerada de los lexemas de las piezas sintaacutecticas encontradas en la entrada analizada en el caso de las variables y de las constantes antes del lexema se mostraraacute un indicativo (Var Const) por ejemplo si la expresioacuten de la entrada es

( a + 987 ) b

se grabaraacute la salida

( 1Var a 2+ 3Const 987 4) 5 6Var b 7 8

En la solucioacuten que se propone a continuacioacuten se muestran la definicioacuten de las opciones y las reglas sin-taacutecticas y lexicograacuteficas (en el resto no hay modificaciones) puede resaltarse lo siguiente- las llamadas al meacutetodo CommonTokenAction se incorporan automaacuteticamente no hay que incluir-las en la especificacioacuten como paraacutemetro de llamada a este meacutetodo siempre se emplea el objeto que re -presenta la pieza que se acaba de detectar- la accioacuten lexicograacutefica comuacuten realiza la actualizacioacuten del contador de piezas y la grabacioacuten del lexe-ma de la pieza detectada con su nuacutemero de orden- la accioacuten lexicograacutefica comuacuten tambieacuten se aplica a las piezas sintaacutecticas anoacutenimas (pareacutentesis y ope-radores) y a la pieza especial que representa el final del fichero (EOF)

- las piezas sintaacutecticas ltconstantegt y ltvariablegt tienen asociada una accioacuten lexicograacutefica propia que se ejecuta antes de la accioacuten lexicograacutefica comuacuten- los caracteres que se saltan (SKIP) no estaacuten afectados por la accioacuten lexicograacutefica comuacuten

options Ignore_Case = true Common_Token_Action = true

31

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarIndicativo(String indicativo) Systemoutprint(indicativo + )

static void grabarLexema (int num String lexema) Systemoutprint(lexema + + num + n)

static void CommonTokenAction(Token pieza) numero++ grabarLexema(numero piezaimage)

TOKEN lt variable [a-z] gt

grabarIndicativo(Var)

32

TOKEN lt constante ( [0-9] ) + gt

grabarIndicativo(Const)

SKIP | n | t | r

A modo de ejemplo de aplicacioacuten de la accioacuten lexicograacutefica comuacuten en lo que sigue se ofrece una solucioacuten para la grabacioacuten (en el fichero predefinido de salida) del texto analizado con las liacuteneas numeradas

∆ Ejemplo 8 [ Listado del texto analizado ]Se desea completar el ejemplo de presentacioacuten con la grabacioacuten de un listado con las liacuteneas numeradas del texto analizado (noacutetese que una expresioacuten puede escribirse ocupando varias liacuteneas) No se muestra la especificacioacuten sintaacutectica debido a que no hay ninguna modificacioacuten en ella Los aspectos resentildeables de la solucioacuten que se propone son- se usa la variable numLin como contador de liacuteneas

- en la variable linea se van yuxtaponiendo los lexemas de las piezas sintaacutecticas de una liacutenea esta yuxtaposicioacuten se realiza mediante la accioacuten lexicograacutefica comuacuten- las secuencias reconocidas con los patrones especificados como SKIP no implican la ejecucioacuten de la accioacuten lexicograacutefica comuacuten por ello su lexema (almacenado en la variable image) ha de yuxtaponer-se expliacutecitamente- hay que separar el patroacuten correspondiente al fin de liacutenea para detectar el momento en que ha de gra-barse la liacutenea completa cuyo final se ha alcanzado

options Ignore_Case = true Common_Token_Action = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(nnAnalisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

TOKEN_MGR_DECLS static int numLin = 0 static String linea =

33

static void CommonTokenAction(Token pieza) linea = linea + piezaimage

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP lt | t | r gt linea = linea + image

SKIP n numLin++ Systemoutprintln(numLin + + linea) linea =

A modo de recapitulacioacutenↄ Accioacuten lexicograacutefica Accioacuten sintaacutectica

bull accioacuten lexicograacutefica bloque de coacutedigo Java asociado a una pieza sintaacutectica nominal (TOKEN) o a una secuencia de caracteres (SKIP) se ejecuta cuando se detecta en la entrada la pieza o la secuencia corres-pondiente no se puede aplicar a piezas sintaacutecticas anoacutenimas (autodefinidas con comillas)bull accioacuten sintaacutectica bloque de coacutedigo Java asociado a un punto de la estructura sintaacutectica (el punto indica-do por el sitio de la produccioacuten donde se inserta el bloque) se ejecuta cuando el anaacutelisis de la entrada pa-sa por ese punto de la estructura

ↄ Valor de una pieza sintaacutectica comunicadaEl analizador lexicograacutefico comunica al analizador sintaacutectico una pieza a traveacutes de un valor de la clase Token (fichero Tokenjava) un objeto de la clase Token representa las caracteriacutesticas de la pieza comunicada En la clase Token entre otros se tienen los campos

String imagelexema de la pieza

int beginLine beginColumn endLine endColumnposicioacuten del lexema en el fichero analizado (indicada por los nuacutemeros de fila y de columna de su comienzo y de su terminacioacuten)

ↄ Valor asociado a un siacutembolo

34

En las producciones que especifican la sintaxis se pueden incluir asignaciones que sirven para dejar ano-tado el valor asociado a un siacutembolo

bull siacutembolo no terminal valor = nombreSimbolo()el meacutetodo de anaacutelisis asociado a nombreSimbolo se ha declarado de un cierto tipo (distinto del tipo void) valor ha de ser una variable del mismo tipo que el meacutetodo

bull siacutembolo terminal dato = lt nombrePieza gtel valor asignado es el valor de la clase Token representativo de la pieza sintaacutectica (nominal) dato ha de ser una variable de tipo Token

ↄ Miradas sobre la pieza sintaacutectica comunicadaLa pieza sintaacutectica comunicada puede mirarse desde cada uno de los dos analizadores

bull analizador sintaacutecticola pieza comunicada (desde el analizador lexicograacutefico) se tiene anotada en un campo de la clase del analizador sintaacutectico declarado como

(static) public Token token

este valor es accesible desde el coacutedigo de todos los meacutetodos de anaacutelisis sintaacutectico el contenido del campo variacutea cuando se recibe una nueva pieza sintaacutectica

bull analizador lexicograacuteficola pieza dispuesta para ser comunicada (al analizador sintaacutectico) se tiene anotada en una variable local declarada como

Token matchedToken

este valor es accesible para el coacutedigo de todas las acciones lexicograacuteficas

ↄ El lexema de la pieza comunicadaEl lexema de la pieza sintaacutectica comunicada se encuentra disponible en dos sitios distintos que compar-ten el mismo nombre pero que son de distinto tipo y se usan de distinta manera

bull String image

es un campo de la clase TOKEN puede consultarse en relacioacuten con un objeto de esa clase que esteacute disponible

bull StringBuffer image

es un campo de la clase del analizador lexicograacutefico ( ∙ ∙ ∙ ∙ TokenManagerjava) es accesible desde el coacutedigo de la clase y por lo tanto desde todas las acciones lexicograacuteficas (bloques de coacute-digo que estaacuten incorporados al analizador lexicograacutefico)

se trata de un valor disponible tanto para las piezas sintaacutecticas nominales (TOKEN) como para las secuencias que se saltan (SKIP)

ↄ Resumen relativo a la lexicografiacutea

TOKEN pieza sintaacutectica nominal∙ ∙ ∙ pieza sintaacutectica anoacutenimaSKIP secuencia de caracteres que no forman pieza

35

Precisiones sobre el analizador sintaacutectico generadoPuede decirse que en su funcionamiento maacutes sencillo cuando no se emplean las posibilidades de examen por adelantado (ldquolookaheadrdquo) JavaCC comprueba que las reglas sintaacutecticas cumplen la condicioacuten LL(1) y generan un analizador descendente-predictivo-recursivo Pero esta apreciacioacuten hay que matizarla ya que el analizador sintaacutectico generado no se ajusta en sentido estricto al modelo de analizador LL(1)

En el modelo de anaacutelisis LL(1) el orden del examen de las distintas alternativas de expansioacuten de un siacutembo-lo no terminal es indiferente ya que se conocen los siacutembolos directores de cada una de ellas (que resultan ser conjuntos disjuntos) en la implementacioacuten del subprograma de anaacutelisis asociado al siacutembolo no terminal ltNombreSimbgt cuyas producciones son

ltNombreSimbgt = α1

| α2

∙ ∙ ∙

| αn

se podriacutea preguntar en cualquier orden si la pieza actual pertenece al conjunto de siacutembolos directores de las distintas reglas Sin embargo en el analizador sintaacutectico generado por JavaCC la implementacioacuten del meacute-todo asociado al siacutembolo no terminal ltNombreSimbgt no sigue esa pauta

Si las anteriores reglas sintaacutecticas se transcriben en JavaCC de la forma

void nombreSimb () α1-jcc

| α2-jcc

∙ ∙ ∙

| αn-jcc

1

accioacutenlexicograacutefica

comuacuten

Commom -TokenAction2

accioacutenlexicograacutefica

propia

(accioacuten ligadaa una pieza)3

bloque dedeclaracioneslexicograacuteficas

TOKEN_MGR_DECLS4

declaracioneslexicograacuteficaspredefinidas

(disponibilidad

de uso)TOKENsisisisi∙ ∙ ∙sino--SKIPnosisisi

Las indicaciones si de las columnas 3 y 4 son consecuencia de las indicaciones de la columna 2

La accioacuten lexicograacutefica comuacuten se ejecuta despueacutes de la accioacuten lexicograacutefica propia

36

el meacutetodo de anaacutelisis generado procede de la siguiente manera

se van considerando las alternativas sucesivamente en el mismo orden en el que estaacuten especifica-das primero la de α1 (α1-jcc) despueacutes la de α2 (α2-jcc) etc

para cada alternativa seleccionada se comprueba si la pieza por adelantado coincide con alguno de los siacutembolos iniciales de la parte derecha y si hay coincidencia se prosigue el anaacutelisis con ella si no hay coincidencia se pasa a la alternativa siguiente

siguiendo con este orden si se llega al final de las alternativas sin haberse encontrado coincidencia alguna se produce un error sintaacutectico

En el analizador generado por JavaCC hay una peculiaridad que no se tiene en los analizadores LL(1) es-trictamente considerados la palabra vaciacutea siacute se considera como posible siacutembolo inicial de la parte derecha una regla (esto ocurre siempre que la parte derecha es anulable) De esta peculiaridad se deriva una conse-cuencia que ha de tenerse en cuenta al escribir la especificacioacuten dado que siempre es posible considerar la palabra vaciacutea como la siguiente pieza por adelantado si se llega a examinar la alternativa correspondiente a una produccioacuten anulable el analizador generado siempre seleccionaraacute esa alternativa con independencia de la siguiente pieza que esteacute presente en la entrada que queda por analizar

Asiacute pues si alguna de las producciones que definen un siacutembolo no terminal es anulable es importante el or-den en que se escriben las alternativas en una especificacioacuten sintaacutectica JavaCC el funcionamiento del ana-lizador generado siacute depende de la colocacioacuten elegida para las reglas

Para ilustrar estas peculiaridades del analizador sintaacutectico generado por JavaCC a continuacioacuten se expone un ejemplo

∆ Ejemplo 9

Se quiere obtener un analizador leacutexico-sintaacutectico de textos que se ajustan al siguiente formato una se-cuencia de uno o maacutes nombres seguida del siacutembolo igual seguido de un nuacutemero entero si la secuencia tiene maacutes de un nombre han de estar separados bien por una coma en todos los casos o bien por el siacutem-bolo dos puntos en todos los casos esto es son correctas las secuencias

uno dos tres = 123exclusivo = 0Abajo Arriba = 57

y son incorrectas las secuenciasuno dos tres = 123

exclusivo inclusive = 0

fuera dentro = 56

Una gramaacutetica que define las secuencias con este formato es

ltListagt = nombre ltOtrosNombresgt = numero

ltOtrosNombresgt = nombre nombre | nombre nombre | ε

se trata de una gramaacutetica que cumple la condicioacuten LL(1)- el siacutembolo director para la alternativa vaciacutea es el siacutembolo igual es evidente pues que las tres reglas de ltOtrosNombresgt tienen siacutembolos directores distintos

37

- la decisioacuten de abandonar la repeticioacuten del anaacutelisis de un nuevo nombre (en las dos primeras alterna-tivas de ltOtrosNombresgt) se puede tomar ante la presencia del siacutembolo igual

En la implementacioacuten de un analizador sintaacutectico LL(1) en sentido estricto la seleccioacuten de la alternativa vaciacutea para expandir el siacutembolo ltOtrosNombresgt se hariacutea tras comprobar expliacutecitamente que la pie-za sintaacutectica por adelantado es el siacutembolo igual

El funcionamiento del analizador generado por JavaCC depende del orden de colocacioacuten de las reglas A continuacioacuten se exponen varias posibilidades de especificacioacuten y se comenta el analizador generado en cada caso

bull Especificacioacuten primeraSe transcriben literalmente las reglas tal y como se han escrito antes en la notacioacuten BNF noacutetese que para poder codificar la parte derecha de la regla vaciacutea ha de incluirse una accioacuten sintaacutectica vaciacutea

PARSER_BEGIN (Igualdad)

public class Igualdad

public static void main (String[] argum) throws ParseException Igualdad analisis = new Igualdad(Systemin) analisissecuencia() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (Igualdad)

void secuencia() lista() ltEOFgt

void lista() lt nombre gt otrosNombres() = lt numero gt

void otrosNombres() lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt ) |

TOKEN lt nombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN lt numero ( [ 0 - 9 ] )+ gt

SKIP | r | n

38

El analizador generado funciona correctamente puesto que en el meacutetodo otrosNombres se selec-ciona la alternativa vaciacutea despueacutes de haber comprobado que la pieza por adelantado no es siacutembolo ini-cial de ninguna de las dos alternativas precedentes (no es una coma ni un siacutembolo dos puntos)

bull Especificacioacuten segundaSe considera la misma especificacioacuten anterior pero cambiando el orden de colocacioacuten de las reglas ahora la produccioacuten vaciacutea se coloca como primera alternativa (soacutelo se muestran las partes modifica-das)

void otrosNombres() | lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt )

Con la entrada de esta especificacioacuten el generador JavaCC produce un mensaje en el que se avisa de que las dos alternativas que estaacuten colocadas detraacutes de la alternativa vaciacutea nunca podraacuten seleccionarse aun asiacute se genera un analizador Pero es un analizador que funciona incorrectamente asiacute por ejemplo la entrada

mxa nyz = 57

se considera incorrecta despueacutes de visto el primer nombre se intenta aplicar la produccioacuten vaciacutea (se espera encontrar el siacutembolo igual) pero habriacutea que aplicar la regla cuyo siacutembolo director es la coma

bull Especificacioacuten terceraSe utilizan las posibilidades de la notacioacuten BNF-Ampliada para prescindir de la especificacioacuten expliacuteci-ta de la palabra vaciacutea se aprovecha que JavaCC admite el operador + para indicar repeticiones de una o maacutes veces la alternativa vaciacutea estaacute impliacutecitamente contemplada en la posibilidad de cero repeti-ciones que contempla el operador (soacutelo se muestran las partes modificadas en la especificacioacuten)

void otrosNombres() ( lt nombre gt )+ | ( lt nombre gt )

El meacutetodo otrosNombres() generado hace las comprobaciones en el siguiente orden

- se comprueba si la pieza por adelantado es una coma (siacutembolo inicial de la primera alternativa) y en este caso se selecciona la primera regla- si no es una coma se comprueba si es un siacutembolo dos puntos (siacutembolo inicial de la segunda alter-nativa) y en este caso se selecciona la segunda regla- si no es un siacutembolo dos puntos se considera la palabra vaciacutea como siacutembolo inicial (siempre posi-ble impliacutecitamente) para la segunda alternativa en este caso para que no haya error la pieza por adelantado ha de ser un siacutembolo igual

Se trata pues de un analizador que funciona correctamente

bull Especificacioacuten cuarta

39

Se pone la misma especificacioacuten anterior pero cambiando el orden de consideracioacuten de la regla vaciacutea ahora se incluye impliacutecitamente en la primera alternativa (soacutelo se muestran las partes modificadas)

void otrosNombres() ( lt nombre gt ) | ( lt nombre gt )+

Para esta especificacioacuten el generador JavaCC emite un aviso en el que se indica que la segunda alter -nativa nunca se seleccionaraacute no obstante se genera un analizador que no funciona como se supone que deberiacutea hacerlo por ejemplo el anaacutelisis de la entrada

nyz mxa = 1

produce un error despueacutes de tratado el primer nombre se intenta aplicar la produccioacuten vaciacutea (corres-pondiente a la repeticioacuten de cero veces contemplada en la primera regla) por lo que no se encuentra el esperado siacutembolo igual

40

Page 24: JavaCC

cada vez que el analizador sintaacutectico recibe una pieza sintaacutectica comunicada por el analizador lexicograacutefico se deja anotada en el campo token y ese contenido no se altera hasta que sea proporcionada la siguiente pieza de la entrada

∆ Ejemplo 4

Se considera una ampliacioacuten del ejemplo de presentacioacuten ademaacutes del anaacutelisis de la entrada se pretende obtener una relacioacuten numerada de las variables y las constantes indicando el nuacutemero de liacutenea y el nuacuteme-ro de columna donde estaacuten situadas por ejemplo si en la entrada se tiene la expresioacuten (grabada en dos liacuteneas)

x (y + 11) + 22

en la salida se tendraacute

1- x linea 1 columna 1

2- y linea 1 columna 6

3- 11 linea 1 columna 10

4- 22 linea 2 columna 5

En la especificacioacuten que se propone como solucioacuten puede apreciarse- en la clase del analizador sintaacutectico se han incluido meacutetodos auxiliares en los que se usan los nom-bres image beginLine beginColumn que son campos de un objeto de clase Token

- en la especificacioacuten sintaacutectica de factor se emplea la variable pieza de tipo Token para reco-ger las caracteriacutesticas de las piezas sintaacutecticas detectadas- la variable numero hace de contador para numerar las constantes y variables

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin

static int numero = 0

private static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

private static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

private static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

23

PARSER_END (ExprMin)

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() Token pieza pieza = ltconstantegt numero++ grabarDatosPieza(numero pieza) | pieza = ltvariablegt numero++ grabarDatosPieza(numero pieza) | ( expresion() )

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP | n | t | r

Si se agrupan las dos acciones sintaacutecticas comunes la especificacioacuten de factor tambieacuten puede escri-birse de esta forma

void factor() Token pieza ( pieza = ltconstantegt | pieza = ltvariablegt

) numero++ grabarDatosPieza(numero pieza)

| ( expresion() )

24

Si se accede al campo token de la clase del analizador sintaacutectico se puede resolver este ejemplo pres-cindiendo de la variable pieza

void factor() ( ltconstantegt | ltvariablegt

) numero++ grabarDatosPieza(numero token)

| ( expresion() )

Tareas asociadas a las piezas sintaacutecticasYa se ha visto que es posible asociar bloques de coacutedigo Java a puntos de la estructura sintaacutectica para que se realicen ciertas operaciones cuando el analizador sintaacutectico pase por esa zona de la estructura Tambieacuten es posible asociar coacutedigo Java a las piezas sintaacutecticas para que el analizador lexicograacutefico efectuacutee determina-das tareas cuando detecte en la entrada analizada piezas que tengan asociado coacutedigo

loz Accioacuten ligada a una pieza sintaacutecticaSi se asocia un bloque de coacutedigo a una pieza sintaacutectica se consigue que cada vez que se detecta en la en-trada analizada la presencia de esa pieza se ejecute en ese preciso momento el coacutedigo del bloque asociado a la pieza Estas acciones se denominan ldquoacciones lexicograacuteficasrdquo (lexical actions) el coacutedigo del bloque se traslada literalmente al coacutedigo del analizador lexicograacutefico generado para que se ejecute oportunamente cuando se detecte la pieza ligada al bloque de coacutedigo La accioacuten lexicograacutefica se ejecuta inmediatamente antes que la pieza sea comunicada al analizador sintaacutectico

Se puede asociar una accioacuten lexicograacutefica a una pieza sintaacutectica declarada con TOKEN y tambieacuten a una se-cuencia de caracteres declarada con SKIP ademaacutes de eacutestas hay otras dos formas de declaraciones lexico-graacuteficas (no mencionadas en esta introduccioacuten) que tambieacuten pueden llevar una accioacuten asociada

En una especificacioacuten JavaCC la forma de una pieza sintaacutectica (o bien de una secuencia de caracteres) se define poniendo su descripcioacuten delimitada entre llaves detraacutes de la indicacioacuten TOKEN (o bien SKIP) si se quiere asociar una accioacuten lexicograacutefica a la pieza (o a la secuencia) hay que colocar el bloque de coacutedigo de la accioacuten a continuacioacuten de la descripcioacuten de la forma de la pieza (o de la secuencia) el bloque (delimi-tado por llaves) queda situado entre la descripcioacuten y la llave que cierra la especificacioacuten

La pieza sintaacutectica especial que representa el final del fichero (EOF) tambieacuten puede llevar asociada una ac-cioacuten lexicograacutefica para ello hay que poner

ltgt TOKEN lt EOF gt coacutedigo de la accioacuten asociada

para justificar la presencia de los caracteres ltgt delante de la palabra TOKEN hay que basarse en el con-cepto de ldquoestado lexicograacuteficordquo (cuya explicacioacuten queda fuera del alcance de esta introduccioacuten)

∆ Ejemplo 5

Se considera nuevamente el ejemplo de presentacioacuten como resultado del anaacutelisis de una expresioacuten se pretende obtener una relacioacuten de los pareacutentesis contenidos en la expresioacuten en el mismo orden en que se encuentran ademaacutes por cada salto de liacutenea del texto de entrada se grabaraacute en la salida el caraacutecter y

25

se pasaraacute a una nueva liacutenea tambieacuten se anotaraacute la frase Final del trabajo cuando se alcance el final del fichero de entrada Asiacute por ejemplo si la entrada analizada es

(a + b) (( x )) (y + 7)

los resultados obtenidos seraacuten

() (())()

Final del trabajo

En la especificacioacuten JavaCC que se propone como solucioacuten soacutelo se muestran las reglas sintaacutecticas y le -xicograacuteficas (en el resto no hay cambios) en esta solucioacuten puede apreciarse- las piezas sintaacutecticas correspondientes a los pareacutentesis se han definido de manera nominal para po-der asociarles una accioacuten lexicograacutefica- los caracteres incluidos en la pieza de tipo SKIP se han descompuesto en dos grupos ya que la ac-cioacuten lexicograacutefica incorporada soacutelo afecta a uno de ellos

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ltpAbrirgt expresion() ltpCerrargt

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

TOKEN

26

lt pAbrir ( gt Systemoutprint(()

TOKEN lt pCerrar ) gt Systemoutprint())

ltgtTOKEN lt EOF gt Systemoutprintln ( Final del trabajo )

SKIP n Systemoutprint( n)

SKIP | t | r

loz Bloque de declaraciones lexicograacuteficasCada definicioacuten de una pieza sintaacutectica puede llevar asociada una accioacuten lexicograacutefica que se ejecuta in-mediatamente despueacutes de detectar en la entrada un lexema que se ajusta a la definicioacuten de la pieza cada accioacuten lexicograacutefica es un bloque de coacutedigo Java que puede contener sus propias declaraciones Pero a ve-ces interesa tener declaraciones que sean compartidas por todas las acciones lexicograacuteficas con indepen-dencia de la pieza a la que esteacute asociada cada una de ellas

Para incluir declaraciones que puedan usarse desde el coacutedigo asociado a cualquiera de las piezas sintaacutecti -cas se dispone de una construccioacuten con el siguiente formato

TOKEN_MGR_DECLS

que consta de una palabra reservada seguida del siacutembolo dos puntos seguido de un bloque de coacutedigo Java (delimitado por llaves) esta declaracioacuten se puede colocar en cualquier parte de una especificacioacuten JavaCC si se incluye soacutelo puede ponerse una vez

Todas las declaraciones de variables o de meacutetodos puestas en el bloque de declaraciones lexicograacuteficas compartidas son accesibles desde el coacutedigo de cualquier accioacuten lexicograacutefica la justificacioacuten es sencilla el bloque de declaraciones se traslada al principio del coacutedigo del analizador lexicograacutefico generado y las ac-ciones lexicograacuteficas tambieacuten se trasladan a ese analizador

loz Declaraciones lexicograacuteficas predefinidasDe lo expuesto hasta ahora se aprecia que en una accioacuten lexicograacutefica asociada a una pieza sintaacutectica re-sultan accesibles

- las declaraciones de su propio bloque (si tiene declaraciones)- los componentes del bloque de declaraciones compartidas (si estaacute incluido en la especificacioacuten)

Ademaacutes de esto se tienen otras declaraciones pertenecientes a la clase TokenManager que constituye el analizador lexicograacutefico que tambieacuten son utilizables desde cualquiera de las acciones lexico-graacuteficas estas declaraciones pueden considerarse como ldquopredefinidasrdquo ya que son incorporadas automaacuteti-camente por el generador Entre estas declaraciones accesibles se encuentran las siguientes

[ static ] StringBuffer imagecontiene la cadena de caracteres representativa de la pieza sintaacutectica actual que se acaba de recono-cer en le entrada se trata de un campo de la clase y se declara como static cuando el analizador

27

se ha generado con la opcioacuten (por defecto) Static = true si se generase con la opcioacuten Sta-tic = false no quedariacutea declarada como static

Token matchedToken

variable de clase Token que representa la pieza sintaacutectica actual la que se acaba de encontrar en la entrada se trata de una variable local del meacutetodo al que se ha trasladado el coacutedigo de la accioacuten (y por ello accesible desde ese coacutedigo)

El campo image puede usarse tanto si la accioacuten lexicograacutefica estaacute asociada a una pieza declarada con TOKEN como si lo estaacute a una secuencia declarada con SKIP Sin embargo la variable matchedToken no puede usarse en una accioacuten asociada a un patroacuten especificado como SKIP el motivo es que lo declara-do como SKIP no constituye propiamente una pieza sintaacutectica ya que no se va a comunicar al analizador sintaacutectico

∆ Ejemplo 6

Se propone aquiacute una nueva solucioacuten para el ejemplo nordm 4 expuesto anteriormente

Esta nueva solucioacuten tal y como se expone aquiacute admite algunas mejoras ya que su pretensioacuten soacutelo es ser-vir de ilustracioacuten para algunos aspectos que se acaban de citar (maacutes adelante se propone otra solucioacuten al-ternativa)

En la especificacioacuten de la solucioacuten es posible apreciar esto- las acciones lexicograacuteficas pueden llevar sus propias declaraciones las variables nLineaVar nColumnaVar nLineaCte y nColumnaCte se declaran dentro de las acciones

- la variable matchedToken a la que se hace referencia en el coacutedigo de la accioacuten lexicograacutefica aso-ciada a una variable contiene la informacioacuten sobre la pieza sintaacutectica que se acaba de detectar una va-riable de la expresioacuten analizada anaacutelogamente con la referencia relativa a una constante- el nombre image que aparece en las acciones lexicograacuteficas se refiere al campo de la clase Expr-MinTokenManager (analizador lexicograacutefico) noacutetese que en efecto se comunica como un paraacuteme-tro de clase StringBuffer

- la variable numero se emplea para numerar las constantes y variables encontradas

- los nombres beginLine y beginColumn son campos (puacuteblicos) de la clase Token por ello se pueden aplicar a un objeto de esa clase

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

void unaExpresion()

28

expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n StringBuffer lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

TOKEN lt variable [a-z] gt

int nLineaVar nColumnaVar numero++ nLineaVar = matchedTokenbeginLine nColumnaVar = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaVar nColumnaVar)

TOKEN lt constante ( [0-9] ) + gt

int nLineaCte nColumnaCte

29

numero++ nLineaCte = matchedTokenbeginLine nColumnaCte = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaCte nColumnaCte)

SKIP | n | t | r

La especificacioacuten precedente se puede escribir de otra manera maacutes escueta como se indica a continua-cioacuten (soacutelo se muestran las modificaciones) sobre esta nueva especificacioacuten puede mencionarse- se han eliminado por innecesarias las variables declaradas dentro de las acciones lexicograacuteficas- el nombre image que aparece en el bloque de las declaraciones compartidas se refiere al campo de la clase Token noacutetese que en efecto se aplica a un objeto de esa clase y se comunica como un paraacute -metro de tipo String

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

TOKEN lt variable [a-z] gt

numero++ grabarDatosPieza(numero matchedToken)

TOKEN lt constante ( [0-9] ) + gt

numero++ grabarDatosPieza(numero matchedToken)

loz Accioacuten lexicograacutefica comuacutenSi hubiera que realizar la misma tarea tras la deteccioacuten de todas y cada una de las piezas sintaacutecticas una posibilidad incoacutemoda seriacutea repetir el mismo coacutedigo en todas las acciones lexicograacuteficas para evitar tanta reiteracioacuten se tiene la opcioacuten Common_Token_Action su valor por defecto es false pero si se le asigna el valor true es posible definir una ldquoaccioacuten lexicograacutefica comuacutenrdquo que se escribe una uacutenica vez y se aplica a todas las piezas sintaacutecticas

30

Cuando la opcioacuten Common_Token_Action se establece con el valor true es obligado incluir el blo-que de declaraciones lexicograacuteficas compartidas (TOKEN_MGR_DECLS) Ademaacutes en ese bloque se preci-sa la definicioacuten de un meacutetodo cuyo encabezamiento sea

[ static ] void CommonTokenAction(Token t)

el coacutedigo de este meacutetodo constituye la accioacuten lexicograacutefica comuacuten que se ejecutaraacute despueacutes de la deteccioacuten de cualquier pieza sintaacutectica el meacutetodo tiene un paraacutemetro de tipo Token mediante el que se puede co-municar la informacioacuten correspondiente a la pieza que se acaba de encontrar en la entrada analizada

Si una pieza sintaacutectica tiene asociada una accioacuten lexicograacutefica propia la accioacuten lexicograacutefica comuacuten (si es-taacute definida) se ejecutaraacute inmediatamente despueacutes de realizar la accioacuten propia

La accioacuten lexicograacutefica comuacuten se aplica a todas las piezas sintaacutecticas definidas como TOKEN bien sea no-minalmente (en las reglas lexicograacuteficas) o bien anoacutenimamente (incorporadas dentro de las reglas sintaacutecti-cas) sin embargo no se aplica cuando la secuencia acoplada se corresponde con una expresioacuten regular es-pecificada como SKIP (noacutetese que por el contrario una especificacioacuten SKIP siacute puede tener asociada una accioacuten lexicograacutefica propia)

El coacutedigo del meacutetodo CommonTokenAction se traslada como un componente maacutes del bloque de decla-raciones compartidas (TOKEN_MGR_DECLS) al principio de la clase del analizador lexicograacutefico

∆ Ejemplo 7

Se pretende ahora ampliar el ejemplo de presentacioacuten con la obtencioacuten de una relacioacuten numerada de los lexemas de las piezas sintaacutecticas encontradas en la entrada analizada en el caso de las variables y de las constantes antes del lexema se mostraraacute un indicativo (Var Const) por ejemplo si la expresioacuten de la entrada es

( a + 987 ) b

se grabaraacute la salida

( 1Var a 2+ 3Const 987 4) 5 6Var b 7 8

En la solucioacuten que se propone a continuacioacuten se muestran la definicioacuten de las opciones y las reglas sin-taacutecticas y lexicograacuteficas (en el resto no hay modificaciones) puede resaltarse lo siguiente- las llamadas al meacutetodo CommonTokenAction se incorporan automaacuteticamente no hay que incluir-las en la especificacioacuten como paraacutemetro de llamada a este meacutetodo siempre se emplea el objeto que re -presenta la pieza que se acaba de detectar- la accioacuten lexicograacutefica comuacuten realiza la actualizacioacuten del contador de piezas y la grabacioacuten del lexe-ma de la pieza detectada con su nuacutemero de orden- la accioacuten lexicograacutefica comuacuten tambieacuten se aplica a las piezas sintaacutecticas anoacutenimas (pareacutentesis y ope-radores) y a la pieza especial que representa el final del fichero (EOF)

- las piezas sintaacutecticas ltconstantegt y ltvariablegt tienen asociada una accioacuten lexicograacutefica propia que se ejecuta antes de la accioacuten lexicograacutefica comuacuten- los caracteres que se saltan (SKIP) no estaacuten afectados por la accioacuten lexicograacutefica comuacuten

options Ignore_Case = true Common_Token_Action = true

31

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarIndicativo(String indicativo) Systemoutprint(indicativo + )

static void grabarLexema (int num String lexema) Systemoutprint(lexema + + num + n)

static void CommonTokenAction(Token pieza) numero++ grabarLexema(numero piezaimage)

TOKEN lt variable [a-z] gt

grabarIndicativo(Var)

32

TOKEN lt constante ( [0-9] ) + gt

grabarIndicativo(Const)

SKIP | n | t | r

A modo de ejemplo de aplicacioacuten de la accioacuten lexicograacutefica comuacuten en lo que sigue se ofrece una solucioacuten para la grabacioacuten (en el fichero predefinido de salida) del texto analizado con las liacuteneas numeradas

∆ Ejemplo 8 [ Listado del texto analizado ]Se desea completar el ejemplo de presentacioacuten con la grabacioacuten de un listado con las liacuteneas numeradas del texto analizado (noacutetese que una expresioacuten puede escribirse ocupando varias liacuteneas) No se muestra la especificacioacuten sintaacutectica debido a que no hay ninguna modificacioacuten en ella Los aspectos resentildeables de la solucioacuten que se propone son- se usa la variable numLin como contador de liacuteneas

- en la variable linea se van yuxtaponiendo los lexemas de las piezas sintaacutecticas de una liacutenea esta yuxtaposicioacuten se realiza mediante la accioacuten lexicograacutefica comuacuten- las secuencias reconocidas con los patrones especificados como SKIP no implican la ejecucioacuten de la accioacuten lexicograacutefica comuacuten por ello su lexema (almacenado en la variable image) ha de yuxtaponer-se expliacutecitamente- hay que separar el patroacuten correspondiente al fin de liacutenea para detectar el momento en que ha de gra-barse la liacutenea completa cuyo final se ha alcanzado

options Ignore_Case = true Common_Token_Action = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(nnAnalisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

TOKEN_MGR_DECLS static int numLin = 0 static String linea =

33

static void CommonTokenAction(Token pieza) linea = linea + piezaimage

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP lt | t | r gt linea = linea + image

SKIP n numLin++ Systemoutprintln(numLin + + linea) linea =

A modo de recapitulacioacutenↄ Accioacuten lexicograacutefica Accioacuten sintaacutectica

bull accioacuten lexicograacutefica bloque de coacutedigo Java asociado a una pieza sintaacutectica nominal (TOKEN) o a una secuencia de caracteres (SKIP) se ejecuta cuando se detecta en la entrada la pieza o la secuencia corres-pondiente no se puede aplicar a piezas sintaacutecticas anoacutenimas (autodefinidas con comillas)bull accioacuten sintaacutectica bloque de coacutedigo Java asociado a un punto de la estructura sintaacutectica (el punto indica-do por el sitio de la produccioacuten donde se inserta el bloque) se ejecuta cuando el anaacutelisis de la entrada pa-sa por ese punto de la estructura

ↄ Valor de una pieza sintaacutectica comunicadaEl analizador lexicograacutefico comunica al analizador sintaacutectico una pieza a traveacutes de un valor de la clase Token (fichero Tokenjava) un objeto de la clase Token representa las caracteriacutesticas de la pieza comunicada En la clase Token entre otros se tienen los campos

String imagelexema de la pieza

int beginLine beginColumn endLine endColumnposicioacuten del lexema en el fichero analizado (indicada por los nuacutemeros de fila y de columna de su comienzo y de su terminacioacuten)

ↄ Valor asociado a un siacutembolo

34

En las producciones que especifican la sintaxis se pueden incluir asignaciones que sirven para dejar ano-tado el valor asociado a un siacutembolo

bull siacutembolo no terminal valor = nombreSimbolo()el meacutetodo de anaacutelisis asociado a nombreSimbolo se ha declarado de un cierto tipo (distinto del tipo void) valor ha de ser una variable del mismo tipo que el meacutetodo

bull siacutembolo terminal dato = lt nombrePieza gtel valor asignado es el valor de la clase Token representativo de la pieza sintaacutectica (nominal) dato ha de ser una variable de tipo Token

ↄ Miradas sobre la pieza sintaacutectica comunicadaLa pieza sintaacutectica comunicada puede mirarse desde cada uno de los dos analizadores

bull analizador sintaacutecticola pieza comunicada (desde el analizador lexicograacutefico) se tiene anotada en un campo de la clase del analizador sintaacutectico declarado como

(static) public Token token

este valor es accesible desde el coacutedigo de todos los meacutetodos de anaacutelisis sintaacutectico el contenido del campo variacutea cuando se recibe una nueva pieza sintaacutectica

bull analizador lexicograacuteficola pieza dispuesta para ser comunicada (al analizador sintaacutectico) se tiene anotada en una variable local declarada como

Token matchedToken

este valor es accesible para el coacutedigo de todas las acciones lexicograacuteficas

ↄ El lexema de la pieza comunicadaEl lexema de la pieza sintaacutectica comunicada se encuentra disponible en dos sitios distintos que compar-ten el mismo nombre pero que son de distinto tipo y se usan de distinta manera

bull String image

es un campo de la clase TOKEN puede consultarse en relacioacuten con un objeto de esa clase que esteacute disponible

bull StringBuffer image

es un campo de la clase del analizador lexicograacutefico ( ∙ ∙ ∙ ∙ TokenManagerjava) es accesible desde el coacutedigo de la clase y por lo tanto desde todas las acciones lexicograacuteficas (bloques de coacute-digo que estaacuten incorporados al analizador lexicograacutefico)

se trata de un valor disponible tanto para las piezas sintaacutecticas nominales (TOKEN) como para las secuencias que se saltan (SKIP)

ↄ Resumen relativo a la lexicografiacutea

TOKEN pieza sintaacutectica nominal∙ ∙ ∙ pieza sintaacutectica anoacutenimaSKIP secuencia de caracteres que no forman pieza

35

Precisiones sobre el analizador sintaacutectico generadoPuede decirse que en su funcionamiento maacutes sencillo cuando no se emplean las posibilidades de examen por adelantado (ldquolookaheadrdquo) JavaCC comprueba que las reglas sintaacutecticas cumplen la condicioacuten LL(1) y generan un analizador descendente-predictivo-recursivo Pero esta apreciacioacuten hay que matizarla ya que el analizador sintaacutectico generado no se ajusta en sentido estricto al modelo de analizador LL(1)

En el modelo de anaacutelisis LL(1) el orden del examen de las distintas alternativas de expansioacuten de un siacutembo-lo no terminal es indiferente ya que se conocen los siacutembolos directores de cada una de ellas (que resultan ser conjuntos disjuntos) en la implementacioacuten del subprograma de anaacutelisis asociado al siacutembolo no terminal ltNombreSimbgt cuyas producciones son

ltNombreSimbgt = α1

| α2

∙ ∙ ∙

| αn

se podriacutea preguntar en cualquier orden si la pieza actual pertenece al conjunto de siacutembolos directores de las distintas reglas Sin embargo en el analizador sintaacutectico generado por JavaCC la implementacioacuten del meacute-todo asociado al siacutembolo no terminal ltNombreSimbgt no sigue esa pauta

Si las anteriores reglas sintaacutecticas se transcriben en JavaCC de la forma

void nombreSimb () α1-jcc

| α2-jcc

∙ ∙ ∙

| αn-jcc

1

accioacutenlexicograacutefica

comuacuten

Commom -TokenAction2

accioacutenlexicograacutefica

propia

(accioacuten ligadaa una pieza)3

bloque dedeclaracioneslexicograacuteficas

TOKEN_MGR_DECLS4

declaracioneslexicograacuteficaspredefinidas

(disponibilidad

de uso)TOKENsisisisi∙ ∙ ∙sino--SKIPnosisisi

Las indicaciones si de las columnas 3 y 4 son consecuencia de las indicaciones de la columna 2

La accioacuten lexicograacutefica comuacuten se ejecuta despueacutes de la accioacuten lexicograacutefica propia

36

el meacutetodo de anaacutelisis generado procede de la siguiente manera

se van considerando las alternativas sucesivamente en el mismo orden en el que estaacuten especifica-das primero la de α1 (α1-jcc) despueacutes la de α2 (α2-jcc) etc

para cada alternativa seleccionada se comprueba si la pieza por adelantado coincide con alguno de los siacutembolos iniciales de la parte derecha y si hay coincidencia se prosigue el anaacutelisis con ella si no hay coincidencia se pasa a la alternativa siguiente

siguiendo con este orden si se llega al final de las alternativas sin haberse encontrado coincidencia alguna se produce un error sintaacutectico

En el analizador generado por JavaCC hay una peculiaridad que no se tiene en los analizadores LL(1) es-trictamente considerados la palabra vaciacutea siacute se considera como posible siacutembolo inicial de la parte derecha una regla (esto ocurre siempre que la parte derecha es anulable) De esta peculiaridad se deriva una conse-cuencia que ha de tenerse en cuenta al escribir la especificacioacuten dado que siempre es posible considerar la palabra vaciacutea como la siguiente pieza por adelantado si se llega a examinar la alternativa correspondiente a una produccioacuten anulable el analizador generado siempre seleccionaraacute esa alternativa con independencia de la siguiente pieza que esteacute presente en la entrada que queda por analizar

Asiacute pues si alguna de las producciones que definen un siacutembolo no terminal es anulable es importante el or-den en que se escriben las alternativas en una especificacioacuten sintaacutectica JavaCC el funcionamiento del ana-lizador generado siacute depende de la colocacioacuten elegida para las reglas

Para ilustrar estas peculiaridades del analizador sintaacutectico generado por JavaCC a continuacioacuten se expone un ejemplo

∆ Ejemplo 9

Se quiere obtener un analizador leacutexico-sintaacutectico de textos que se ajustan al siguiente formato una se-cuencia de uno o maacutes nombres seguida del siacutembolo igual seguido de un nuacutemero entero si la secuencia tiene maacutes de un nombre han de estar separados bien por una coma en todos los casos o bien por el siacutem-bolo dos puntos en todos los casos esto es son correctas las secuencias

uno dos tres = 123exclusivo = 0Abajo Arriba = 57

y son incorrectas las secuenciasuno dos tres = 123

exclusivo inclusive = 0

fuera dentro = 56

Una gramaacutetica que define las secuencias con este formato es

ltListagt = nombre ltOtrosNombresgt = numero

ltOtrosNombresgt = nombre nombre | nombre nombre | ε

se trata de una gramaacutetica que cumple la condicioacuten LL(1)- el siacutembolo director para la alternativa vaciacutea es el siacutembolo igual es evidente pues que las tres reglas de ltOtrosNombresgt tienen siacutembolos directores distintos

37

- la decisioacuten de abandonar la repeticioacuten del anaacutelisis de un nuevo nombre (en las dos primeras alterna-tivas de ltOtrosNombresgt) se puede tomar ante la presencia del siacutembolo igual

En la implementacioacuten de un analizador sintaacutectico LL(1) en sentido estricto la seleccioacuten de la alternativa vaciacutea para expandir el siacutembolo ltOtrosNombresgt se hariacutea tras comprobar expliacutecitamente que la pie-za sintaacutectica por adelantado es el siacutembolo igual

El funcionamiento del analizador generado por JavaCC depende del orden de colocacioacuten de las reglas A continuacioacuten se exponen varias posibilidades de especificacioacuten y se comenta el analizador generado en cada caso

bull Especificacioacuten primeraSe transcriben literalmente las reglas tal y como se han escrito antes en la notacioacuten BNF noacutetese que para poder codificar la parte derecha de la regla vaciacutea ha de incluirse una accioacuten sintaacutectica vaciacutea

PARSER_BEGIN (Igualdad)

public class Igualdad

public static void main (String[] argum) throws ParseException Igualdad analisis = new Igualdad(Systemin) analisissecuencia() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (Igualdad)

void secuencia() lista() ltEOFgt

void lista() lt nombre gt otrosNombres() = lt numero gt

void otrosNombres() lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt ) |

TOKEN lt nombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN lt numero ( [ 0 - 9 ] )+ gt

SKIP | r | n

38

El analizador generado funciona correctamente puesto que en el meacutetodo otrosNombres se selec-ciona la alternativa vaciacutea despueacutes de haber comprobado que la pieza por adelantado no es siacutembolo ini-cial de ninguna de las dos alternativas precedentes (no es una coma ni un siacutembolo dos puntos)

bull Especificacioacuten segundaSe considera la misma especificacioacuten anterior pero cambiando el orden de colocacioacuten de las reglas ahora la produccioacuten vaciacutea se coloca como primera alternativa (soacutelo se muestran las partes modifica-das)

void otrosNombres() | lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt )

Con la entrada de esta especificacioacuten el generador JavaCC produce un mensaje en el que se avisa de que las dos alternativas que estaacuten colocadas detraacutes de la alternativa vaciacutea nunca podraacuten seleccionarse aun asiacute se genera un analizador Pero es un analizador que funciona incorrectamente asiacute por ejemplo la entrada

mxa nyz = 57

se considera incorrecta despueacutes de visto el primer nombre se intenta aplicar la produccioacuten vaciacutea (se espera encontrar el siacutembolo igual) pero habriacutea que aplicar la regla cuyo siacutembolo director es la coma

bull Especificacioacuten terceraSe utilizan las posibilidades de la notacioacuten BNF-Ampliada para prescindir de la especificacioacuten expliacuteci-ta de la palabra vaciacutea se aprovecha que JavaCC admite el operador + para indicar repeticiones de una o maacutes veces la alternativa vaciacutea estaacute impliacutecitamente contemplada en la posibilidad de cero repeti-ciones que contempla el operador (soacutelo se muestran las partes modificadas en la especificacioacuten)

void otrosNombres() ( lt nombre gt )+ | ( lt nombre gt )

El meacutetodo otrosNombres() generado hace las comprobaciones en el siguiente orden

- se comprueba si la pieza por adelantado es una coma (siacutembolo inicial de la primera alternativa) y en este caso se selecciona la primera regla- si no es una coma se comprueba si es un siacutembolo dos puntos (siacutembolo inicial de la segunda alter-nativa) y en este caso se selecciona la segunda regla- si no es un siacutembolo dos puntos se considera la palabra vaciacutea como siacutembolo inicial (siempre posi-ble impliacutecitamente) para la segunda alternativa en este caso para que no haya error la pieza por adelantado ha de ser un siacutembolo igual

Se trata pues de un analizador que funciona correctamente

bull Especificacioacuten cuarta

39

Se pone la misma especificacioacuten anterior pero cambiando el orden de consideracioacuten de la regla vaciacutea ahora se incluye impliacutecitamente en la primera alternativa (soacutelo se muestran las partes modificadas)

void otrosNombres() ( lt nombre gt ) | ( lt nombre gt )+

Para esta especificacioacuten el generador JavaCC emite un aviso en el que se indica que la segunda alter -nativa nunca se seleccionaraacute no obstante se genera un analizador que no funciona como se supone que deberiacutea hacerlo por ejemplo el anaacutelisis de la entrada

nyz mxa = 1

produce un error despueacutes de tratado el primer nombre se intenta aplicar la produccioacuten vaciacutea (corres-pondiente a la repeticioacuten de cero veces contemplada en la primera regla) por lo que no se encuentra el esperado siacutembolo igual

40

Page 25: JavaCC

PARSER_END (ExprMin)

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() Token pieza pieza = ltconstantegt numero++ grabarDatosPieza(numero pieza) | pieza = ltvariablegt numero++ grabarDatosPieza(numero pieza) | ( expresion() )

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP | n | t | r

Si se agrupan las dos acciones sintaacutecticas comunes la especificacioacuten de factor tambieacuten puede escri-birse de esta forma

void factor() Token pieza ( pieza = ltconstantegt | pieza = ltvariablegt

) numero++ grabarDatosPieza(numero pieza)

| ( expresion() )

24

Si se accede al campo token de la clase del analizador sintaacutectico se puede resolver este ejemplo pres-cindiendo de la variable pieza

void factor() ( ltconstantegt | ltvariablegt

) numero++ grabarDatosPieza(numero token)

| ( expresion() )

Tareas asociadas a las piezas sintaacutecticasYa se ha visto que es posible asociar bloques de coacutedigo Java a puntos de la estructura sintaacutectica para que se realicen ciertas operaciones cuando el analizador sintaacutectico pase por esa zona de la estructura Tambieacuten es posible asociar coacutedigo Java a las piezas sintaacutecticas para que el analizador lexicograacutefico efectuacutee determina-das tareas cuando detecte en la entrada analizada piezas que tengan asociado coacutedigo

loz Accioacuten ligada a una pieza sintaacutecticaSi se asocia un bloque de coacutedigo a una pieza sintaacutectica se consigue que cada vez que se detecta en la en-trada analizada la presencia de esa pieza se ejecute en ese preciso momento el coacutedigo del bloque asociado a la pieza Estas acciones se denominan ldquoacciones lexicograacuteficasrdquo (lexical actions) el coacutedigo del bloque se traslada literalmente al coacutedigo del analizador lexicograacutefico generado para que se ejecute oportunamente cuando se detecte la pieza ligada al bloque de coacutedigo La accioacuten lexicograacutefica se ejecuta inmediatamente antes que la pieza sea comunicada al analizador sintaacutectico

Se puede asociar una accioacuten lexicograacutefica a una pieza sintaacutectica declarada con TOKEN y tambieacuten a una se-cuencia de caracteres declarada con SKIP ademaacutes de eacutestas hay otras dos formas de declaraciones lexico-graacuteficas (no mencionadas en esta introduccioacuten) que tambieacuten pueden llevar una accioacuten asociada

En una especificacioacuten JavaCC la forma de una pieza sintaacutectica (o bien de una secuencia de caracteres) se define poniendo su descripcioacuten delimitada entre llaves detraacutes de la indicacioacuten TOKEN (o bien SKIP) si se quiere asociar una accioacuten lexicograacutefica a la pieza (o a la secuencia) hay que colocar el bloque de coacutedigo de la accioacuten a continuacioacuten de la descripcioacuten de la forma de la pieza (o de la secuencia) el bloque (delimi-tado por llaves) queda situado entre la descripcioacuten y la llave que cierra la especificacioacuten

La pieza sintaacutectica especial que representa el final del fichero (EOF) tambieacuten puede llevar asociada una ac-cioacuten lexicograacutefica para ello hay que poner

ltgt TOKEN lt EOF gt coacutedigo de la accioacuten asociada

para justificar la presencia de los caracteres ltgt delante de la palabra TOKEN hay que basarse en el con-cepto de ldquoestado lexicograacuteficordquo (cuya explicacioacuten queda fuera del alcance de esta introduccioacuten)

∆ Ejemplo 5

Se considera nuevamente el ejemplo de presentacioacuten como resultado del anaacutelisis de una expresioacuten se pretende obtener una relacioacuten de los pareacutentesis contenidos en la expresioacuten en el mismo orden en que se encuentran ademaacutes por cada salto de liacutenea del texto de entrada se grabaraacute en la salida el caraacutecter y

25

se pasaraacute a una nueva liacutenea tambieacuten se anotaraacute la frase Final del trabajo cuando se alcance el final del fichero de entrada Asiacute por ejemplo si la entrada analizada es

(a + b) (( x )) (y + 7)

los resultados obtenidos seraacuten

() (())()

Final del trabajo

En la especificacioacuten JavaCC que se propone como solucioacuten soacutelo se muestran las reglas sintaacutecticas y le -xicograacuteficas (en el resto no hay cambios) en esta solucioacuten puede apreciarse- las piezas sintaacutecticas correspondientes a los pareacutentesis se han definido de manera nominal para po-der asociarles una accioacuten lexicograacutefica- los caracteres incluidos en la pieza de tipo SKIP se han descompuesto en dos grupos ya que la ac-cioacuten lexicograacutefica incorporada soacutelo afecta a uno de ellos

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ltpAbrirgt expresion() ltpCerrargt

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

TOKEN

26

lt pAbrir ( gt Systemoutprint(()

TOKEN lt pCerrar ) gt Systemoutprint())

ltgtTOKEN lt EOF gt Systemoutprintln ( Final del trabajo )

SKIP n Systemoutprint( n)

SKIP | t | r

loz Bloque de declaraciones lexicograacuteficasCada definicioacuten de una pieza sintaacutectica puede llevar asociada una accioacuten lexicograacutefica que se ejecuta in-mediatamente despueacutes de detectar en la entrada un lexema que se ajusta a la definicioacuten de la pieza cada accioacuten lexicograacutefica es un bloque de coacutedigo Java que puede contener sus propias declaraciones Pero a ve-ces interesa tener declaraciones que sean compartidas por todas las acciones lexicograacuteficas con indepen-dencia de la pieza a la que esteacute asociada cada una de ellas

Para incluir declaraciones que puedan usarse desde el coacutedigo asociado a cualquiera de las piezas sintaacutecti -cas se dispone de una construccioacuten con el siguiente formato

TOKEN_MGR_DECLS

que consta de una palabra reservada seguida del siacutembolo dos puntos seguido de un bloque de coacutedigo Java (delimitado por llaves) esta declaracioacuten se puede colocar en cualquier parte de una especificacioacuten JavaCC si se incluye soacutelo puede ponerse una vez

Todas las declaraciones de variables o de meacutetodos puestas en el bloque de declaraciones lexicograacuteficas compartidas son accesibles desde el coacutedigo de cualquier accioacuten lexicograacutefica la justificacioacuten es sencilla el bloque de declaraciones se traslada al principio del coacutedigo del analizador lexicograacutefico generado y las ac-ciones lexicograacuteficas tambieacuten se trasladan a ese analizador

loz Declaraciones lexicograacuteficas predefinidasDe lo expuesto hasta ahora se aprecia que en una accioacuten lexicograacutefica asociada a una pieza sintaacutectica re-sultan accesibles

- las declaraciones de su propio bloque (si tiene declaraciones)- los componentes del bloque de declaraciones compartidas (si estaacute incluido en la especificacioacuten)

Ademaacutes de esto se tienen otras declaraciones pertenecientes a la clase TokenManager que constituye el analizador lexicograacutefico que tambieacuten son utilizables desde cualquiera de las acciones lexico-graacuteficas estas declaraciones pueden considerarse como ldquopredefinidasrdquo ya que son incorporadas automaacuteti-camente por el generador Entre estas declaraciones accesibles se encuentran las siguientes

[ static ] StringBuffer imagecontiene la cadena de caracteres representativa de la pieza sintaacutectica actual que se acaba de recono-cer en le entrada se trata de un campo de la clase y se declara como static cuando el analizador

27

se ha generado con la opcioacuten (por defecto) Static = true si se generase con la opcioacuten Sta-tic = false no quedariacutea declarada como static

Token matchedToken

variable de clase Token que representa la pieza sintaacutectica actual la que se acaba de encontrar en la entrada se trata de una variable local del meacutetodo al que se ha trasladado el coacutedigo de la accioacuten (y por ello accesible desde ese coacutedigo)

El campo image puede usarse tanto si la accioacuten lexicograacutefica estaacute asociada a una pieza declarada con TOKEN como si lo estaacute a una secuencia declarada con SKIP Sin embargo la variable matchedToken no puede usarse en una accioacuten asociada a un patroacuten especificado como SKIP el motivo es que lo declara-do como SKIP no constituye propiamente una pieza sintaacutectica ya que no se va a comunicar al analizador sintaacutectico

∆ Ejemplo 6

Se propone aquiacute una nueva solucioacuten para el ejemplo nordm 4 expuesto anteriormente

Esta nueva solucioacuten tal y como se expone aquiacute admite algunas mejoras ya que su pretensioacuten soacutelo es ser-vir de ilustracioacuten para algunos aspectos que se acaban de citar (maacutes adelante se propone otra solucioacuten al-ternativa)

En la especificacioacuten de la solucioacuten es posible apreciar esto- las acciones lexicograacuteficas pueden llevar sus propias declaraciones las variables nLineaVar nColumnaVar nLineaCte y nColumnaCte se declaran dentro de las acciones

- la variable matchedToken a la que se hace referencia en el coacutedigo de la accioacuten lexicograacutefica aso-ciada a una variable contiene la informacioacuten sobre la pieza sintaacutectica que se acaba de detectar una va-riable de la expresioacuten analizada anaacutelogamente con la referencia relativa a una constante- el nombre image que aparece en las acciones lexicograacuteficas se refiere al campo de la clase Expr-MinTokenManager (analizador lexicograacutefico) noacutetese que en efecto se comunica como un paraacuteme-tro de clase StringBuffer

- la variable numero se emplea para numerar las constantes y variables encontradas

- los nombres beginLine y beginColumn son campos (puacuteblicos) de la clase Token por ello se pueden aplicar a un objeto de esa clase

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

void unaExpresion()

28

expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n StringBuffer lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

TOKEN lt variable [a-z] gt

int nLineaVar nColumnaVar numero++ nLineaVar = matchedTokenbeginLine nColumnaVar = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaVar nColumnaVar)

TOKEN lt constante ( [0-9] ) + gt

int nLineaCte nColumnaCte

29

numero++ nLineaCte = matchedTokenbeginLine nColumnaCte = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaCte nColumnaCte)

SKIP | n | t | r

La especificacioacuten precedente se puede escribir de otra manera maacutes escueta como se indica a continua-cioacuten (soacutelo se muestran las modificaciones) sobre esta nueva especificacioacuten puede mencionarse- se han eliminado por innecesarias las variables declaradas dentro de las acciones lexicograacuteficas- el nombre image que aparece en el bloque de las declaraciones compartidas se refiere al campo de la clase Token noacutetese que en efecto se aplica a un objeto de esa clase y se comunica como un paraacute -metro de tipo String

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

TOKEN lt variable [a-z] gt

numero++ grabarDatosPieza(numero matchedToken)

TOKEN lt constante ( [0-9] ) + gt

numero++ grabarDatosPieza(numero matchedToken)

loz Accioacuten lexicograacutefica comuacutenSi hubiera que realizar la misma tarea tras la deteccioacuten de todas y cada una de las piezas sintaacutecticas una posibilidad incoacutemoda seriacutea repetir el mismo coacutedigo en todas las acciones lexicograacuteficas para evitar tanta reiteracioacuten se tiene la opcioacuten Common_Token_Action su valor por defecto es false pero si se le asigna el valor true es posible definir una ldquoaccioacuten lexicograacutefica comuacutenrdquo que se escribe una uacutenica vez y se aplica a todas las piezas sintaacutecticas

30

Cuando la opcioacuten Common_Token_Action se establece con el valor true es obligado incluir el blo-que de declaraciones lexicograacuteficas compartidas (TOKEN_MGR_DECLS) Ademaacutes en ese bloque se preci-sa la definicioacuten de un meacutetodo cuyo encabezamiento sea

[ static ] void CommonTokenAction(Token t)

el coacutedigo de este meacutetodo constituye la accioacuten lexicograacutefica comuacuten que se ejecutaraacute despueacutes de la deteccioacuten de cualquier pieza sintaacutectica el meacutetodo tiene un paraacutemetro de tipo Token mediante el que se puede co-municar la informacioacuten correspondiente a la pieza que se acaba de encontrar en la entrada analizada

Si una pieza sintaacutectica tiene asociada una accioacuten lexicograacutefica propia la accioacuten lexicograacutefica comuacuten (si es-taacute definida) se ejecutaraacute inmediatamente despueacutes de realizar la accioacuten propia

La accioacuten lexicograacutefica comuacuten se aplica a todas las piezas sintaacutecticas definidas como TOKEN bien sea no-minalmente (en las reglas lexicograacuteficas) o bien anoacutenimamente (incorporadas dentro de las reglas sintaacutecti-cas) sin embargo no se aplica cuando la secuencia acoplada se corresponde con una expresioacuten regular es-pecificada como SKIP (noacutetese que por el contrario una especificacioacuten SKIP siacute puede tener asociada una accioacuten lexicograacutefica propia)

El coacutedigo del meacutetodo CommonTokenAction se traslada como un componente maacutes del bloque de decla-raciones compartidas (TOKEN_MGR_DECLS) al principio de la clase del analizador lexicograacutefico

∆ Ejemplo 7

Se pretende ahora ampliar el ejemplo de presentacioacuten con la obtencioacuten de una relacioacuten numerada de los lexemas de las piezas sintaacutecticas encontradas en la entrada analizada en el caso de las variables y de las constantes antes del lexema se mostraraacute un indicativo (Var Const) por ejemplo si la expresioacuten de la entrada es

( a + 987 ) b

se grabaraacute la salida

( 1Var a 2+ 3Const 987 4) 5 6Var b 7 8

En la solucioacuten que se propone a continuacioacuten se muestran la definicioacuten de las opciones y las reglas sin-taacutecticas y lexicograacuteficas (en el resto no hay modificaciones) puede resaltarse lo siguiente- las llamadas al meacutetodo CommonTokenAction se incorporan automaacuteticamente no hay que incluir-las en la especificacioacuten como paraacutemetro de llamada a este meacutetodo siempre se emplea el objeto que re -presenta la pieza que se acaba de detectar- la accioacuten lexicograacutefica comuacuten realiza la actualizacioacuten del contador de piezas y la grabacioacuten del lexe-ma de la pieza detectada con su nuacutemero de orden- la accioacuten lexicograacutefica comuacuten tambieacuten se aplica a las piezas sintaacutecticas anoacutenimas (pareacutentesis y ope-radores) y a la pieza especial que representa el final del fichero (EOF)

- las piezas sintaacutecticas ltconstantegt y ltvariablegt tienen asociada una accioacuten lexicograacutefica propia que se ejecuta antes de la accioacuten lexicograacutefica comuacuten- los caracteres que se saltan (SKIP) no estaacuten afectados por la accioacuten lexicograacutefica comuacuten

options Ignore_Case = true Common_Token_Action = true

31

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarIndicativo(String indicativo) Systemoutprint(indicativo + )

static void grabarLexema (int num String lexema) Systemoutprint(lexema + + num + n)

static void CommonTokenAction(Token pieza) numero++ grabarLexema(numero piezaimage)

TOKEN lt variable [a-z] gt

grabarIndicativo(Var)

32

TOKEN lt constante ( [0-9] ) + gt

grabarIndicativo(Const)

SKIP | n | t | r

A modo de ejemplo de aplicacioacuten de la accioacuten lexicograacutefica comuacuten en lo que sigue se ofrece una solucioacuten para la grabacioacuten (en el fichero predefinido de salida) del texto analizado con las liacuteneas numeradas

∆ Ejemplo 8 [ Listado del texto analizado ]Se desea completar el ejemplo de presentacioacuten con la grabacioacuten de un listado con las liacuteneas numeradas del texto analizado (noacutetese que una expresioacuten puede escribirse ocupando varias liacuteneas) No se muestra la especificacioacuten sintaacutectica debido a que no hay ninguna modificacioacuten en ella Los aspectos resentildeables de la solucioacuten que se propone son- se usa la variable numLin como contador de liacuteneas

- en la variable linea se van yuxtaponiendo los lexemas de las piezas sintaacutecticas de una liacutenea esta yuxtaposicioacuten se realiza mediante la accioacuten lexicograacutefica comuacuten- las secuencias reconocidas con los patrones especificados como SKIP no implican la ejecucioacuten de la accioacuten lexicograacutefica comuacuten por ello su lexema (almacenado en la variable image) ha de yuxtaponer-se expliacutecitamente- hay que separar el patroacuten correspondiente al fin de liacutenea para detectar el momento en que ha de gra-barse la liacutenea completa cuyo final se ha alcanzado

options Ignore_Case = true Common_Token_Action = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(nnAnalisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

TOKEN_MGR_DECLS static int numLin = 0 static String linea =

33

static void CommonTokenAction(Token pieza) linea = linea + piezaimage

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP lt | t | r gt linea = linea + image

SKIP n numLin++ Systemoutprintln(numLin + + linea) linea =

A modo de recapitulacioacutenↄ Accioacuten lexicograacutefica Accioacuten sintaacutectica

bull accioacuten lexicograacutefica bloque de coacutedigo Java asociado a una pieza sintaacutectica nominal (TOKEN) o a una secuencia de caracteres (SKIP) se ejecuta cuando se detecta en la entrada la pieza o la secuencia corres-pondiente no se puede aplicar a piezas sintaacutecticas anoacutenimas (autodefinidas con comillas)bull accioacuten sintaacutectica bloque de coacutedigo Java asociado a un punto de la estructura sintaacutectica (el punto indica-do por el sitio de la produccioacuten donde se inserta el bloque) se ejecuta cuando el anaacutelisis de la entrada pa-sa por ese punto de la estructura

ↄ Valor de una pieza sintaacutectica comunicadaEl analizador lexicograacutefico comunica al analizador sintaacutectico una pieza a traveacutes de un valor de la clase Token (fichero Tokenjava) un objeto de la clase Token representa las caracteriacutesticas de la pieza comunicada En la clase Token entre otros se tienen los campos

String imagelexema de la pieza

int beginLine beginColumn endLine endColumnposicioacuten del lexema en el fichero analizado (indicada por los nuacutemeros de fila y de columna de su comienzo y de su terminacioacuten)

ↄ Valor asociado a un siacutembolo

34

En las producciones que especifican la sintaxis se pueden incluir asignaciones que sirven para dejar ano-tado el valor asociado a un siacutembolo

bull siacutembolo no terminal valor = nombreSimbolo()el meacutetodo de anaacutelisis asociado a nombreSimbolo se ha declarado de un cierto tipo (distinto del tipo void) valor ha de ser una variable del mismo tipo que el meacutetodo

bull siacutembolo terminal dato = lt nombrePieza gtel valor asignado es el valor de la clase Token representativo de la pieza sintaacutectica (nominal) dato ha de ser una variable de tipo Token

ↄ Miradas sobre la pieza sintaacutectica comunicadaLa pieza sintaacutectica comunicada puede mirarse desde cada uno de los dos analizadores

bull analizador sintaacutecticola pieza comunicada (desde el analizador lexicograacutefico) se tiene anotada en un campo de la clase del analizador sintaacutectico declarado como

(static) public Token token

este valor es accesible desde el coacutedigo de todos los meacutetodos de anaacutelisis sintaacutectico el contenido del campo variacutea cuando se recibe una nueva pieza sintaacutectica

bull analizador lexicograacuteficola pieza dispuesta para ser comunicada (al analizador sintaacutectico) se tiene anotada en una variable local declarada como

Token matchedToken

este valor es accesible para el coacutedigo de todas las acciones lexicograacuteficas

ↄ El lexema de la pieza comunicadaEl lexema de la pieza sintaacutectica comunicada se encuentra disponible en dos sitios distintos que compar-ten el mismo nombre pero que son de distinto tipo y se usan de distinta manera

bull String image

es un campo de la clase TOKEN puede consultarse en relacioacuten con un objeto de esa clase que esteacute disponible

bull StringBuffer image

es un campo de la clase del analizador lexicograacutefico ( ∙ ∙ ∙ ∙ TokenManagerjava) es accesible desde el coacutedigo de la clase y por lo tanto desde todas las acciones lexicograacuteficas (bloques de coacute-digo que estaacuten incorporados al analizador lexicograacutefico)

se trata de un valor disponible tanto para las piezas sintaacutecticas nominales (TOKEN) como para las secuencias que se saltan (SKIP)

ↄ Resumen relativo a la lexicografiacutea

TOKEN pieza sintaacutectica nominal∙ ∙ ∙ pieza sintaacutectica anoacutenimaSKIP secuencia de caracteres que no forman pieza

35

Precisiones sobre el analizador sintaacutectico generadoPuede decirse que en su funcionamiento maacutes sencillo cuando no se emplean las posibilidades de examen por adelantado (ldquolookaheadrdquo) JavaCC comprueba que las reglas sintaacutecticas cumplen la condicioacuten LL(1) y generan un analizador descendente-predictivo-recursivo Pero esta apreciacioacuten hay que matizarla ya que el analizador sintaacutectico generado no se ajusta en sentido estricto al modelo de analizador LL(1)

En el modelo de anaacutelisis LL(1) el orden del examen de las distintas alternativas de expansioacuten de un siacutembo-lo no terminal es indiferente ya que se conocen los siacutembolos directores de cada una de ellas (que resultan ser conjuntos disjuntos) en la implementacioacuten del subprograma de anaacutelisis asociado al siacutembolo no terminal ltNombreSimbgt cuyas producciones son

ltNombreSimbgt = α1

| α2

∙ ∙ ∙

| αn

se podriacutea preguntar en cualquier orden si la pieza actual pertenece al conjunto de siacutembolos directores de las distintas reglas Sin embargo en el analizador sintaacutectico generado por JavaCC la implementacioacuten del meacute-todo asociado al siacutembolo no terminal ltNombreSimbgt no sigue esa pauta

Si las anteriores reglas sintaacutecticas se transcriben en JavaCC de la forma

void nombreSimb () α1-jcc

| α2-jcc

∙ ∙ ∙

| αn-jcc

1

accioacutenlexicograacutefica

comuacuten

Commom -TokenAction2

accioacutenlexicograacutefica

propia

(accioacuten ligadaa una pieza)3

bloque dedeclaracioneslexicograacuteficas

TOKEN_MGR_DECLS4

declaracioneslexicograacuteficaspredefinidas

(disponibilidad

de uso)TOKENsisisisi∙ ∙ ∙sino--SKIPnosisisi

Las indicaciones si de las columnas 3 y 4 son consecuencia de las indicaciones de la columna 2

La accioacuten lexicograacutefica comuacuten se ejecuta despueacutes de la accioacuten lexicograacutefica propia

36

el meacutetodo de anaacutelisis generado procede de la siguiente manera

se van considerando las alternativas sucesivamente en el mismo orden en el que estaacuten especifica-das primero la de α1 (α1-jcc) despueacutes la de α2 (α2-jcc) etc

para cada alternativa seleccionada se comprueba si la pieza por adelantado coincide con alguno de los siacutembolos iniciales de la parte derecha y si hay coincidencia se prosigue el anaacutelisis con ella si no hay coincidencia se pasa a la alternativa siguiente

siguiendo con este orden si se llega al final de las alternativas sin haberse encontrado coincidencia alguna se produce un error sintaacutectico

En el analizador generado por JavaCC hay una peculiaridad que no se tiene en los analizadores LL(1) es-trictamente considerados la palabra vaciacutea siacute se considera como posible siacutembolo inicial de la parte derecha una regla (esto ocurre siempre que la parte derecha es anulable) De esta peculiaridad se deriva una conse-cuencia que ha de tenerse en cuenta al escribir la especificacioacuten dado que siempre es posible considerar la palabra vaciacutea como la siguiente pieza por adelantado si se llega a examinar la alternativa correspondiente a una produccioacuten anulable el analizador generado siempre seleccionaraacute esa alternativa con independencia de la siguiente pieza que esteacute presente en la entrada que queda por analizar

Asiacute pues si alguna de las producciones que definen un siacutembolo no terminal es anulable es importante el or-den en que se escriben las alternativas en una especificacioacuten sintaacutectica JavaCC el funcionamiento del ana-lizador generado siacute depende de la colocacioacuten elegida para las reglas

Para ilustrar estas peculiaridades del analizador sintaacutectico generado por JavaCC a continuacioacuten se expone un ejemplo

∆ Ejemplo 9

Se quiere obtener un analizador leacutexico-sintaacutectico de textos que se ajustan al siguiente formato una se-cuencia de uno o maacutes nombres seguida del siacutembolo igual seguido de un nuacutemero entero si la secuencia tiene maacutes de un nombre han de estar separados bien por una coma en todos los casos o bien por el siacutem-bolo dos puntos en todos los casos esto es son correctas las secuencias

uno dos tres = 123exclusivo = 0Abajo Arriba = 57

y son incorrectas las secuenciasuno dos tres = 123

exclusivo inclusive = 0

fuera dentro = 56

Una gramaacutetica que define las secuencias con este formato es

ltListagt = nombre ltOtrosNombresgt = numero

ltOtrosNombresgt = nombre nombre | nombre nombre | ε

se trata de una gramaacutetica que cumple la condicioacuten LL(1)- el siacutembolo director para la alternativa vaciacutea es el siacutembolo igual es evidente pues que las tres reglas de ltOtrosNombresgt tienen siacutembolos directores distintos

37

- la decisioacuten de abandonar la repeticioacuten del anaacutelisis de un nuevo nombre (en las dos primeras alterna-tivas de ltOtrosNombresgt) se puede tomar ante la presencia del siacutembolo igual

En la implementacioacuten de un analizador sintaacutectico LL(1) en sentido estricto la seleccioacuten de la alternativa vaciacutea para expandir el siacutembolo ltOtrosNombresgt se hariacutea tras comprobar expliacutecitamente que la pie-za sintaacutectica por adelantado es el siacutembolo igual

El funcionamiento del analizador generado por JavaCC depende del orden de colocacioacuten de las reglas A continuacioacuten se exponen varias posibilidades de especificacioacuten y se comenta el analizador generado en cada caso

bull Especificacioacuten primeraSe transcriben literalmente las reglas tal y como se han escrito antes en la notacioacuten BNF noacutetese que para poder codificar la parte derecha de la regla vaciacutea ha de incluirse una accioacuten sintaacutectica vaciacutea

PARSER_BEGIN (Igualdad)

public class Igualdad

public static void main (String[] argum) throws ParseException Igualdad analisis = new Igualdad(Systemin) analisissecuencia() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (Igualdad)

void secuencia() lista() ltEOFgt

void lista() lt nombre gt otrosNombres() = lt numero gt

void otrosNombres() lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt ) |

TOKEN lt nombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN lt numero ( [ 0 - 9 ] )+ gt

SKIP | r | n

38

El analizador generado funciona correctamente puesto que en el meacutetodo otrosNombres se selec-ciona la alternativa vaciacutea despueacutes de haber comprobado que la pieza por adelantado no es siacutembolo ini-cial de ninguna de las dos alternativas precedentes (no es una coma ni un siacutembolo dos puntos)

bull Especificacioacuten segundaSe considera la misma especificacioacuten anterior pero cambiando el orden de colocacioacuten de las reglas ahora la produccioacuten vaciacutea se coloca como primera alternativa (soacutelo se muestran las partes modifica-das)

void otrosNombres() | lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt )

Con la entrada de esta especificacioacuten el generador JavaCC produce un mensaje en el que se avisa de que las dos alternativas que estaacuten colocadas detraacutes de la alternativa vaciacutea nunca podraacuten seleccionarse aun asiacute se genera un analizador Pero es un analizador que funciona incorrectamente asiacute por ejemplo la entrada

mxa nyz = 57

se considera incorrecta despueacutes de visto el primer nombre se intenta aplicar la produccioacuten vaciacutea (se espera encontrar el siacutembolo igual) pero habriacutea que aplicar la regla cuyo siacutembolo director es la coma

bull Especificacioacuten terceraSe utilizan las posibilidades de la notacioacuten BNF-Ampliada para prescindir de la especificacioacuten expliacuteci-ta de la palabra vaciacutea se aprovecha que JavaCC admite el operador + para indicar repeticiones de una o maacutes veces la alternativa vaciacutea estaacute impliacutecitamente contemplada en la posibilidad de cero repeti-ciones que contempla el operador (soacutelo se muestran las partes modificadas en la especificacioacuten)

void otrosNombres() ( lt nombre gt )+ | ( lt nombre gt )

El meacutetodo otrosNombres() generado hace las comprobaciones en el siguiente orden

- se comprueba si la pieza por adelantado es una coma (siacutembolo inicial de la primera alternativa) y en este caso se selecciona la primera regla- si no es una coma se comprueba si es un siacutembolo dos puntos (siacutembolo inicial de la segunda alter-nativa) y en este caso se selecciona la segunda regla- si no es un siacutembolo dos puntos se considera la palabra vaciacutea como siacutembolo inicial (siempre posi-ble impliacutecitamente) para la segunda alternativa en este caso para que no haya error la pieza por adelantado ha de ser un siacutembolo igual

Se trata pues de un analizador que funciona correctamente

bull Especificacioacuten cuarta

39

Se pone la misma especificacioacuten anterior pero cambiando el orden de consideracioacuten de la regla vaciacutea ahora se incluye impliacutecitamente en la primera alternativa (soacutelo se muestran las partes modificadas)

void otrosNombres() ( lt nombre gt ) | ( lt nombre gt )+

Para esta especificacioacuten el generador JavaCC emite un aviso en el que se indica que la segunda alter -nativa nunca se seleccionaraacute no obstante se genera un analizador que no funciona como se supone que deberiacutea hacerlo por ejemplo el anaacutelisis de la entrada

nyz mxa = 1

produce un error despueacutes de tratado el primer nombre se intenta aplicar la produccioacuten vaciacutea (corres-pondiente a la repeticioacuten de cero veces contemplada en la primera regla) por lo que no se encuentra el esperado siacutembolo igual

40

Page 26: JavaCC

Si se accede al campo token de la clase del analizador sintaacutectico se puede resolver este ejemplo pres-cindiendo de la variable pieza

void factor() ( ltconstantegt | ltvariablegt

) numero++ grabarDatosPieza(numero token)

| ( expresion() )

Tareas asociadas a las piezas sintaacutecticasYa se ha visto que es posible asociar bloques de coacutedigo Java a puntos de la estructura sintaacutectica para que se realicen ciertas operaciones cuando el analizador sintaacutectico pase por esa zona de la estructura Tambieacuten es posible asociar coacutedigo Java a las piezas sintaacutecticas para que el analizador lexicograacutefico efectuacutee determina-das tareas cuando detecte en la entrada analizada piezas que tengan asociado coacutedigo

loz Accioacuten ligada a una pieza sintaacutecticaSi se asocia un bloque de coacutedigo a una pieza sintaacutectica se consigue que cada vez que se detecta en la en-trada analizada la presencia de esa pieza se ejecute en ese preciso momento el coacutedigo del bloque asociado a la pieza Estas acciones se denominan ldquoacciones lexicograacuteficasrdquo (lexical actions) el coacutedigo del bloque se traslada literalmente al coacutedigo del analizador lexicograacutefico generado para que se ejecute oportunamente cuando se detecte la pieza ligada al bloque de coacutedigo La accioacuten lexicograacutefica se ejecuta inmediatamente antes que la pieza sea comunicada al analizador sintaacutectico

Se puede asociar una accioacuten lexicograacutefica a una pieza sintaacutectica declarada con TOKEN y tambieacuten a una se-cuencia de caracteres declarada con SKIP ademaacutes de eacutestas hay otras dos formas de declaraciones lexico-graacuteficas (no mencionadas en esta introduccioacuten) que tambieacuten pueden llevar una accioacuten asociada

En una especificacioacuten JavaCC la forma de una pieza sintaacutectica (o bien de una secuencia de caracteres) se define poniendo su descripcioacuten delimitada entre llaves detraacutes de la indicacioacuten TOKEN (o bien SKIP) si se quiere asociar una accioacuten lexicograacutefica a la pieza (o a la secuencia) hay que colocar el bloque de coacutedigo de la accioacuten a continuacioacuten de la descripcioacuten de la forma de la pieza (o de la secuencia) el bloque (delimi-tado por llaves) queda situado entre la descripcioacuten y la llave que cierra la especificacioacuten

La pieza sintaacutectica especial que representa el final del fichero (EOF) tambieacuten puede llevar asociada una ac-cioacuten lexicograacutefica para ello hay que poner

ltgt TOKEN lt EOF gt coacutedigo de la accioacuten asociada

para justificar la presencia de los caracteres ltgt delante de la palabra TOKEN hay que basarse en el con-cepto de ldquoestado lexicograacuteficordquo (cuya explicacioacuten queda fuera del alcance de esta introduccioacuten)

∆ Ejemplo 5

Se considera nuevamente el ejemplo de presentacioacuten como resultado del anaacutelisis de una expresioacuten se pretende obtener una relacioacuten de los pareacutentesis contenidos en la expresioacuten en el mismo orden en que se encuentran ademaacutes por cada salto de liacutenea del texto de entrada se grabaraacute en la salida el caraacutecter y

25

se pasaraacute a una nueva liacutenea tambieacuten se anotaraacute la frase Final del trabajo cuando se alcance el final del fichero de entrada Asiacute por ejemplo si la entrada analizada es

(a + b) (( x )) (y + 7)

los resultados obtenidos seraacuten

() (())()

Final del trabajo

En la especificacioacuten JavaCC que se propone como solucioacuten soacutelo se muestran las reglas sintaacutecticas y le -xicograacuteficas (en el resto no hay cambios) en esta solucioacuten puede apreciarse- las piezas sintaacutecticas correspondientes a los pareacutentesis se han definido de manera nominal para po-der asociarles una accioacuten lexicograacutefica- los caracteres incluidos en la pieza de tipo SKIP se han descompuesto en dos grupos ya que la ac-cioacuten lexicograacutefica incorporada soacutelo afecta a uno de ellos

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ltpAbrirgt expresion() ltpCerrargt

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

TOKEN

26

lt pAbrir ( gt Systemoutprint(()

TOKEN lt pCerrar ) gt Systemoutprint())

ltgtTOKEN lt EOF gt Systemoutprintln ( Final del trabajo )

SKIP n Systemoutprint( n)

SKIP | t | r

loz Bloque de declaraciones lexicograacuteficasCada definicioacuten de una pieza sintaacutectica puede llevar asociada una accioacuten lexicograacutefica que se ejecuta in-mediatamente despueacutes de detectar en la entrada un lexema que se ajusta a la definicioacuten de la pieza cada accioacuten lexicograacutefica es un bloque de coacutedigo Java que puede contener sus propias declaraciones Pero a ve-ces interesa tener declaraciones que sean compartidas por todas las acciones lexicograacuteficas con indepen-dencia de la pieza a la que esteacute asociada cada una de ellas

Para incluir declaraciones que puedan usarse desde el coacutedigo asociado a cualquiera de las piezas sintaacutecti -cas se dispone de una construccioacuten con el siguiente formato

TOKEN_MGR_DECLS

que consta de una palabra reservada seguida del siacutembolo dos puntos seguido de un bloque de coacutedigo Java (delimitado por llaves) esta declaracioacuten se puede colocar en cualquier parte de una especificacioacuten JavaCC si se incluye soacutelo puede ponerse una vez

Todas las declaraciones de variables o de meacutetodos puestas en el bloque de declaraciones lexicograacuteficas compartidas son accesibles desde el coacutedigo de cualquier accioacuten lexicograacutefica la justificacioacuten es sencilla el bloque de declaraciones se traslada al principio del coacutedigo del analizador lexicograacutefico generado y las ac-ciones lexicograacuteficas tambieacuten se trasladan a ese analizador

loz Declaraciones lexicograacuteficas predefinidasDe lo expuesto hasta ahora se aprecia que en una accioacuten lexicograacutefica asociada a una pieza sintaacutectica re-sultan accesibles

- las declaraciones de su propio bloque (si tiene declaraciones)- los componentes del bloque de declaraciones compartidas (si estaacute incluido en la especificacioacuten)

Ademaacutes de esto se tienen otras declaraciones pertenecientes a la clase TokenManager que constituye el analizador lexicograacutefico que tambieacuten son utilizables desde cualquiera de las acciones lexico-graacuteficas estas declaraciones pueden considerarse como ldquopredefinidasrdquo ya que son incorporadas automaacuteti-camente por el generador Entre estas declaraciones accesibles se encuentran las siguientes

[ static ] StringBuffer imagecontiene la cadena de caracteres representativa de la pieza sintaacutectica actual que se acaba de recono-cer en le entrada se trata de un campo de la clase y se declara como static cuando el analizador

27

se ha generado con la opcioacuten (por defecto) Static = true si se generase con la opcioacuten Sta-tic = false no quedariacutea declarada como static

Token matchedToken

variable de clase Token que representa la pieza sintaacutectica actual la que se acaba de encontrar en la entrada se trata de una variable local del meacutetodo al que se ha trasladado el coacutedigo de la accioacuten (y por ello accesible desde ese coacutedigo)

El campo image puede usarse tanto si la accioacuten lexicograacutefica estaacute asociada a una pieza declarada con TOKEN como si lo estaacute a una secuencia declarada con SKIP Sin embargo la variable matchedToken no puede usarse en una accioacuten asociada a un patroacuten especificado como SKIP el motivo es que lo declara-do como SKIP no constituye propiamente una pieza sintaacutectica ya que no se va a comunicar al analizador sintaacutectico

∆ Ejemplo 6

Se propone aquiacute una nueva solucioacuten para el ejemplo nordm 4 expuesto anteriormente

Esta nueva solucioacuten tal y como se expone aquiacute admite algunas mejoras ya que su pretensioacuten soacutelo es ser-vir de ilustracioacuten para algunos aspectos que se acaban de citar (maacutes adelante se propone otra solucioacuten al-ternativa)

En la especificacioacuten de la solucioacuten es posible apreciar esto- las acciones lexicograacuteficas pueden llevar sus propias declaraciones las variables nLineaVar nColumnaVar nLineaCte y nColumnaCte se declaran dentro de las acciones

- la variable matchedToken a la que se hace referencia en el coacutedigo de la accioacuten lexicograacutefica aso-ciada a una variable contiene la informacioacuten sobre la pieza sintaacutectica que se acaba de detectar una va-riable de la expresioacuten analizada anaacutelogamente con la referencia relativa a una constante- el nombre image que aparece en las acciones lexicograacuteficas se refiere al campo de la clase Expr-MinTokenManager (analizador lexicograacutefico) noacutetese que en efecto se comunica como un paraacuteme-tro de clase StringBuffer

- la variable numero se emplea para numerar las constantes y variables encontradas

- los nombres beginLine y beginColumn son campos (puacuteblicos) de la clase Token por ello se pueden aplicar a un objeto de esa clase

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

void unaExpresion()

28

expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n StringBuffer lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

TOKEN lt variable [a-z] gt

int nLineaVar nColumnaVar numero++ nLineaVar = matchedTokenbeginLine nColumnaVar = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaVar nColumnaVar)

TOKEN lt constante ( [0-9] ) + gt

int nLineaCte nColumnaCte

29

numero++ nLineaCte = matchedTokenbeginLine nColumnaCte = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaCte nColumnaCte)

SKIP | n | t | r

La especificacioacuten precedente se puede escribir de otra manera maacutes escueta como se indica a continua-cioacuten (soacutelo se muestran las modificaciones) sobre esta nueva especificacioacuten puede mencionarse- se han eliminado por innecesarias las variables declaradas dentro de las acciones lexicograacuteficas- el nombre image que aparece en el bloque de las declaraciones compartidas se refiere al campo de la clase Token noacutetese que en efecto se aplica a un objeto de esa clase y se comunica como un paraacute -metro de tipo String

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

TOKEN lt variable [a-z] gt

numero++ grabarDatosPieza(numero matchedToken)

TOKEN lt constante ( [0-9] ) + gt

numero++ grabarDatosPieza(numero matchedToken)

loz Accioacuten lexicograacutefica comuacutenSi hubiera que realizar la misma tarea tras la deteccioacuten de todas y cada una de las piezas sintaacutecticas una posibilidad incoacutemoda seriacutea repetir el mismo coacutedigo en todas las acciones lexicograacuteficas para evitar tanta reiteracioacuten se tiene la opcioacuten Common_Token_Action su valor por defecto es false pero si se le asigna el valor true es posible definir una ldquoaccioacuten lexicograacutefica comuacutenrdquo que se escribe una uacutenica vez y se aplica a todas las piezas sintaacutecticas

30

Cuando la opcioacuten Common_Token_Action se establece con el valor true es obligado incluir el blo-que de declaraciones lexicograacuteficas compartidas (TOKEN_MGR_DECLS) Ademaacutes en ese bloque se preci-sa la definicioacuten de un meacutetodo cuyo encabezamiento sea

[ static ] void CommonTokenAction(Token t)

el coacutedigo de este meacutetodo constituye la accioacuten lexicograacutefica comuacuten que se ejecutaraacute despueacutes de la deteccioacuten de cualquier pieza sintaacutectica el meacutetodo tiene un paraacutemetro de tipo Token mediante el que se puede co-municar la informacioacuten correspondiente a la pieza que se acaba de encontrar en la entrada analizada

Si una pieza sintaacutectica tiene asociada una accioacuten lexicograacutefica propia la accioacuten lexicograacutefica comuacuten (si es-taacute definida) se ejecutaraacute inmediatamente despueacutes de realizar la accioacuten propia

La accioacuten lexicograacutefica comuacuten se aplica a todas las piezas sintaacutecticas definidas como TOKEN bien sea no-minalmente (en las reglas lexicograacuteficas) o bien anoacutenimamente (incorporadas dentro de las reglas sintaacutecti-cas) sin embargo no se aplica cuando la secuencia acoplada se corresponde con una expresioacuten regular es-pecificada como SKIP (noacutetese que por el contrario una especificacioacuten SKIP siacute puede tener asociada una accioacuten lexicograacutefica propia)

El coacutedigo del meacutetodo CommonTokenAction se traslada como un componente maacutes del bloque de decla-raciones compartidas (TOKEN_MGR_DECLS) al principio de la clase del analizador lexicograacutefico

∆ Ejemplo 7

Se pretende ahora ampliar el ejemplo de presentacioacuten con la obtencioacuten de una relacioacuten numerada de los lexemas de las piezas sintaacutecticas encontradas en la entrada analizada en el caso de las variables y de las constantes antes del lexema se mostraraacute un indicativo (Var Const) por ejemplo si la expresioacuten de la entrada es

( a + 987 ) b

se grabaraacute la salida

( 1Var a 2+ 3Const 987 4) 5 6Var b 7 8

En la solucioacuten que se propone a continuacioacuten se muestran la definicioacuten de las opciones y las reglas sin-taacutecticas y lexicograacuteficas (en el resto no hay modificaciones) puede resaltarse lo siguiente- las llamadas al meacutetodo CommonTokenAction se incorporan automaacuteticamente no hay que incluir-las en la especificacioacuten como paraacutemetro de llamada a este meacutetodo siempre se emplea el objeto que re -presenta la pieza que se acaba de detectar- la accioacuten lexicograacutefica comuacuten realiza la actualizacioacuten del contador de piezas y la grabacioacuten del lexe-ma de la pieza detectada con su nuacutemero de orden- la accioacuten lexicograacutefica comuacuten tambieacuten se aplica a las piezas sintaacutecticas anoacutenimas (pareacutentesis y ope-radores) y a la pieza especial que representa el final del fichero (EOF)

- las piezas sintaacutecticas ltconstantegt y ltvariablegt tienen asociada una accioacuten lexicograacutefica propia que se ejecuta antes de la accioacuten lexicograacutefica comuacuten- los caracteres que se saltan (SKIP) no estaacuten afectados por la accioacuten lexicograacutefica comuacuten

options Ignore_Case = true Common_Token_Action = true

31

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarIndicativo(String indicativo) Systemoutprint(indicativo + )

static void grabarLexema (int num String lexema) Systemoutprint(lexema + + num + n)

static void CommonTokenAction(Token pieza) numero++ grabarLexema(numero piezaimage)

TOKEN lt variable [a-z] gt

grabarIndicativo(Var)

32

TOKEN lt constante ( [0-9] ) + gt

grabarIndicativo(Const)

SKIP | n | t | r

A modo de ejemplo de aplicacioacuten de la accioacuten lexicograacutefica comuacuten en lo que sigue se ofrece una solucioacuten para la grabacioacuten (en el fichero predefinido de salida) del texto analizado con las liacuteneas numeradas

∆ Ejemplo 8 [ Listado del texto analizado ]Se desea completar el ejemplo de presentacioacuten con la grabacioacuten de un listado con las liacuteneas numeradas del texto analizado (noacutetese que una expresioacuten puede escribirse ocupando varias liacuteneas) No se muestra la especificacioacuten sintaacutectica debido a que no hay ninguna modificacioacuten en ella Los aspectos resentildeables de la solucioacuten que se propone son- se usa la variable numLin como contador de liacuteneas

- en la variable linea se van yuxtaponiendo los lexemas de las piezas sintaacutecticas de una liacutenea esta yuxtaposicioacuten se realiza mediante la accioacuten lexicograacutefica comuacuten- las secuencias reconocidas con los patrones especificados como SKIP no implican la ejecucioacuten de la accioacuten lexicograacutefica comuacuten por ello su lexema (almacenado en la variable image) ha de yuxtaponer-se expliacutecitamente- hay que separar el patroacuten correspondiente al fin de liacutenea para detectar el momento en que ha de gra-barse la liacutenea completa cuyo final se ha alcanzado

options Ignore_Case = true Common_Token_Action = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(nnAnalisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

TOKEN_MGR_DECLS static int numLin = 0 static String linea =

33

static void CommonTokenAction(Token pieza) linea = linea + piezaimage

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP lt | t | r gt linea = linea + image

SKIP n numLin++ Systemoutprintln(numLin + + linea) linea =

A modo de recapitulacioacutenↄ Accioacuten lexicograacutefica Accioacuten sintaacutectica

bull accioacuten lexicograacutefica bloque de coacutedigo Java asociado a una pieza sintaacutectica nominal (TOKEN) o a una secuencia de caracteres (SKIP) se ejecuta cuando se detecta en la entrada la pieza o la secuencia corres-pondiente no se puede aplicar a piezas sintaacutecticas anoacutenimas (autodefinidas con comillas)bull accioacuten sintaacutectica bloque de coacutedigo Java asociado a un punto de la estructura sintaacutectica (el punto indica-do por el sitio de la produccioacuten donde se inserta el bloque) se ejecuta cuando el anaacutelisis de la entrada pa-sa por ese punto de la estructura

ↄ Valor de una pieza sintaacutectica comunicadaEl analizador lexicograacutefico comunica al analizador sintaacutectico una pieza a traveacutes de un valor de la clase Token (fichero Tokenjava) un objeto de la clase Token representa las caracteriacutesticas de la pieza comunicada En la clase Token entre otros se tienen los campos

String imagelexema de la pieza

int beginLine beginColumn endLine endColumnposicioacuten del lexema en el fichero analizado (indicada por los nuacutemeros de fila y de columna de su comienzo y de su terminacioacuten)

ↄ Valor asociado a un siacutembolo

34

En las producciones que especifican la sintaxis se pueden incluir asignaciones que sirven para dejar ano-tado el valor asociado a un siacutembolo

bull siacutembolo no terminal valor = nombreSimbolo()el meacutetodo de anaacutelisis asociado a nombreSimbolo se ha declarado de un cierto tipo (distinto del tipo void) valor ha de ser una variable del mismo tipo que el meacutetodo

bull siacutembolo terminal dato = lt nombrePieza gtel valor asignado es el valor de la clase Token representativo de la pieza sintaacutectica (nominal) dato ha de ser una variable de tipo Token

ↄ Miradas sobre la pieza sintaacutectica comunicadaLa pieza sintaacutectica comunicada puede mirarse desde cada uno de los dos analizadores

bull analizador sintaacutecticola pieza comunicada (desde el analizador lexicograacutefico) se tiene anotada en un campo de la clase del analizador sintaacutectico declarado como

(static) public Token token

este valor es accesible desde el coacutedigo de todos los meacutetodos de anaacutelisis sintaacutectico el contenido del campo variacutea cuando se recibe una nueva pieza sintaacutectica

bull analizador lexicograacuteficola pieza dispuesta para ser comunicada (al analizador sintaacutectico) se tiene anotada en una variable local declarada como

Token matchedToken

este valor es accesible para el coacutedigo de todas las acciones lexicograacuteficas

ↄ El lexema de la pieza comunicadaEl lexema de la pieza sintaacutectica comunicada se encuentra disponible en dos sitios distintos que compar-ten el mismo nombre pero que son de distinto tipo y se usan de distinta manera

bull String image

es un campo de la clase TOKEN puede consultarse en relacioacuten con un objeto de esa clase que esteacute disponible

bull StringBuffer image

es un campo de la clase del analizador lexicograacutefico ( ∙ ∙ ∙ ∙ TokenManagerjava) es accesible desde el coacutedigo de la clase y por lo tanto desde todas las acciones lexicograacuteficas (bloques de coacute-digo que estaacuten incorporados al analizador lexicograacutefico)

se trata de un valor disponible tanto para las piezas sintaacutecticas nominales (TOKEN) como para las secuencias que se saltan (SKIP)

ↄ Resumen relativo a la lexicografiacutea

TOKEN pieza sintaacutectica nominal∙ ∙ ∙ pieza sintaacutectica anoacutenimaSKIP secuencia de caracteres que no forman pieza

35

Precisiones sobre el analizador sintaacutectico generadoPuede decirse que en su funcionamiento maacutes sencillo cuando no se emplean las posibilidades de examen por adelantado (ldquolookaheadrdquo) JavaCC comprueba que las reglas sintaacutecticas cumplen la condicioacuten LL(1) y generan un analizador descendente-predictivo-recursivo Pero esta apreciacioacuten hay que matizarla ya que el analizador sintaacutectico generado no se ajusta en sentido estricto al modelo de analizador LL(1)

En el modelo de anaacutelisis LL(1) el orden del examen de las distintas alternativas de expansioacuten de un siacutembo-lo no terminal es indiferente ya que se conocen los siacutembolos directores de cada una de ellas (que resultan ser conjuntos disjuntos) en la implementacioacuten del subprograma de anaacutelisis asociado al siacutembolo no terminal ltNombreSimbgt cuyas producciones son

ltNombreSimbgt = α1

| α2

∙ ∙ ∙

| αn

se podriacutea preguntar en cualquier orden si la pieza actual pertenece al conjunto de siacutembolos directores de las distintas reglas Sin embargo en el analizador sintaacutectico generado por JavaCC la implementacioacuten del meacute-todo asociado al siacutembolo no terminal ltNombreSimbgt no sigue esa pauta

Si las anteriores reglas sintaacutecticas se transcriben en JavaCC de la forma

void nombreSimb () α1-jcc

| α2-jcc

∙ ∙ ∙

| αn-jcc

1

accioacutenlexicograacutefica

comuacuten

Commom -TokenAction2

accioacutenlexicograacutefica

propia

(accioacuten ligadaa una pieza)3

bloque dedeclaracioneslexicograacuteficas

TOKEN_MGR_DECLS4

declaracioneslexicograacuteficaspredefinidas

(disponibilidad

de uso)TOKENsisisisi∙ ∙ ∙sino--SKIPnosisisi

Las indicaciones si de las columnas 3 y 4 son consecuencia de las indicaciones de la columna 2

La accioacuten lexicograacutefica comuacuten se ejecuta despueacutes de la accioacuten lexicograacutefica propia

36

el meacutetodo de anaacutelisis generado procede de la siguiente manera

se van considerando las alternativas sucesivamente en el mismo orden en el que estaacuten especifica-das primero la de α1 (α1-jcc) despueacutes la de α2 (α2-jcc) etc

para cada alternativa seleccionada se comprueba si la pieza por adelantado coincide con alguno de los siacutembolos iniciales de la parte derecha y si hay coincidencia se prosigue el anaacutelisis con ella si no hay coincidencia se pasa a la alternativa siguiente

siguiendo con este orden si se llega al final de las alternativas sin haberse encontrado coincidencia alguna se produce un error sintaacutectico

En el analizador generado por JavaCC hay una peculiaridad que no se tiene en los analizadores LL(1) es-trictamente considerados la palabra vaciacutea siacute se considera como posible siacutembolo inicial de la parte derecha una regla (esto ocurre siempre que la parte derecha es anulable) De esta peculiaridad se deriva una conse-cuencia que ha de tenerse en cuenta al escribir la especificacioacuten dado que siempre es posible considerar la palabra vaciacutea como la siguiente pieza por adelantado si se llega a examinar la alternativa correspondiente a una produccioacuten anulable el analizador generado siempre seleccionaraacute esa alternativa con independencia de la siguiente pieza que esteacute presente en la entrada que queda por analizar

Asiacute pues si alguna de las producciones que definen un siacutembolo no terminal es anulable es importante el or-den en que se escriben las alternativas en una especificacioacuten sintaacutectica JavaCC el funcionamiento del ana-lizador generado siacute depende de la colocacioacuten elegida para las reglas

Para ilustrar estas peculiaridades del analizador sintaacutectico generado por JavaCC a continuacioacuten se expone un ejemplo

∆ Ejemplo 9

Se quiere obtener un analizador leacutexico-sintaacutectico de textos que se ajustan al siguiente formato una se-cuencia de uno o maacutes nombres seguida del siacutembolo igual seguido de un nuacutemero entero si la secuencia tiene maacutes de un nombre han de estar separados bien por una coma en todos los casos o bien por el siacutem-bolo dos puntos en todos los casos esto es son correctas las secuencias

uno dos tres = 123exclusivo = 0Abajo Arriba = 57

y son incorrectas las secuenciasuno dos tres = 123

exclusivo inclusive = 0

fuera dentro = 56

Una gramaacutetica que define las secuencias con este formato es

ltListagt = nombre ltOtrosNombresgt = numero

ltOtrosNombresgt = nombre nombre | nombre nombre | ε

se trata de una gramaacutetica que cumple la condicioacuten LL(1)- el siacutembolo director para la alternativa vaciacutea es el siacutembolo igual es evidente pues que las tres reglas de ltOtrosNombresgt tienen siacutembolos directores distintos

37

- la decisioacuten de abandonar la repeticioacuten del anaacutelisis de un nuevo nombre (en las dos primeras alterna-tivas de ltOtrosNombresgt) se puede tomar ante la presencia del siacutembolo igual

En la implementacioacuten de un analizador sintaacutectico LL(1) en sentido estricto la seleccioacuten de la alternativa vaciacutea para expandir el siacutembolo ltOtrosNombresgt se hariacutea tras comprobar expliacutecitamente que la pie-za sintaacutectica por adelantado es el siacutembolo igual

El funcionamiento del analizador generado por JavaCC depende del orden de colocacioacuten de las reglas A continuacioacuten se exponen varias posibilidades de especificacioacuten y se comenta el analizador generado en cada caso

bull Especificacioacuten primeraSe transcriben literalmente las reglas tal y como se han escrito antes en la notacioacuten BNF noacutetese que para poder codificar la parte derecha de la regla vaciacutea ha de incluirse una accioacuten sintaacutectica vaciacutea

PARSER_BEGIN (Igualdad)

public class Igualdad

public static void main (String[] argum) throws ParseException Igualdad analisis = new Igualdad(Systemin) analisissecuencia() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (Igualdad)

void secuencia() lista() ltEOFgt

void lista() lt nombre gt otrosNombres() = lt numero gt

void otrosNombres() lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt ) |

TOKEN lt nombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN lt numero ( [ 0 - 9 ] )+ gt

SKIP | r | n

38

El analizador generado funciona correctamente puesto que en el meacutetodo otrosNombres se selec-ciona la alternativa vaciacutea despueacutes de haber comprobado que la pieza por adelantado no es siacutembolo ini-cial de ninguna de las dos alternativas precedentes (no es una coma ni un siacutembolo dos puntos)

bull Especificacioacuten segundaSe considera la misma especificacioacuten anterior pero cambiando el orden de colocacioacuten de las reglas ahora la produccioacuten vaciacutea se coloca como primera alternativa (soacutelo se muestran las partes modifica-das)

void otrosNombres() | lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt )

Con la entrada de esta especificacioacuten el generador JavaCC produce un mensaje en el que se avisa de que las dos alternativas que estaacuten colocadas detraacutes de la alternativa vaciacutea nunca podraacuten seleccionarse aun asiacute se genera un analizador Pero es un analizador que funciona incorrectamente asiacute por ejemplo la entrada

mxa nyz = 57

se considera incorrecta despueacutes de visto el primer nombre se intenta aplicar la produccioacuten vaciacutea (se espera encontrar el siacutembolo igual) pero habriacutea que aplicar la regla cuyo siacutembolo director es la coma

bull Especificacioacuten terceraSe utilizan las posibilidades de la notacioacuten BNF-Ampliada para prescindir de la especificacioacuten expliacuteci-ta de la palabra vaciacutea se aprovecha que JavaCC admite el operador + para indicar repeticiones de una o maacutes veces la alternativa vaciacutea estaacute impliacutecitamente contemplada en la posibilidad de cero repeti-ciones que contempla el operador (soacutelo se muestran las partes modificadas en la especificacioacuten)

void otrosNombres() ( lt nombre gt )+ | ( lt nombre gt )

El meacutetodo otrosNombres() generado hace las comprobaciones en el siguiente orden

- se comprueba si la pieza por adelantado es una coma (siacutembolo inicial de la primera alternativa) y en este caso se selecciona la primera regla- si no es una coma se comprueba si es un siacutembolo dos puntos (siacutembolo inicial de la segunda alter-nativa) y en este caso se selecciona la segunda regla- si no es un siacutembolo dos puntos se considera la palabra vaciacutea como siacutembolo inicial (siempre posi-ble impliacutecitamente) para la segunda alternativa en este caso para que no haya error la pieza por adelantado ha de ser un siacutembolo igual

Se trata pues de un analizador que funciona correctamente

bull Especificacioacuten cuarta

39

Se pone la misma especificacioacuten anterior pero cambiando el orden de consideracioacuten de la regla vaciacutea ahora se incluye impliacutecitamente en la primera alternativa (soacutelo se muestran las partes modificadas)

void otrosNombres() ( lt nombre gt ) | ( lt nombre gt )+

Para esta especificacioacuten el generador JavaCC emite un aviso en el que se indica que la segunda alter -nativa nunca se seleccionaraacute no obstante se genera un analizador que no funciona como se supone que deberiacutea hacerlo por ejemplo el anaacutelisis de la entrada

nyz mxa = 1

produce un error despueacutes de tratado el primer nombre se intenta aplicar la produccioacuten vaciacutea (corres-pondiente a la repeticioacuten de cero veces contemplada en la primera regla) por lo que no se encuentra el esperado siacutembolo igual

40

Page 27: JavaCC

se pasaraacute a una nueva liacutenea tambieacuten se anotaraacute la frase Final del trabajo cuando se alcance el final del fichero de entrada Asiacute por ejemplo si la entrada analizada es

(a + b) (( x )) (y + 7)

los resultados obtenidos seraacuten

() (())()

Final del trabajo

En la especificacioacuten JavaCC que se propone como solucioacuten soacutelo se muestran las reglas sintaacutecticas y le -xicograacuteficas (en el resto no hay cambios) en esta solucioacuten puede apreciarse- las piezas sintaacutecticas correspondientes a los pareacutentesis se han definido de manera nominal para po-der asociarles una accioacuten lexicograacutefica- los caracteres incluidos en la pieza de tipo SKIP se han descompuesto en dos grupos ya que la ac-cioacuten lexicograacutefica incorporada soacutelo afecta a uno de ellos

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ltpAbrirgt expresion() ltpCerrargt

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

TOKEN

26

lt pAbrir ( gt Systemoutprint(()

TOKEN lt pCerrar ) gt Systemoutprint())

ltgtTOKEN lt EOF gt Systemoutprintln ( Final del trabajo )

SKIP n Systemoutprint( n)

SKIP | t | r

loz Bloque de declaraciones lexicograacuteficasCada definicioacuten de una pieza sintaacutectica puede llevar asociada una accioacuten lexicograacutefica que se ejecuta in-mediatamente despueacutes de detectar en la entrada un lexema que se ajusta a la definicioacuten de la pieza cada accioacuten lexicograacutefica es un bloque de coacutedigo Java que puede contener sus propias declaraciones Pero a ve-ces interesa tener declaraciones que sean compartidas por todas las acciones lexicograacuteficas con indepen-dencia de la pieza a la que esteacute asociada cada una de ellas

Para incluir declaraciones que puedan usarse desde el coacutedigo asociado a cualquiera de las piezas sintaacutecti -cas se dispone de una construccioacuten con el siguiente formato

TOKEN_MGR_DECLS

que consta de una palabra reservada seguida del siacutembolo dos puntos seguido de un bloque de coacutedigo Java (delimitado por llaves) esta declaracioacuten se puede colocar en cualquier parte de una especificacioacuten JavaCC si se incluye soacutelo puede ponerse una vez

Todas las declaraciones de variables o de meacutetodos puestas en el bloque de declaraciones lexicograacuteficas compartidas son accesibles desde el coacutedigo de cualquier accioacuten lexicograacutefica la justificacioacuten es sencilla el bloque de declaraciones se traslada al principio del coacutedigo del analizador lexicograacutefico generado y las ac-ciones lexicograacuteficas tambieacuten se trasladan a ese analizador

loz Declaraciones lexicograacuteficas predefinidasDe lo expuesto hasta ahora se aprecia que en una accioacuten lexicograacutefica asociada a una pieza sintaacutectica re-sultan accesibles

- las declaraciones de su propio bloque (si tiene declaraciones)- los componentes del bloque de declaraciones compartidas (si estaacute incluido en la especificacioacuten)

Ademaacutes de esto se tienen otras declaraciones pertenecientes a la clase TokenManager que constituye el analizador lexicograacutefico que tambieacuten son utilizables desde cualquiera de las acciones lexico-graacuteficas estas declaraciones pueden considerarse como ldquopredefinidasrdquo ya que son incorporadas automaacuteti-camente por el generador Entre estas declaraciones accesibles se encuentran las siguientes

[ static ] StringBuffer imagecontiene la cadena de caracteres representativa de la pieza sintaacutectica actual que se acaba de recono-cer en le entrada se trata de un campo de la clase y se declara como static cuando el analizador

27

se ha generado con la opcioacuten (por defecto) Static = true si se generase con la opcioacuten Sta-tic = false no quedariacutea declarada como static

Token matchedToken

variable de clase Token que representa la pieza sintaacutectica actual la que se acaba de encontrar en la entrada se trata de una variable local del meacutetodo al que se ha trasladado el coacutedigo de la accioacuten (y por ello accesible desde ese coacutedigo)

El campo image puede usarse tanto si la accioacuten lexicograacutefica estaacute asociada a una pieza declarada con TOKEN como si lo estaacute a una secuencia declarada con SKIP Sin embargo la variable matchedToken no puede usarse en una accioacuten asociada a un patroacuten especificado como SKIP el motivo es que lo declara-do como SKIP no constituye propiamente una pieza sintaacutectica ya que no se va a comunicar al analizador sintaacutectico

∆ Ejemplo 6

Se propone aquiacute una nueva solucioacuten para el ejemplo nordm 4 expuesto anteriormente

Esta nueva solucioacuten tal y como se expone aquiacute admite algunas mejoras ya que su pretensioacuten soacutelo es ser-vir de ilustracioacuten para algunos aspectos que se acaban de citar (maacutes adelante se propone otra solucioacuten al-ternativa)

En la especificacioacuten de la solucioacuten es posible apreciar esto- las acciones lexicograacuteficas pueden llevar sus propias declaraciones las variables nLineaVar nColumnaVar nLineaCte y nColumnaCte se declaran dentro de las acciones

- la variable matchedToken a la que se hace referencia en el coacutedigo de la accioacuten lexicograacutefica aso-ciada a una variable contiene la informacioacuten sobre la pieza sintaacutectica que se acaba de detectar una va-riable de la expresioacuten analizada anaacutelogamente con la referencia relativa a una constante- el nombre image que aparece en las acciones lexicograacuteficas se refiere al campo de la clase Expr-MinTokenManager (analizador lexicograacutefico) noacutetese que en efecto se comunica como un paraacuteme-tro de clase StringBuffer

- la variable numero se emplea para numerar las constantes y variables encontradas

- los nombres beginLine y beginColumn son campos (puacuteblicos) de la clase Token por ello se pueden aplicar a un objeto de esa clase

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

void unaExpresion()

28

expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n StringBuffer lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

TOKEN lt variable [a-z] gt

int nLineaVar nColumnaVar numero++ nLineaVar = matchedTokenbeginLine nColumnaVar = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaVar nColumnaVar)

TOKEN lt constante ( [0-9] ) + gt

int nLineaCte nColumnaCte

29

numero++ nLineaCte = matchedTokenbeginLine nColumnaCte = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaCte nColumnaCte)

SKIP | n | t | r

La especificacioacuten precedente se puede escribir de otra manera maacutes escueta como se indica a continua-cioacuten (soacutelo se muestran las modificaciones) sobre esta nueva especificacioacuten puede mencionarse- se han eliminado por innecesarias las variables declaradas dentro de las acciones lexicograacuteficas- el nombre image que aparece en el bloque de las declaraciones compartidas se refiere al campo de la clase Token noacutetese que en efecto se aplica a un objeto de esa clase y se comunica como un paraacute -metro de tipo String

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

TOKEN lt variable [a-z] gt

numero++ grabarDatosPieza(numero matchedToken)

TOKEN lt constante ( [0-9] ) + gt

numero++ grabarDatosPieza(numero matchedToken)

loz Accioacuten lexicograacutefica comuacutenSi hubiera que realizar la misma tarea tras la deteccioacuten de todas y cada una de las piezas sintaacutecticas una posibilidad incoacutemoda seriacutea repetir el mismo coacutedigo en todas las acciones lexicograacuteficas para evitar tanta reiteracioacuten se tiene la opcioacuten Common_Token_Action su valor por defecto es false pero si se le asigna el valor true es posible definir una ldquoaccioacuten lexicograacutefica comuacutenrdquo que se escribe una uacutenica vez y se aplica a todas las piezas sintaacutecticas

30

Cuando la opcioacuten Common_Token_Action se establece con el valor true es obligado incluir el blo-que de declaraciones lexicograacuteficas compartidas (TOKEN_MGR_DECLS) Ademaacutes en ese bloque se preci-sa la definicioacuten de un meacutetodo cuyo encabezamiento sea

[ static ] void CommonTokenAction(Token t)

el coacutedigo de este meacutetodo constituye la accioacuten lexicograacutefica comuacuten que se ejecutaraacute despueacutes de la deteccioacuten de cualquier pieza sintaacutectica el meacutetodo tiene un paraacutemetro de tipo Token mediante el que se puede co-municar la informacioacuten correspondiente a la pieza que se acaba de encontrar en la entrada analizada

Si una pieza sintaacutectica tiene asociada una accioacuten lexicograacutefica propia la accioacuten lexicograacutefica comuacuten (si es-taacute definida) se ejecutaraacute inmediatamente despueacutes de realizar la accioacuten propia

La accioacuten lexicograacutefica comuacuten se aplica a todas las piezas sintaacutecticas definidas como TOKEN bien sea no-minalmente (en las reglas lexicograacuteficas) o bien anoacutenimamente (incorporadas dentro de las reglas sintaacutecti-cas) sin embargo no se aplica cuando la secuencia acoplada se corresponde con una expresioacuten regular es-pecificada como SKIP (noacutetese que por el contrario una especificacioacuten SKIP siacute puede tener asociada una accioacuten lexicograacutefica propia)

El coacutedigo del meacutetodo CommonTokenAction se traslada como un componente maacutes del bloque de decla-raciones compartidas (TOKEN_MGR_DECLS) al principio de la clase del analizador lexicograacutefico

∆ Ejemplo 7

Se pretende ahora ampliar el ejemplo de presentacioacuten con la obtencioacuten de una relacioacuten numerada de los lexemas de las piezas sintaacutecticas encontradas en la entrada analizada en el caso de las variables y de las constantes antes del lexema se mostraraacute un indicativo (Var Const) por ejemplo si la expresioacuten de la entrada es

( a + 987 ) b

se grabaraacute la salida

( 1Var a 2+ 3Const 987 4) 5 6Var b 7 8

En la solucioacuten que se propone a continuacioacuten se muestran la definicioacuten de las opciones y las reglas sin-taacutecticas y lexicograacuteficas (en el resto no hay modificaciones) puede resaltarse lo siguiente- las llamadas al meacutetodo CommonTokenAction se incorporan automaacuteticamente no hay que incluir-las en la especificacioacuten como paraacutemetro de llamada a este meacutetodo siempre se emplea el objeto que re -presenta la pieza que se acaba de detectar- la accioacuten lexicograacutefica comuacuten realiza la actualizacioacuten del contador de piezas y la grabacioacuten del lexe-ma de la pieza detectada con su nuacutemero de orden- la accioacuten lexicograacutefica comuacuten tambieacuten se aplica a las piezas sintaacutecticas anoacutenimas (pareacutentesis y ope-radores) y a la pieza especial que representa el final del fichero (EOF)

- las piezas sintaacutecticas ltconstantegt y ltvariablegt tienen asociada una accioacuten lexicograacutefica propia que se ejecuta antes de la accioacuten lexicograacutefica comuacuten- los caracteres que se saltan (SKIP) no estaacuten afectados por la accioacuten lexicograacutefica comuacuten

options Ignore_Case = true Common_Token_Action = true

31

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarIndicativo(String indicativo) Systemoutprint(indicativo + )

static void grabarLexema (int num String lexema) Systemoutprint(lexema + + num + n)

static void CommonTokenAction(Token pieza) numero++ grabarLexema(numero piezaimage)

TOKEN lt variable [a-z] gt

grabarIndicativo(Var)

32

TOKEN lt constante ( [0-9] ) + gt

grabarIndicativo(Const)

SKIP | n | t | r

A modo de ejemplo de aplicacioacuten de la accioacuten lexicograacutefica comuacuten en lo que sigue se ofrece una solucioacuten para la grabacioacuten (en el fichero predefinido de salida) del texto analizado con las liacuteneas numeradas

∆ Ejemplo 8 [ Listado del texto analizado ]Se desea completar el ejemplo de presentacioacuten con la grabacioacuten de un listado con las liacuteneas numeradas del texto analizado (noacutetese que una expresioacuten puede escribirse ocupando varias liacuteneas) No se muestra la especificacioacuten sintaacutectica debido a que no hay ninguna modificacioacuten en ella Los aspectos resentildeables de la solucioacuten que se propone son- se usa la variable numLin como contador de liacuteneas

- en la variable linea se van yuxtaponiendo los lexemas de las piezas sintaacutecticas de una liacutenea esta yuxtaposicioacuten se realiza mediante la accioacuten lexicograacutefica comuacuten- las secuencias reconocidas con los patrones especificados como SKIP no implican la ejecucioacuten de la accioacuten lexicograacutefica comuacuten por ello su lexema (almacenado en la variable image) ha de yuxtaponer-se expliacutecitamente- hay que separar el patroacuten correspondiente al fin de liacutenea para detectar el momento en que ha de gra-barse la liacutenea completa cuyo final se ha alcanzado

options Ignore_Case = true Common_Token_Action = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(nnAnalisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

TOKEN_MGR_DECLS static int numLin = 0 static String linea =

33

static void CommonTokenAction(Token pieza) linea = linea + piezaimage

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP lt | t | r gt linea = linea + image

SKIP n numLin++ Systemoutprintln(numLin + + linea) linea =

A modo de recapitulacioacutenↄ Accioacuten lexicograacutefica Accioacuten sintaacutectica

bull accioacuten lexicograacutefica bloque de coacutedigo Java asociado a una pieza sintaacutectica nominal (TOKEN) o a una secuencia de caracteres (SKIP) se ejecuta cuando se detecta en la entrada la pieza o la secuencia corres-pondiente no se puede aplicar a piezas sintaacutecticas anoacutenimas (autodefinidas con comillas)bull accioacuten sintaacutectica bloque de coacutedigo Java asociado a un punto de la estructura sintaacutectica (el punto indica-do por el sitio de la produccioacuten donde se inserta el bloque) se ejecuta cuando el anaacutelisis de la entrada pa-sa por ese punto de la estructura

ↄ Valor de una pieza sintaacutectica comunicadaEl analizador lexicograacutefico comunica al analizador sintaacutectico una pieza a traveacutes de un valor de la clase Token (fichero Tokenjava) un objeto de la clase Token representa las caracteriacutesticas de la pieza comunicada En la clase Token entre otros se tienen los campos

String imagelexema de la pieza

int beginLine beginColumn endLine endColumnposicioacuten del lexema en el fichero analizado (indicada por los nuacutemeros de fila y de columna de su comienzo y de su terminacioacuten)

ↄ Valor asociado a un siacutembolo

34

En las producciones que especifican la sintaxis se pueden incluir asignaciones que sirven para dejar ano-tado el valor asociado a un siacutembolo

bull siacutembolo no terminal valor = nombreSimbolo()el meacutetodo de anaacutelisis asociado a nombreSimbolo se ha declarado de un cierto tipo (distinto del tipo void) valor ha de ser una variable del mismo tipo que el meacutetodo

bull siacutembolo terminal dato = lt nombrePieza gtel valor asignado es el valor de la clase Token representativo de la pieza sintaacutectica (nominal) dato ha de ser una variable de tipo Token

ↄ Miradas sobre la pieza sintaacutectica comunicadaLa pieza sintaacutectica comunicada puede mirarse desde cada uno de los dos analizadores

bull analizador sintaacutecticola pieza comunicada (desde el analizador lexicograacutefico) se tiene anotada en un campo de la clase del analizador sintaacutectico declarado como

(static) public Token token

este valor es accesible desde el coacutedigo de todos los meacutetodos de anaacutelisis sintaacutectico el contenido del campo variacutea cuando se recibe una nueva pieza sintaacutectica

bull analizador lexicograacuteficola pieza dispuesta para ser comunicada (al analizador sintaacutectico) se tiene anotada en una variable local declarada como

Token matchedToken

este valor es accesible para el coacutedigo de todas las acciones lexicograacuteficas

ↄ El lexema de la pieza comunicadaEl lexema de la pieza sintaacutectica comunicada se encuentra disponible en dos sitios distintos que compar-ten el mismo nombre pero que son de distinto tipo y se usan de distinta manera

bull String image

es un campo de la clase TOKEN puede consultarse en relacioacuten con un objeto de esa clase que esteacute disponible

bull StringBuffer image

es un campo de la clase del analizador lexicograacutefico ( ∙ ∙ ∙ ∙ TokenManagerjava) es accesible desde el coacutedigo de la clase y por lo tanto desde todas las acciones lexicograacuteficas (bloques de coacute-digo que estaacuten incorporados al analizador lexicograacutefico)

se trata de un valor disponible tanto para las piezas sintaacutecticas nominales (TOKEN) como para las secuencias que se saltan (SKIP)

ↄ Resumen relativo a la lexicografiacutea

TOKEN pieza sintaacutectica nominal∙ ∙ ∙ pieza sintaacutectica anoacutenimaSKIP secuencia de caracteres que no forman pieza

35

Precisiones sobre el analizador sintaacutectico generadoPuede decirse que en su funcionamiento maacutes sencillo cuando no se emplean las posibilidades de examen por adelantado (ldquolookaheadrdquo) JavaCC comprueba que las reglas sintaacutecticas cumplen la condicioacuten LL(1) y generan un analizador descendente-predictivo-recursivo Pero esta apreciacioacuten hay que matizarla ya que el analizador sintaacutectico generado no se ajusta en sentido estricto al modelo de analizador LL(1)

En el modelo de anaacutelisis LL(1) el orden del examen de las distintas alternativas de expansioacuten de un siacutembo-lo no terminal es indiferente ya que se conocen los siacutembolos directores de cada una de ellas (que resultan ser conjuntos disjuntos) en la implementacioacuten del subprograma de anaacutelisis asociado al siacutembolo no terminal ltNombreSimbgt cuyas producciones son

ltNombreSimbgt = α1

| α2

∙ ∙ ∙

| αn

se podriacutea preguntar en cualquier orden si la pieza actual pertenece al conjunto de siacutembolos directores de las distintas reglas Sin embargo en el analizador sintaacutectico generado por JavaCC la implementacioacuten del meacute-todo asociado al siacutembolo no terminal ltNombreSimbgt no sigue esa pauta

Si las anteriores reglas sintaacutecticas se transcriben en JavaCC de la forma

void nombreSimb () α1-jcc

| α2-jcc

∙ ∙ ∙

| αn-jcc

1

accioacutenlexicograacutefica

comuacuten

Commom -TokenAction2

accioacutenlexicograacutefica

propia

(accioacuten ligadaa una pieza)3

bloque dedeclaracioneslexicograacuteficas

TOKEN_MGR_DECLS4

declaracioneslexicograacuteficaspredefinidas

(disponibilidad

de uso)TOKENsisisisi∙ ∙ ∙sino--SKIPnosisisi

Las indicaciones si de las columnas 3 y 4 son consecuencia de las indicaciones de la columna 2

La accioacuten lexicograacutefica comuacuten se ejecuta despueacutes de la accioacuten lexicograacutefica propia

36

el meacutetodo de anaacutelisis generado procede de la siguiente manera

se van considerando las alternativas sucesivamente en el mismo orden en el que estaacuten especifica-das primero la de α1 (α1-jcc) despueacutes la de α2 (α2-jcc) etc

para cada alternativa seleccionada se comprueba si la pieza por adelantado coincide con alguno de los siacutembolos iniciales de la parte derecha y si hay coincidencia se prosigue el anaacutelisis con ella si no hay coincidencia se pasa a la alternativa siguiente

siguiendo con este orden si se llega al final de las alternativas sin haberse encontrado coincidencia alguna se produce un error sintaacutectico

En el analizador generado por JavaCC hay una peculiaridad que no se tiene en los analizadores LL(1) es-trictamente considerados la palabra vaciacutea siacute se considera como posible siacutembolo inicial de la parte derecha una regla (esto ocurre siempre que la parte derecha es anulable) De esta peculiaridad se deriva una conse-cuencia que ha de tenerse en cuenta al escribir la especificacioacuten dado que siempre es posible considerar la palabra vaciacutea como la siguiente pieza por adelantado si se llega a examinar la alternativa correspondiente a una produccioacuten anulable el analizador generado siempre seleccionaraacute esa alternativa con independencia de la siguiente pieza que esteacute presente en la entrada que queda por analizar

Asiacute pues si alguna de las producciones que definen un siacutembolo no terminal es anulable es importante el or-den en que se escriben las alternativas en una especificacioacuten sintaacutectica JavaCC el funcionamiento del ana-lizador generado siacute depende de la colocacioacuten elegida para las reglas

Para ilustrar estas peculiaridades del analizador sintaacutectico generado por JavaCC a continuacioacuten se expone un ejemplo

∆ Ejemplo 9

Se quiere obtener un analizador leacutexico-sintaacutectico de textos que se ajustan al siguiente formato una se-cuencia de uno o maacutes nombres seguida del siacutembolo igual seguido de un nuacutemero entero si la secuencia tiene maacutes de un nombre han de estar separados bien por una coma en todos los casos o bien por el siacutem-bolo dos puntos en todos los casos esto es son correctas las secuencias

uno dos tres = 123exclusivo = 0Abajo Arriba = 57

y son incorrectas las secuenciasuno dos tres = 123

exclusivo inclusive = 0

fuera dentro = 56

Una gramaacutetica que define las secuencias con este formato es

ltListagt = nombre ltOtrosNombresgt = numero

ltOtrosNombresgt = nombre nombre | nombre nombre | ε

se trata de una gramaacutetica que cumple la condicioacuten LL(1)- el siacutembolo director para la alternativa vaciacutea es el siacutembolo igual es evidente pues que las tres reglas de ltOtrosNombresgt tienen siacutembolos directores distintos

37

- la decisioacuten de abandonar la repeticioacuten del anaacutelisis de un nuevo nombre (en las dos primeras alterna-tivas de ltOtrosNombresgt) se puede tomar ante la presencia del siacutembolo igual

En la implementacioacuten de un analizador sintaacutectico LL(1) en sentido estricto la seleccioacuten de la alternativa vaciacutea para expandir el siacutembolo ltOtrosNombresgt se hariacutea tras comprobar expliacutecitamente que la pie-za sintaacutectica por adelantado es el siacutembolo igual

El funcionamiento del analizador generado por JavaCC depende del orden de colocacioacuten de las reglas A continuacioacuten se exponen varias posibilidades de especificacioacuten y se comenta el analizador generado en cada caso

bull Especificacioacuten primeraSe transcriben literalmente las reglas tal y como se han escrito antes en la notacioacuten BNF noacutetese que para poder codificar la parte derecha de la regla vaciacutea ha de incluirse una accioacuten sintaacutectica vaciacutea

PARSER_BEGIN (Igualdad)

public class Igualdad

public static void main (String[] argum) throws ParseException Igualdad analisis = new Igualdad(Systemin) analisissecuencia() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (Igualdad)

void secuencia() lista() ltEOFgt

void lista() lt nombre gt otrosNombres() = lt numero gt

void otrosNombres() lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt ) |

TOKEN lt nombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN lt numero ( [ 0 - 9 ] )+ gt

SKIP | r | n

38

El analizador generado funciona correctamente puesto que en el meacutetodo otrosNombres se selec-ciona la alternativa vaciacutea despueacutes de haber comprobado que la pieza por adelantado no es siacutembolo ini-cial de ninguna de las dos alternativas precedentes (no es una coma ni un siacutembolo dos puntos)

bull Especificacioacuten segundaSe considera la misma especificacioacuten anterior pero cambiando el orden de colocacioacuten de las reglas ahora la produccioacuten vaciacutea se coloca como primera alternativa (soacutelo se muestran las partes modifica-das)

void otrosNombres() | lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt )

Con la entrada de esta especificacioacuten el generador JavaCC produce un mensaje en el que se avisa de que las dos alternativas que estaacuten colocadas detraacutes de la alternativa vaciacutea nunca podraacuten seleccionarse aun asiacute se genera un analizador Pero es un analizador que funciona incorrectamente asiacute por ejemplo la entrada

mxa nyz = 57

se considera incorrecta despueacutes de visto el primer nombre se intenta aplicar la produccioacuten vaciacutea (se espera encontrar el siacutembolo igual) pero habriacutea que aplicar la regla cuyo siacutembolo director es la coma

bull Especificacioacuten terceraSe utilizan las posibilidades de la notacioacuten BNF-Ampliada para prescindir de la especificacioacuten expliacuteci-ta de la palabra vaciacutea se aprovecha que JavaCC admite el operador + para indicar repeticiones de una o maacutes veces la alternativa vaciacutea estaacute impliacutecitamente contemplada en la posibilidad de cero repeti-ciones que contempla el operador (soacutelo se muestran las partes modificadas en la especificacioacuten)

void otrosNombres() ( lt nombre gt )+ | ( lt nombre gt )

El meacutetodo otrosNombres() generado hace las comprobaciones en el siguiente orden

- se comprueba si la pieza por adelantado es una coma (siacutembolo inicial de la primera alternativa) y en este caso se selecciona la primera regla- si no es una coma se comprueba si es un siacutembolo dos puntos (siacutembolo inicial de la segunda alter-nativa) y en este caso se selecciona la segunda regla- si no es un siacutembolo dos puntos se considera la palabra vaciacutea como siacutembolo inicial (siempre posi-ble impliacutecitamente) para la segunda alternativa en este caso para que no haya error la pieza por adelantado ha de ser un siacutembolo igual

Se trata pues de un analizador que funciona correctamente

bull Especificacioacuten cuarta

39

Se pone la misma especificacioacuten anterior pero cambiando el orden de consideracioacuten de la regla vaciacutea ahora se incluye impliacutecitamente en la primera alternativa (soacutelo se muestran las partes modificadas)

void otrosNombres() ( lt nombre gt ) | ( lt nombre gt )+

Para esta especificacioacuten el generador JavaCC emite un aviso en el que se indica que la segunda alter -nativa nunca se seleccionaraacute no obstante se genera un analizador que no funciona como se supone que deberiacutea hacerlo por ejemplo el anaacutelisis de la entrada

nyz mxa = 1

produce un error despueacutes de tratado el primer nombre se intenta aplicar la produccioacuten vaciacutea (corres-pondiente a la repeticioacuten de cero veces contemplada en la primera regla) por lo que no se encuentra el esperado siacutembolo igual

40

Page 28: JavaCC

lt pAbrir ( gt Systemoutprint(()

TOKEN lt pCerrar ) gt Systemoutprint())

ltgtTOKEN lt EOF gt Systemoutprintln ( Final del trabajo )

SKIP n Systemoutprint( n)

SKIP | t | r

loz Bloque de declaraciones lexicograacuteficasCada definicioacuten de una pieza sintaacutectica puede llevar asociada una accioacuten lexicograacutefica que se ejecuta in-mediatamente despueacutes de detectar en la entrada un lexema que se ajusta a la definicioacuten de la pieza cada accioacuten lexicograacutefica es un bloque de coacutedigo Java que puede contener sus propias declaraciones Pero a ve-ces interesa tener declaraciones que sean compartidas por todas las acciones lexicograacuteficas con indepen-dencia de la pieza a la que esteacute asociada cada una de ellas

Para incluir declaraciones que puedan usarse desde el coacutedigo asociado a cualquiera de las piezas sintaacutecti -cas se dispone de una construccioacuten con el siguiente formato

TOKEN_MGR_DECLS

que consta de una palabra reservada seguida del siacutembolo dos puntos seguido de un bloque de coacutedigo Java (delimitado por llaves) esta declaracioacuten se puede colocar en cualquier parte de una especificacioacuten JavaCC si se incluye soacutelo puede ponerse una vez

Todas las declaraciones de variables o de meacutetodos puestas en el bloque de declaraciones lexicograacuteficas compartidas son accesibles desde el coacutedigo de cualquier accioacuten lexicograacutefica la justificacioacuten es sencilla el bloque de declaraciones se traslada al principio del coacutedigo del analizador lexicograacutefico generado y las ac-ciones lexicograacuteficas tambieacuten se trasladan a ese analizador

loz Declaraciones lexicograacuteficas predefinidasDe lo expuesto hasta ahora se aprecia que en una accioacuten lexicograacutefica asociada a una pieza sintaacutectica re-sultan accesibles

- las declaraciones de su propio bloque (si tiene declaraciones)- los componentes del bloque de declaraciones compartidas (si estaacute incluido en la especificacioacuten)

Ademaacutes de esto se tienen otras declaraciones pertenecientes a la clase TokenManager que constituye el analizador lexicograacutefico que tambieacuten son utilizables desde cualquiera de las acciones lexico-graacuteficas estas declaraciones pueden considerarse como ldquopredefinidasrdquo ya que son incorporadas automaacuteti-camente por el generador Entre estas declaraciones accesibles se encuentran las siguientes

[ static ] StringBuffer imagecontiene la cadena de caracteres representativa de la pieza sintaacutectica actual que se acaba de recono-cer en le entrada se trata de un campo de la clase y se declara como static cuando el analizador

27

se ha generado con la opcioacuten (por defecto) Static = true si se generase con la opcioacuten Sta-tic = false no quedariacutea declarada como static

Token matchedToken

variable de clase Token que representa la pieza sintaacutectica actual la que se acaba de encontrar en la entrada se trata de una variable local del meacutetodo al que se ha trasladado el coacutedigo de la accioacuten (y por ello accesible desde ese coacutedigo)

El campo image puede usarse tanto si la accioacuten lexicograacutefica estaacute asociada a una pieza declarada con TOKEN como si lo estaacute a una secuencia declarada con SKIP Sin embargo la variable matchedToken no puede usarse en una accioacuten asociada a un patroacuten especificado como SKIP el motivo es que lo declara-do como SKIP no constituye propiamente una pieza sintaacutectica ya que no se va a comunicar al analizador sintaacutectico

∆ Ejemplo 6

Se propone aquiacute una nueva solucioacuten para el ejemplo nordm 4 expuesto anteriormente

Esta nueva solucioacuten tal y como se expone aquiacute admite algunas mejoras ya que su pretensioacuten soacutelo es ser-vir de ilustracioacuten para algunos aspectos que se acaban de citar (maacutes adelante se propone otra solucioacuten al-ternativa)

En la especificacioacuten de la solucioacuten es posible apreciar esto- las acciones lexicograacuteficas pueden llevar sus propias declaraciones las variables nLineaVar nColumnaVar nLineaCte y nColumnaCte se declaran dentro de las acciones

- la variable matchedToken a la que se hace referencia en el coacutedigo de la accioacuten lexicograacutefica aso-ciada a una variable contiene la informacioacuten sobre la pieza sintaacutectica que se acaba de detectar una va-riable de la expresioacuten analizada anaacutelogamente con la referencia relativa a una constante- el nombre image que aparece en las acciones lexicograacuteficas se refiere al campo de la clase Expr-MinTokenManager (analizador lexicograacutefico) noacutetese que en efecto se comunica como un paraacuteme-tro de clase StringBuffer

- la variable numero se emplea para numerar las constantes y variables encontradas

- los nombres beginLine y beginColumn son campos (puacuteblicos) de la clase Token por ello se pueden aplicar a un objeto de esa clase

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

void unaExpresion()

28

expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n StringBuffer lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

TOKEN lt variable [a-z] gt

int nLineaVar nColumnaVar numero++ nLineaVar = matchedTokenbeginLine nColumnaVar = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaVar nColumnaVar)

TOKEN lt constante ( [0-9] ) + gt

int nLineaCte nColumnaCte

29

numero++ nLineaCte = matchedTokenbeginLine nColumnaCte = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaCte nColumnaCte)

SKIP | n | t | r

La especificacioacuten precedente se puede escribir de otra manera maacutes escueta como se indica a continua-cioacuten (soacutelo se muestran las modificaciones) sobre esta nueva especificacioacuten puede mencionarse- se han eliminado por innecesarias las variables declaradas dentro de las acciones lexicograacuteficas- el nombre image que aparece en el bloque de las declaraciones compartidas se refiere al campo de la clase Token noacutetese que en efecto se aplica a un objeto de esa clase y se comunica como un paraacute -metro de tipo String

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

TOKEN lt variable [a-z] gt

numero++ grabarDatosPieza(numero matchedToken)

TOKEN lt constante ( [0-9] ) + gt

numero++ grabarDatosPieza(numero matchedToken)

loz Accioacuten lexicograacutefica comuacutenSi hubiera que realizar la misma tarea tras la deteccioacuten de todas y cada una de las piezas sintaacutecticas una posibilidad incoacutemoda seriacutea repetir el mismo coacutedigo en todas las acciones lexicograacuteficas para evitar tanta reiteracioacuten se tiene la opcioacuten Common_Token_Action su valor por defecto es false pero si se le asigna el valor true es posible definir una ldquoaccioacuten lexicograacutefica comuacutenrdquo que se escribe una uacutenica vez y se aplica a todas las piezas sintaacutecticas

30

Cuando la opcioacuten Common_Token_Action se establece con el valor true es obligado incluir el blo-que de declaraciones lexicograacuteficas compartidas (TOKEN_MGR_DECLS) Ademaacutes en ese bloque se preci-sa la definicioacuten de un meacutetodo cuyo encabezamiento sea

[ static ] void CommonTokenAction(Token t)

el coacutedigo de este meacutetodo constituye la accioacuten lexicograacutefica comuacuten que se ejecutaraacute despueacutes de la deteccioacuten de cualquier pieza sintaacutectica el meacutetodo tiene un paraacutemetro de tipo Token mediante el que se puede co-municar la informacioacuten correspondiente a la pieza que se acaba de encontrar en la entrada analizada

Si una pieza sintaacutectica tiene asociada una accioacuten lexicograacutefica propia la accioacuten lexicograacutefica comuacuten (si es-taacute definida) se ejecutaraacute inmediatamente despueacutes de realizar la accioacuten propia

La accioacuten lexicograacutefica comuacuten se aplica a todas las piezas sintaacutecticas definidas como TOKEN bien sea no-minalmente (en las reglas lexicograacuteficas) o bien anoacutenimamente (incorporadas dentro de las reglas sintaacutecti-cas) sin embargo no se aplica cuando la secuencia acoplada se corresponde con una expresioacuten regular es-pecificada como SKIP (noacutetese que por el contrario una especificacioacuten SKIP siacute puede tener asociada una accioacuten lexicograacutefica propia)

El coacutedigo del meacutetodo CommonTokenAction se traslada como un componente maacutes del bloque de decla-raciones compartidas (TOKEN_MGR_DECLS) al principio de la clase del analizador lexicograacutefico

∆ Ejemplo 7

Se pretende ahora ampliar el ejemplo de presentacioacuten con la obtencioacuten de una relacioacuten numerada de los lexemas de las piezas sintaacutecticas encontradas en la entrada analizada en el caso de las variables y de las constantes antes del lexema se mostraraacute un indicativo (Var Const) por ejemplo si la expresioacuten de la entrada es

( a + 987 ) b

se grabaraacute la salida

( 1Var a 2+ 3Const 987 4) 5 6Var b 7 8

En la solucioacuten que se propone a continuacioacuten se muestran la definicioacuten de las opciones y las reglas sin-taacutecticas y lexicograacuteficas (en el resto no hay modificaciones) puede resaltarse lo siguiente- las llamadas al meacutetodo CommonTokenAction se incorporan automaacuteticamente no hay que incluir-las en la especificacioacuten como paraacutemetro de llamada a este meacutetodo siempre se emplea el objeto que re -presenta la pieza que se acaba de detectar- la accioacuten lexicograacutefica comuacuten realiza la actualizacioacuten del contador de piezas y la grabacioacuten del lexe-ma de la pieza detectada con su nuacutemero de orden- la accioacuten lexicograacutefica comuacuten tambieacuten se aplica a las piezas sintaacutecticas anoacutenimas (pareacutentesis y ope-radores) y a la pieza especial que representa el final del fichero (EOF)

- las piezas sintaacutecticas ltconstantegt y ltvariablegt tienen asociada una accioacuten lexicograacutefica propia que se ejecuta antes de la accioacuten lexicograacutefica comuacuten- los caracteres que se saltan (SKIP) no estaacuten afectados por la accioacuten lexicograacutefica comuacuten

options Ignore_Case = true Common_Token_Action = true

31

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarIndicativo(String indicativo) Systemoutprint(indicativo + )

static void grabarLexema (int num String lexema) Systemoutprint(lexema + + num + n)

static void CommonTokenAction(Token pieza) numero++ grabarLexema(numero piezaimage)

TOKEN lt variable [a-z] gt

grabarIndicativo(Var)

32

TOKEN lt constante ( [0-9] ) + gt

grabarIndicativo(Const)

SKIP | n | t | r

A modo de ejemplo de aplicacioacuten de la accioacuten lexicograacutefica comuacuten en lo que sigue se ofrece una solucioacuten para la grabacioacuten (en el fichero predefinido de salida) del texto analizado con las liacuteneas numeradas

∆ Ejemplo 8 [ Listado del texto analizado ]Se desea completar el ejemplo de presentacioacuten con la grabacioacuten de un listado con las liacuteneas numeradas del texto analizado (noacutetese que una expresioacuten puede escribirse ocupando varias liacuteneas) No se muestra la especificacioacuten sintaacutectica debido a que no hay ninguna modificacioacuten en ella Los aspectos resentildeables de la solucioacuten que se propone son- se usa la variable numLin como contador de liacuteneas

- en la variable linea se van yuxtaponiendo los lexemas de las piezas sintaacutecticas de una liacutenea esta yuxtaposicioacuten se realiza mediante la accioacuten lexicograacutefica comuacuten- las secuencias reconocidas con los patrones especificados como SKIP no implican la ejecucioacuten de la accioacuten lexicograacutefica comuacuten por ello su lexema (almacenado en la variable image) ha de yuxtaponer-se expliacutecitamente- hay que separar el patroacuten correspondiente al fin de liacutenea para detectar el momento en que ha de gra-barse la liacutenea completa cuyo final se ha alcanzado

options Ignore_Case = true Common_Token_Action = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(nnAnalisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

TOKEN_MGR_DECLS static int numLin = 0 static String linea =

33

static void CommonTokenAction(Token pieza) linea = linea + piezaimage

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP lt | t | r gt linea = linea + image

SKIP n numLin++ Systemoutprintln(numLin + + linea) linea =

A modo de recapitulacioacutenↄ Accioacuten lexicograacutefica Accioacuten sintaacutectica

bull accioacuten lexicograacutefica bloque de coacutedigo Java asociado a una pieza sintaacutectica nominal (TOKEN) o a una secuencia de caracteres (SKIP) se ejecuta cuando se detecta en la entrada la pieza o la secuencia corres-pondiente no se puede aplicar a piezas sintaacutecticas anoacutenimas (autodefinidas con comillas)bull accioacuten sintaacutectica bloque de coacutedigo Java asociado a un punto de la estructura sintaacutectica (el punto indica-do por el sitio de la produccioacuten donde se inserta el bloque) se ejecuta cuando el anaacutelisis de la entrada pa-sa por ese punto de la estructura

ↄ Valor de una pieza sintaacutectica comunicadaEl analizador lexicograacutefico comunica al analizador sintaacutectico una pieza a traveacutes de un valor de la clase Token (fichero Tokenjava) un objeto de la clase Token representa las caracteriacutesticas de la pieza comunicada En la clase Token entre otros se tienen los campos

String imagelexema de la pieza

int beginLine beginColumn endLine endColumnposicioacuten del lexema en el fichero analizado (indicada por los nuacutemeros de fila y de columna de su comienzo y de su terminacioacuten)

ↄ Valor asociado a un siacutembolo

34

En las producciones que especifican la sintaxis se pueden incluir asignaciones que sirven para dejar ano-tado el valor asociado a un siacutembolo

bull siacutembolo no terminal valor = nombreSimbolo()el meacutetodo de anaacutelisis asociado a nombreSimbolo se ha declarado de un cierto tipo (distinto del tipo void) valor ha de ser una variable del mismo tipo que el meacutetodo

bull siacutembolo terminal dato = lt nombrePieza gtel valor asignado es el valor de la clase Token representativo de la pieza sintaacutectica (nominal) dato ha de ser una variable de tipo Token

ↄ Miradas sobre la pieza sintaacutectica comunicadaLa pieza sintaacutectica comunicada puede mirarse desde cada uno de los dos analizadores

bull analizador sintaacutecticola pieza comunicada (desde el analizador lexicograacutefico) se tiene anotada en un campo de la clase del analizador sintaacutectico declarado como

(static) public Token token

este valor es accesible desde el coacutedigo de todos los meacutetodos de anaacutelisis sintaacutectico el contenido del campo variacutea cuando se recibe una nueva pieza sintaacutectica

bull analizador lexicograacuteficola pieza dispuesta para ser comunicada (al analizador sintaacutectico) se tiene anotada en una variable local declarada como

Token matchedToken

este valor es accesible para el coacutedigo de todas las acciones lexicograacuteficas

ↄ El lexema de la pieza comunicadaEl lexema de la pieza sintaacutectica comunicada se encuentra disponible en dos sitios distintos que compar-ten el mismo nombre pero que son de distinto tipo y se usan de distinta manera

bull String image

es un campo de la clase TOKEN puede consultarse en relacioacuten con un objeto de esa clase que esteacute disponible

bull StringBuffer image

es un campo de la clase del analizador lexicograacutefico ( ∙ ∙ ∙ ∙ TokenManagerjava) es accesible desde el coacutedigo de la clase y por lo tanto desde todas las acciones lexicograacuteficas (bloques de coacute-digo que estaacuten incorporados al analizador lexicograacutefico)

se trata de un valor disponible tanto para las piezas sintaacutecticas nominales (TOKEN) como para las secuencias que se saltan (SKIP)

ↄ Resumen relativo a la lexicografiacutea

TOKEN pieza sintaacutectica nominal∙ ∙ ∙ pieza sintaacutectica anoacutenimaSKIP secuencia de caracteres que no forman pieza

35

Precisiones sobre el analizador sintaacutectico generadoPuede decirse que en su funcionamiento maacutes sencillo cuando no se emplean las posibilidades de examen por adelantado (ldquolookaheadrdquo) JavaCC comprueba que las reglas sintaacutecticas cumplen la condicioacuten LL(1) y generan un analizador descendente-predictivo-recursivo Pero esta apreciacioacuten hay que matizarla ya que el analizador sintaacutectico generado no se ajusta en sentido estricto al modelo de analizador LL(1)

En el modelo de anaacutelisis LL(1) el orden del examen de las distintas alternativas de expansioacuten de un siacutembo-lo no terminal es indiferente ya que se conocen los siacutembolos directores de cada una de ellas (que resultan ser conjuntos disjuntos) en la implementacioacuten del subprograma de anaacutelisis asociado al siacutembolo no terminal ltNombreSimbgt cuyas producciones son

ltNombreSimbgt = α1

| α2

∙ ∙ ∙

| αn

se podriacutea preguntar en cualquier orden si la pieza actual pertenece al conjunto de siacutembolos directores de las distintas reglas Sin embargo en el analizador sintaacutectico generado por JavaCC la implementacioacuten del meacute-todo asociado al siacutembolo no terminal ltNombreSimbgt no sigue esa pauta

Si las anteriores reglas sintaacutecticas se transcriben en JavaCC de la forma

void nombreSimb () α1-jcc

| α2-jcc

∙ ∙ ∙

| αn-jcc

1

accioacutenlexicograacutefica

comuacuten

Commom -TokenAction2

accioacutenlexicograacutefica

propia

(accioacuten ligadaa una pieza)3

bloque dedeclaracioneslexicograacuteficas

TOKEN_MGR_DECLS4

declaracioneslexicograacuteficaspredefinidas

(disponibilidad

de uso)TOKENsisisisi∙ ∙ ∙sino--SKIPnosisisi

Las indicaciones si de las columnas 3 y 4 son consecuencia de las indicaciones de la columna 2

La accioacuten lexicograacutefica comuacuten se ejecuta despueacutes de la accioacuten lexicograacutefica propia

36

el meacutetodo de anaacutelisis generado procede de la siguiente manera

se van considerando las alternativas sucesivamente en el mismo orden en el que estaacuten especifica-das primero la de α1 (α1-jcc) despueacutes la de α2 (α2-jcc) etc

para cada alternativa seleccionada se comprueba si la pieza por adelantado coincide con alguno de los siacutembolos iniciales de la parte derecha y si hay coincidencia se prosigue el anaacutelisis con ella si no hay coincidencia se pasa a la alternativa siguiente

siguiendo con este orden si se llega al final de las alternativas sin haberse encontrado coincidencia alguna se produce un error sintaacutectico

En el analizador generado por JavaCC hay una peculiaridad que no se tiene en los analizadores LL(1) es-trictamente considerados la palabra vaciacutea siacute se considera como posible siacutembolo inicial de la parte derecha una regla (esto ocurre siempre que la parte derecha es anulable) De esta peculiaridad se deriva una conse-cuencia que ha de tenerse en cuenta al escribir la especificacioacuten dado que siempre es posible considerar la palabra vaciacutea como la siguiente pieza por adelantado si se llega a examinar la alternativa correspondiente a una produccioacuten anulable el analizador generado siempre seleccionaraacute esa alternativa con independencia de la siguiente pieza que esteacute presente en la entrada que queda por analizar

Asiacute pues si alguna de las producciones que definen un siacutembolo no terminal es anulable es importante el or-den en que se escriben las alternativas en una especificacioacuten sintaacutectica JavaCC el funcionamiento del ana-lizador generado siacute depende de la colocacioacuten elegida para las reglas

Para ilustrar estas peculiaridades del analizador sintaacutectico generado por JavaCC a continuacioacuten se expone un ejemplo

∆ Ejemplo 9

Se quiere obtener un analizador leacutexico-sintaacutectico de textos que se ajustan al siguiente formato una se-cuencia de uno o maacutes nombres seguida del siacutembolo igual seguido de un nuacutemero entero si la secuencia tiene maacutes de un nombre han de estar separados bien por una coma en todos los casos o bien por el siacutem-bolo dos puntos en todos los casos esto es son correctas las secuencias

uno dos tres = 123exclusivo = 0Abajo Arriba = 57

y son incorrectas las secuenciasuno dos tres = 123

exclusivo inclusive = 0

fuera dentro = 56

Una gramaacutetica que define las secuencias con este formato es

ltListagt = nombre ltOtrosNombresgt = numero

ltOtrosNombresgt = nombre nombre | nombre nombre | ε

se trata de una gramaacutetica que cumple la condicioacuten LL(1)- el siacutembolo director para la alternativa vaciacutea es el siacutembolo igual es evidente pues que las tres reglas de ltOtrosNombresgt tienen siacutembolos directores distintos

37

- la decisioacuten de abandonar la repeticioacuten del anaacutelisis de un nuevo nombre (en las dos primeras alterna-tivas de ltOtrosNombresgt) se puede tomar ante la presencia del siacutembolo igual

En la implementacioacuten de un analizador sintaacutectico LL(1) en sentido estricto la seleccioacuten de la alternativa vaciacutea para expandir el siacutembolo ltOtrosNombresgt se hariacutea tras comprobar expliacutecitamente que la pie-za sintaacutectica por adelantado es el siacutembolo igual

El funcionamiento del analizador generado por JavaCC depende del orden de colocacioacuten de las reglas A continuacioacuten se exponen varias posibilidades de especificacioacuten y se comenta el analizador generado en cada caso

bull Especificacioacuten primeraSe transcriben literalmente las reglas tal y como se han escrito antes en la notacioacuten BNF noacutetese que para poder codificar la parte derecha de la regla vaciacutea ha de incluirse una accioacuten sintaacutectica vaciacutea

PARSER_BEGIN (Igualdad)

public class Igualdad

public static void main (String[] argum) throws ParseException Igualdad analisis = new Igualdad(Systemin) analisissecuencia() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (Igualdad)

void secuencia() lista() ltEOFgt

void lista() lt nombre gt otrosNombres() = lt numero gt

void otrosNombres() lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt ) |

TOKEN lt nombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN lt numero ( [ 0 - 9 ] )+ gt

SKIP | r | n

38

El analizador generado funciona correctamente puesto que en el meacutetodo otrosNombres se selec-ciona la alternativa vaciacutea despueacutes de haber comprobado que la pieza por adelantado no es siacutembolo ini-cial de ninguna de las dos alternativas precedentes (no es una coma ni un siacutembolo dos puntos)

bull Especificacioacuten segundaSe considera la misma especificacioacuten anterior pero cambiando el orden de colocacioacuten de las reglas ahora la produccioacuten vaciacutea se coloca como primera alternativa (soacutelo se muestran las partes modifica-das)

void otrosNombres() | lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt )

Con la entrada de esta especificacioacuten el generador JavaCC produce un mensaje en el que se avisa de que las dos alternativas que estaacuten colocadas detraacutes de la alternativa vaciacutea nunca podraacuten seleccionarse aun asiacute se genera un analizador Pero es un analizador que funciona incorrectamente asiacute por ejemplo la entrada

mxa nyz = 57

se considera incorrecta despueacutes de visto el primer nombre se intenta aplicar la produccioacuten vaciacutea (se espera encontrar el siacutembolo igual) pero habriacutea que aplicar la regla cuyo siacutembolo director es la coma

bull Especificacioacuten terceraSe utilizan las posibilidades de la notacioacuten BNF-Ampliada para prescindir de la especificacioacuten expliacuteci-ta de la palabra vaciacutea se aprovecha que JavaCC admite el operador + para indicar repeticiones de una o maacutes veces la alternativa vaciacutea estaacute impliacutecitamente contemplada en la posibilidad de cero repeti-ciones que contempla el operador (soacutelo se muestran las partes modificadas en la especificacioacuten)

void otrosNombres() ( lt nombre gt )+ | ( lt nombre gt )

El meacutetodo otrosNombres() generado hace las comprobaciones en el siguiente orden

- se comprueba si la pieza por adelantado es una coma (siacutembolo inicial de la primera alternativa) y en este caso se selecciona la primera regla- si no es una coma se comprueba si es un siacutembolo dos puntos (siacutembolo inicial de la segunda alter-nativa) y en este caso se selecciona la segunda regla- si no es un siacutembolo dos puntos se considera la palabra vaciacutea como siacutembolo inicial (siempre posi-ble impliacutecitamente) para la segunda alternativa en este caso para que no haya error la pieza por adelantado ha de ser un siacutembolo igual

Se trata pues de un analizador que funciona correctamente

bull Especificacioacuten cuarta

39

Se pone la misma especificacioacuten anterior pero cambiando el orden de consideracioacuten de la regla vaciacutea ahora se incluye impliacutecitamente en la primera alternativa (soacutelo se muestran las partes modificadas)

void otrosNombres() ( lt nombre gt ) | ( lt nombre gt )+

Para esta especificacioacuten el generador JavaCC emite un aviso en el que se indica que la segunda alter -nativa nunca se seleccionaraacute no obstante se genera un analizador que no funciona como se supone que deberiacutea hacerlo por ejemplo el anaacutelisis de la entrada

nyz mxa = 1

produce un error despueacutes de tratado el primer nombre se intenta aplicar la produccioacuten vaciacutea (corres-pondiente a la repeticioacuten de cero veces contemplada en la primera regla) por lo que no se encuentra el esperado siacutembolo igual

40

Page 29: JavaCC

se ha generado con la opcioacuten (por defecto) Static = true si se generase con la opcioacuten Sta-tic = false no quedariacutea declarada como static

Token matchedToken

variable de clase Token que representa la pieza sintaacutectica actual la que se acaba de encontrar en la entrada se trata de una variable local del meacutetodo al que se ha trasladado el coacutedigo de la accioacuten (y por ello accesible desde ese coacutedigo)

El campo image puede usarse tanto si la accioacuten lexicograacutefica estaacute asociada a una pieza declarada con TOKEN como si lo estaacute a una secuencia declarada con SKIP Sin embargo la variable matchedToken no puede usarse en una accioacuten asociada a un patroacuten especificado como SKIP el motivo es que lo declara-do como SKIP no constituye propiamente una pieza sintaacutectica ya que no se va a comunicar al analizador sintaacutectico

∆ Ejemplo 6

Se propone aquiacute una nueva solucioacuten para el ejemplo nordm 4 expuesto anteriormente

Esta nueva solucioacuten tal y como se expone aquiacute admite algunas mejoras ya que su pretensioacuten soacutelo es ser-vir de ilustracioacuten para algunos aspectos que se acaban de citar (maacutes adelante se propone otra solucioacuten al-ternativa)

En la especificacioacuten de la solucioacuten es posible apreciar esto- las acciones lexicograacuteficas pueden llevar sus propias declaraciones las variables nLineaVar nColumnaVar nLineaCte y nColumnaCte se declaran dentro de las acciones

- la variable matchedToken a la que se hace referencia en el coacutedigo de la accioacuten lexicograacutefica aso-ciada a una variable contiene la informacioacuten sobre la pieza sintaacutectica que se acaba de detectar una va-riable de la expresioacuten analizada anaacutelogamente con la referencia relativa a una constante- el nombre image que aparece en las acciones lexicograacuteficas se refiere al campo de la clase Expr-MinTokenManager (analizador lexicograacutefico) noacutetese que en efecto se comunica como un paraacuteme-tro de clase StringBuffer

- la variable numero se emplea para numerar las constantes y variables encontradas

- los nombres beginLine y beginColumn son campos (puacuteblicos) de la clase Token por ello se pueden aplicar a un objeto de esa clase

options Ignore_Case = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

void unaExpresion()

28

expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n StringBuffer lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

TOKEN lt variable [a-z] gt

int nLineaVar nColumnaVar numero++ nLineaVar = matchedTokenbeginLine nColumnaVar = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaVar nColumnaVar)

TOKEN lt constante ( [0-9] ) + gt

int nLineaCte nColumnaCte

29

numero++ nLineaCte = matchedTokenbeginLine nColumnaCte = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaCte nColumnaCte)

SKIP | n | t | r

La especificacioacuten precedente se puede escribir de otra manera maacutes escueta como se indica a continua-cioacuten (soacutelo se muestran las modificaciones) sobre esta nueva especificacioacuten puede mencionarse- se han eliminado por innecesarias las variables declaradas dentro de las acciones lexicograacuteficas- el nombre image que aparece en el bloque de las declaraciones compartidas se refiere al campo de la clase Token noacutetese que en efecto se aplica a un objeto de esa clase y se comunica como un paraacute -metro de tipo String

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

TOKEN lt variable [a-z] gt

numero++ grabarDatosPieza(numero matchedToken)

TOKEN lt constante ( [0-9] ) + gt

numero++ grabarDatosPieza(numero matchedToken)

loz Accioacuten lexicograacutefica comuacutenSi hubiera que realizar la misma tarea tras la deteccioacuten de todas y cada una de las piezas sintaacutecticas una posibilidad incoacutemoda seriacutea repetir el mismo coacutedigo en todas las acciones lexicograacuteficas para evitar tanta reiteracioacuten se tiene la opcioacuten Common_Token_Action su valor por defecto es false pero si se le asigna el valor true es posible definir una ldquoaccioacuten lexicograacutefica comuacutenrdquo que se escribe una uacutenica vez y se aplica a todas las piezas sintaacutecticas

30

Cuando la opcioacuten Common_Token_Action se establece con el valor true es obligado incluir el blo-que de declaraciones lexicograacuteficas compartidas (TOKEN_MGR_DECLS) Ademaacutes en ese bloque se preci-sa la definicioacuten de un meacutetodo cuyo encabezamiento sea

[ static ] void CommonTokenAction(Token t)

el coacutedigo de este meacutetodo constituye la accioacuten lexicograacutefica comuacuten que se ejecutaraacute despueacutes de la deteccioacuten de cualquier pieza sintaacutectica el meacutetodo tiene un paraacutemetro de tipo Token mediante el que se puede co-municar la informacioacuten correspondiente a la pieza que se acaba de encontrar en la entrada analizada

Si una pieza sintaacutectica tiene asociada una accioacuten lexicograacutefica propia la accioacuten lexicograacutefica comuacuten (si es-taacute definida) se ejecutaraacute inmediatamente despueacutes de realizar la accioacuten propia

La accioacuten lexicograacutefica comuacuten se aplica a todas las piezas sintaacutecticas definidas como TOKEN bien sea no-minalmente (en las reglas lexicograacuteficas) o bien anoacutenimamente (incorporadas dentro de las reglas sintaacutecti-cas) sin embargo no se aplica cuando la secuencia acoplada se corresponde con una expresioacuten regular es-pecificada como SKIP (noacutetese que por el contrario una especificacioacuten SKIP siacute puede tener asociada una accioacuten lexicograacutefica propia)

El coacutedigo del meacutetodo CommonTokenAction se traslada como un componente maacutes del bloque de decla-raciones compartidas (TOKEN_MGR_DECLS) al principio de la clase del analizador lexicograacutefico

∆ Ejemplo 7

Se pretende ahora ampliar el ejemplo de presentacioacuten con la obtencioacuten de una relacioacuten numerada de los lexemas de las piezas sintaacutecticas encontradas en la entrada analizada en el caso de las variables y de las constantes antes del lexema se mostraraacute un indicativo (Var Const) por ejemplo si la expresioacuten de la entrada es

( a + 987 ) b

se grabaraacute la salida

( 1Var a 2+ 3Const 987 4) 5 6Var b 7 8

En la solucioacuten que se propone a continuacioacuten se muestran la definicioacuten de las opciones y las reglas sin-taacutecticas y lexicograacuteficas (en el resto no hay modificaciones) puede resaltarse lo siguiente- las llamadas al meacutetodo CommonTokenAction se incorporan automaacuteticamente no hay que incluir-las en la especificacioacuten como paraacutemetro de llamada a este meacutetodo siempre se emplea el objeto que re -presenta la pieza que se acaba de detectar- la accioacuten lexicograacutefica comuacuten realiza la actualizacioacuten del contador de piezas y la grabacioacuten del lexe-ma de la pieza detectada con su nuacutemero de orden- la accioacuten lexicograacutefica comuacuten tambieacuten se aplica a las piezas sintaacutecticas anoacutenimas (pareacutentesis y ope-radores) y a la pieza especial que representa el final del fichero (EOF)

- las piezas sintaacutecticas ltconstantegt y ltvariablegt tienen asociada una accioacuten lexicograacutefica propia que se ejecuta antes de la accioacuten lexicograacutefica comuacuten- los caracteres que se saltan (SKIP) no estaacuten afectados por la accioacuten lexicograacutefica comuacuten

options Ignore_Case = true Common_Token_Action = true

31

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarIndicativo(String indicativo) Systemoutprint(indicativo + )

static void grabarLexema (int num String lexema) Systemoutprint(lexema + + num + n)

static void CommonTokenAction(Token pieza) numero++ grabarLexema(numero piezaimage)

TOKEN lt variable [a-z] gt

grabarIndicativo(Var)

32

TOKEN lt constante ( [0-9] ) + gt

grabarIndicativo(Const)

SKIP | n | t | r

A modo de ejemplo de aplicacioacuten de la accioacuten lexicograacutefica comuacuten en lo que sigue se ofrece una solucioacuten para la grabacioacuten (en el fichero predefinido de salida) del texto analizado con las liacuteneas numeradas

∆ Ejemplo 8 [ Listado del texto analizado ]Se desea completar el ejemplo de presentacioacuten con la grabacioacuten de un listado con las liacuteneas numeradas del texto analizado (noacutetese que una expresioacuten puede escribirse ocupando varias liacuteneas) No se muestra la especificacioacuten sintaacutectica debido a que no hay ninguna modificacioacuten en ella Los aspectos resentildeables de la solucioacuten que se propone son- se usa la variable numLin como contador de liacuteneas

- en la variable linea se van yuxtaponiendo los lexemas de las piezas sintaacutecticas de una liacutenea esta yuxtaposicioacuten se realiza mediante la accioacuten lexicograacutefica comuacuten- las secuencias reconocidas con los patrones especificados como SKIP no implican la ejecucioacuten de la accioacuten lexicograacutefica comuacuten por ello su lexema (almacenado en la variable image) ha de yuxtaponer-se expliacutecitamente- hay que separar el patroacuten correspondiente al fin de liacutenea para detectar el momento en que ha de gra-barse la liacutenea completa cuyo final se ha alcanzado

options Ignore_Case = true Common_Token_Action = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(nnAnalisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

TOKEN_MGR_DECLS static int numLin = 0 static String linea =

33

static void CommonTokenAction(Token pieza) linea = linea + piezaimage

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP lt | t | r gt linea = linea + image

SKIP n numLin++ Systemoutprintln(numLin + + linea) linea =

A modo de recapitulacioacutenↄ Accioacuten lexicograacutefica Accioacuten sintaacutectica

bull accioacuten lexicograacutefica bloque de coacutedigo Java asociado a una pieza sintaacutectica nominal (TOKEN) o a una secuencia de caracteres (SKIP) se ejecuta cuando se detecta en la entrada la pieza o la secuencia corres-pondiente no se puede aplicar a piezas sintaacutecticas anoacutenimas (autodefinidas con comillas)bull accioacuten sintaacutectica bloque de coacutedigo Java asociado a un punto de la estructura sintaacutectica (el punto indica-do por el sitio de la produccioacuten donde se inserta el bloque) se ejecuta cuando el anaacutelisis de la entrada pa-sa por ese punto de la estructura

ↄ Valor de una pieza sintaacutectica comunicadaEl analizador lexicograacutefico comunica al analizador sintaacutectico una pieza a traveacutes de un valor de la clase Token (fichero Tokenjava) un objeto de la clase Token representa las caracteriacutesticas de la pieza comunicada En la clase Token entre otros se tienen los campos

String imagelexema de la pieza

int beginLine beginColumn endLine endColumnposicioacuten del lexema en el fichero analizado (indicada por los nuacutemeros de fila y de columna de su comienzo y de su terminacioacuten)

ↄ Valor asociado a un siacutembolo

34

En las producciones que especifican la sintaxis se pueden incluir asignaciones que sirven para dejar ano-tado el valor asociado a un siacutembolo

bull siacutembolo no terminal valor = nombreSimbolo()el meacutetodo de anaacutelisis asociado a nombreSimbolo se ha declarado de un cierto tipo (distinto del tipo void) valor ha de ser una variable del mismo tipo que el meacutetodo

bull siacutembolo terminal dato = lt nombrePieza gtel valor asignado es el valor de la clase Token representativo de la pieza sintaacutectica (nominal) dato ha de ser una variable de tipo Token

ↄ Miradas sobre la pieza sintaacutectica comunicadaLa pieza sintaacutectica comunicada puede mirarse desde cada uno de los dos analizadores

bull analizador sintaacutecticola pieza comunicada (desde el analizador lexicograacutefico) se tiene anotada en un campo de la clase del analizador sintaacutectico declarado como

(static) public Token token

este valor es accesible desde el coacutedigo de todos los meacutetodos de anaacutelisis sintaacutectico el contenido del campo variacutea cuando se recibe una nueva pieza sintaacutectica

bull analizador lexicograacuteficola pieza dispuesta para ser comunicada (al analizador sintaacutectico) se tiene anotada en una variable local declarada como

Token matchedToken

este valor es accesible para el coacutedigo de todas las acciones lexicograacuteficas

ↄ El lexema de la pieza comunicadaEl lexema de la pieza sintaacutectica comunicada se encuentra disponible en dos sitios distintos que compar-ten el mismo nombre pero que son de distinto tipo y se usan de distinta manera

bull String image

es un campo de la clase TOKEN puede consultarse en relacioacuten con un objeto de esa clase que esteacute disponible

bull StringBuffer image

es un campo de la clase del analizador lexicograacutefico ( ∙ ∙ ∙ ∙ TokenManagerjava) es accesible desde el coacutedigo de la clase y por lo tanto desde todas las acciones lexicograacuteficas (bloques de coacute-digo que estaacuten incorporados al analizador lexicograacutefico)

se trata de un valor disponible tanto para las piezas sintaacutecticas nominales (TOKEN) como para las secuencias que se saltan (SKIP)

ↄ Resumen relativo a la lexicografiacutea

TOKEN pieza sintaacutectica nominal∙ ∙ ∙ pieza sintaacutectica anoacutenimaSKIP secuencia de caracteres que no forman pieza

35

Precisiones sobre el analizador sintaacutectico generadoPuede decirse que en su funcionamiento maacutes sencillo cuando no se emplean las posibilidades de examen por adelantado (ldquolookaheadrdquo) JavaCC comprueba que las reglas sintaacutecticas cumplen la condicioacuten LL(1) y generan un analizador descendente-predictivo-recursivo Pero esta apreciacioacuten hay que matizarla ya que el analizador sintaacutectico generado no se ajusta en sentido estricto al modelo de analizador LL(1)

En el modelo de anaacutelisis LL(1) el orden del examen de las distintas alternativas de expansioacuten de un siacutembo-lo no terminal es indiferente ya que se conocen los siacutembolos directores de cada una de ellas (que resultan ser conjuntos disjuntos) en la implementacioacuten del subprograma de anaacutelisis asociado al siacutembolo no terminal ltNombreSimbgt cuyas producciones son

ltNombreSimbgt = α1

| α2

∙ ∙ ∙

| αn

se podriacutea preguntar en cualquier orden si la pieza actual pertenece al conjunto de siacutembolos directores de las distintas reglas Sin embargo en el analizador sintaacutectico generado por JavaCC la implementacioacuten del meacute-todo asociado al siacutembolo no terminal ltNombreSimbgt no sigue esa pauta

Si las anteriores reglas sintaacutecticas se transcriben en JavaCC de la forma

void nombreSimb () α1-jcc

| α2-jcc

∙ ∙ ∙

| αn-jcc

1

accioacutenlexicograacutefica

comuacuten

Commom -TokenAction2

accioacutenlexicograacutefica

propia

(accioacuten ligadaa una pieza)3

bloque dedeclaracioneslexicograacuteficas

TOKEN_MGR_DECLS4

declaracioneslexicograacuteficaspredefinidas

(disponibilidad

de uso)TOKENsisisisi∙ ∙ ∙sino--SKIPnosisisi

Las indicaciones si de las columnas 3 y 4 son consecuencia de las indicaciones de la columna 2

La accioacuten lexicograacutefica comuacuten se ejecuta despueacutes de la accioacuten lexicograacutefica propia

36

el meacutetodo de anaacutelisis generado procede de la siguiente manera

se van considerando las alternativas sucesivamente en el mismo orden en el que estaacuten especifica-das primero la de α1 (α1-jcc) despueacutes la de α2 (α2-jcc) etc

para cada alternativa seleccionada se comprueba si la pieza por adelantado coincide con alguno de los siacutembolos iniciales de la parte derecha y si hay coincidencia se prosigue el anaacutelisis con ella si no hay coincidencia se pasa a la alternativa siguiente

siguiendo con este orden si se llega al final de las alternativas sin haberse encontrado coincidencia alguna se produce un error sintaacutectico

En el analizador generado por JavaCC hay una peculiaridad que no se tiene en los analizadores LL(1) es-trictamente considerados la palabra vaciacutea siacute se considera como posible siacutembolo inicial de la parte derecha una regla (esto ocurre siempre que la parte derecha es anulable) De esta peculiaridad se deriva una conse-cuencia que ha de tenerse en cuenta al escribir la especificacioacuten dado que siempre es posible considerar la palabra vaciacutea como la siguiente pieza por adelantado si se llega a examinar la alternativa correspondiente a una produccioacuten anulable el analizador generado siempre seleccionaraacute esa alternativa con independencia de la siguiente pieza que esteacute presente en la entrada que queda por analizar

Asiacute pues si alguna de las producciones que definen un siacutembolo no terminal es anulable es importante el or-den en que se escriben las alternativas en una especificacioacuten sintaacutectica JavaCC el funcionamiento del ana-lizador generado siacute depende de la colocacioacuten elegida para las reglas

Para ilustrar estas peculiaridades del analizador sintaacutectico generado por JavaCC a continuacioacuten se expone un ejemplo

∆ Ejemplo 9

Se quiere obtener un analizador leacutexico-sintaacutectico de textos que se ajustan al siguiente formato una se-cuencia de uno o maacutes nombres seguida del siacutembolo igual seguido de un nuacutemero entero si la secuencia tiene maacutes de un nombre han de estar separados bien por una coma en todos los casos o bien por el siacutem-bolo dos puntos en todos los casos esto es son correctas las secuencias

uno dos tres = 123exclusivo = 0Abajo Arriba = 57

y son incorrectas las secuenciasuno dos tres = 123

exclusivo inclusive = 0

fuera dentro = 56

Una gramaacutetica que define las secuencias con este formato es

ltListagt = nombre ltOtrosNombresgt = numero

ltOtrosNombresgt = nombre nombre | nombre nombre | ε

se trata de una gramaacutetica que cumple la condicioacuten LL(1)- el siacutembolo director para la alternativa vaciacutea es el siacutembolo igual es evidente pues que las tres reglas de ltOtrosNombresgt tienen siacutembolos directores distintos

37

- la decisioacuten de abandonar la repeticioacuten del anaacutelisis de un nuevo nombre (en las dos primeras alterna-tivas de ltOtrosNombresgt) se puede tomar ante la presencia del siacutembolo igual

En la implementacioacuten de un analizador sintaacutectico LL(1) en sentido estricto la seleccioacuten de la alternativa vaciacutea para expandir el siacutembolo ltOtrosNombresgt se hariacutea tras comprobar expliacutecitamente que la pie-za sintaacutectica por adelantado es el siacutembolo igual

El funcionamiento del analizador generado por JavaCC depende del orden de colocacioacuten de las reglas A continuacioacuten se exponen varias posibilidades de especificacioacuten y se comenta el analizador generado en cada caso

bull Especificacioacuten primeraSe transcriben literalmente las reglas tal y como se han escrito antes en la notacioacuten BNF noacutetese que para poder codificar la parte derecha de la regla vaciacutea ha de incluirse una accioacuten sintaacutectica vaciacutea

PARSER_BEGIN (Igualdad)

public class Igualdad

public static void main (String[] argum) throws ParseException Igualdad analisis = new Igualdad(Systemin) analisissecuencia() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (Igualdad)

void secuencia() lista() ltEOFgt

void lista() lt nombre gt otrosNombres() = lt numero gt

void otrosNombres() lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt ) |

TOKEN lt nombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN lt numero ( [ 0 - 9 ] )+ gt

SKIP | r | n

38

El analizador generado funciona correctamente puesto que en el meacutetodo otrosNombres se selec-ciona la alternativa vaciacutea despueacutes de haber comprobado que la pieza por adelantado no es siacutembolo ini-cial de ninguna de las dos alternativas precedentes (no es una coma ni un siacutembolo dos puntos)

bull Especificacioacuten segundaSe considera la misma especificacioacuten anterior pero cambiando el orden de colocacioacuten de las reglas ahora la produccioacuten vaciacutea se coloca como primera alternativa (soacutelo se muestran las partes modifica-das)

void otrosNombres() | lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt )

Con la entrada de esta especificacioacuten el generador JavaCC produce un mensaje en el que se avisa de que las dos alternativas que estaacuten colocadas detraacutes de la alternativa vaciacutea nunca podraacuten seleccionarse aun asiacute se genera un analizador Pero es un analizador que funciona incorrectamente asiacute por ejemplo la entrada

mxa nyz = 57

se considera incorrecta despueacutes de visto el primer nombre se intenta aplicar la produccioacuten vaciacutea (se espera encontrar el siacutembolo igual) pero habriacutea que aplicar la regla cuyo siacutembolo director es la coma

bull Especificacioacuten terceraSe utilizan las posibilidades de la notacioacuten BNF-Ampliada para prescindir de la especificacioacuten expliacuteci-ta de la palabra vaciacutea se aprovecha que JavaCC admite el operador + para indicar repeticiones de una o maacutes veces la alternativa vaciacutea estaacute impliacutecitamente contemplada en la posibilidad de cero repeti-ciones que contempla el operador (soacutelo se muestran las partes modificadas en la especificacioacuten)

void otrosNombres() ( lt nombre gt )+ | ( lt nombre gt )

El meacutetodo otrosNombres() generado hace las comprobaciones en el siguiente orden

- se comprueba si la pieza por adelantado es una coma (siacutembolo inicial de la primera alternativa) y en este caso se selecciona la primera regla- si no es una coma se comprueba si es un siacutembolo dos puntos (siacutembolo inicial de la segunda alter-nativa) y en este caso se selecciona la segunda regla- si no es un siacutembolo dos puntos se considera la palabra vaciacutea como siacutembolo inicial (siempre posi-ble impliacutecitamente) para la segunda alternativa en este caso para que no haya error la pieza por adelantado ha de ser un siacutembolo igual

Se trata pues de un analizador que funciona correctamente

bull Especificacioacuten cuarta

39

Se pone la misma especificacioacuten anterior pero cambiando el orden de consideracioacuten de la regla vaciacutea ahora se incluye impliacutecitamente en la primera alternativa (soacutelo se muestran las partes modificadas)

void otrosNombres() ( lt nombre gt ) | ( lt nombre gt )+

Para esta especificacioacuten el generador JavaCC emite un aviso en el que se indica que la segunda alter -nativa nunca se seleccionaraacute no obstante se genera un analizador que no funciona como se supone que deberiacutea hacerlo por ejemplo el anaacutelisis de la entrada

nyz mxa = 1

produce un error despueacutes de tratado el primer nombre se intenta aplicar la produccioacuten vaciacutea (corres-pondiente a la repeticioacuten de cero veces contemplada en la primera regla) por lo que no se encuentra el esperado siacutembolo igual

40

Page 30: JavaCC

expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n StringBuffer lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

TOKEN lt variable [a-z] gt

int nLineaVar nColumnaVar numero++ nLineaVar = matchedTokenbeginLine nColumnaVar = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaVar nColumnaVar)

TOKEN lt constante ( [0-9] ) + gt

int nLineaCte nColumnaCte

29

numero++ nLineaCte = matchedTokenbeginLine nColumnaCte = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaCte nColumnaCte)

SKIP | n | t | r

La especificacioacuten precedente se puede escribir de otra manera maacutes escueta como se indica a continua-cioacuten (soacutelo se muestran las modificaciones) sobre esta nueva especificacioacuten puede mencionarse- se han eliminado por innecesarias las variables declaradas dentro de las acciones lexicograacuteficas- el nombre image que aparece en el bloque de las declaraciones compartidas se refiere al campo de la clase Token noacutetese que en efecto se aplica a un objeto de esa clase y se comunica como un paraacute -metro de tipo String

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

TOKEN lt variable [a-z] gt

numero++ grabarDatosPieza(numero matchedToken)

TOKEN lt constante ( [0-9] ) + gt

numero++ grabarDatosPieza(numero matchedToken)

loz Accioacuten lexicograacutefica comuacutenSi hubiera que realizar la misma tarea tras la deteccioacuten de todas y cada una de las piezas sintaacutecticas una posibilidad incoacutemoda seriacutea repetir el mismo coacutedigo en todas las acciones lexicograacuteficas para evitar tanta reiteracioacuten se tiene la opcioacuten Common_Token_Action su valor por defecto es false pero si se le asigna el valor true es posible definir una ldquoaccioacuten lexicograacutefica comuacutenrdquo que se escribe una uacutenica vez y se aplica a todas las piezas sintaacutecticas

30

Cuando la opcioacuten Common_Token_Action se establece con el valor true es obligado incluir el blo-que de declaraciones lexicograacuteficas compartidas (TOKEN_MGR_DECLS) Ademaacutes en ese bloque se preci-sa la definicioacuten de un meacutetodo cuyo encabezamiento sea

[ static ] void CommonTokenAction(Token t)

el coacutedigo de este meacutetodo constituye la accioacuten lexicograacutefica comuacuten que se ejecutaraacute despueacutes de la deteccioacuten de cualquier pieza sintaacutectica el meacutetodo tiene un paraacutemetro de tipo Token mediante el que se puede co-municar la informacioacuten correspondiente a la pieza que se acaba de encontrar en la entrada analizada

Si una pieza sintaacutectica tiene asociada una accioacuten lexicograacutefica propia la accioacuten lexicograacutefica comuacuten (si es-taacute definida) se ejecutaraacute inmediatamente despueacutes de realizar la accioacuten propia

La accioacuten lexicograacutefica comuacuten se aplica a todas las piezas sintaacutecticas definidas como TOKEN bien sea no-minalmente (en las reglas lexicograacuteficas) o bien anoacutenimamente (incorporadas dentro de las reglas sintaacutecti-cas) sin embargo no se aplica cuando la secuencia acoplada se corresponde con una expresioacuten regular es-pecificada como SKIP (noacutetese que por el contrario una especificacioacuten SKIP siacute puede tener asociada una accioacuten lexicograacutefica propia)

El coacutedigo del meacutetodo CommonTokenAction se traslada como un componente maacutes del bloque de decla-raciones compartidas (TOKEN_MGR_DECLS) al principio de la clase del analizador lexicograacutefico

∆ Ejemplo 7

Se pretende ahora ampliar el ejemplo de presentacioacuten con la obtencioacuten de una relacioacuten numerada de los lexemas de las piezas sintaacutecticas encontradas en la entrada analizada en el caso de las variables y de las constantes antes del lexema se mostraraacute un indicativo (Var Const) por ejemplo si la expresioacuten de la entrada es

( a + 987 ) b

se grabaraacute la salida

( 1Var a 2+ 3Const 987 4) 5 6Var b 7 8

En la solucioacuten que se propone a continuacioacuten se muestran la definicioacuten de las opciones y las reglas sin-taacutecticas y lexicograacuteficas (en el resto no hay modificaciones) puede resaltarse lo siguiente- las llamadas al meacutetodo CommonTokenAction se incorporan automaacuteticamente no hay que incluir-las en la especificacioacuten como paraacutemetro de llamada a este meacutetodo siempre se emplea el objeto que re -presenta la pieza que se acaba de detectar- la accioacuten lexicograacutefica comuacuten realiza la actualizacioacuten del contador de piezas y la grabacioacuten del lexe-ma de la pieza detectada con su nuacutemero de orden- la accioacuten lexicograacutefica comuacuten tambieacuten se aplica a las piezas sintaacutecticas anoacutenimas (pareacutentesis y ope-radores) y a la pieza especial que representa el final del fichero (EOF)

- las piezas sintaacutecticas ltconstantegt y ltvariablegt tienen asociada una accioacuten lexicograacutefica propia que se ejecuta antes de la accioacuten lexicograacutefica comuacuten- los caracteres que se saltan (SKIP) no estaacuten afectados por la accioacuten lexicograacutefica comuacuten

options Ignore_Case = true Common_Token_Action = true

31

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarIndicativo(String indicativo) Systemoutprint(indicativo + )

static void grabarLexema (int num String lexema) Systemoutprint(lexema + + num + n)

static void CommonTokenAction(Token pieza) numero++ grabarLexema(numero piezaimage)

TOKEN lt variable [a-z] gt

grabarIndicativo(Var)

32

TOKEN lt constante ( [0-9] ) + gt

grabarIndicativo(Const)

SKIP | n | t | r

A modo de ejemplo de aplicacioacuten de la accioacuten lexicograacutefica comuacuten en lo que sigue se ofrece una solucioacuten para la grabacioacuten (en el fichero predefinido de salida) del texto analizado con las liacuteneas numeradas

∆ Ejemplo 8 [ Listado del texto analizado ]Se desea completar el ejemplo de presentacioacuten con la grabacioacuten de un listado con las liacuteneas numeradas del texto analizado (noacutetese que una expresioacuten puede escribirse ocupando varias liacuteneas) No se muestra la especificacioacuten sintaacutectica debido a que no hay ninguna modificacioacuten en ella Los aspectos resentildeables de la solucioacuten que se propone son- se usa la variable numLin como contador de liacuteneas

- en la variable linea se van yuxtaponiendo los lexemas de las piezas sintaacutecticas de una liacutenea esta yuxtaposicioacuten se realiza mediante la accioacuten lexicograacutefica comuacuten- las secuencias reconocidas con los patrones especificados como SKIP no implican la ejecucioacuten de la accioacuten lexicograacutefica comuacuten por ello su lexema (almacenado en la variable image) ha de yuxtaponer-se expliacutecitamente- hay que separar el patroacuten correspondiente al fin de liacutenea para detectar el momento en que ha de gra-barse la liacutenea completa cuyo final se ha alcanzado

options Ignore_Case = true Common_Token_Action = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(nnAnalisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

TOKEN_MGR_DECLS static int numLin = 0 static String linea =

33

static void CommonTokenAction(Token pieza) linea = linea + piezaimage

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP lt | t | r gt linea = linea + image

SKIP n numLin++ Systemoutprintln(numLin + + linea) linea =

A modo de recapitulacioacutenↄ Accioacuten lexicograacutefica Accioacuten sintaacutectica

bull accioacuten lexicograacutefica bloque de coacutedigo Java asociado a una pieza sintaacutectica nominal (TOKEN) o a una secuencia de caracteres (SKIP) se ejecuta cuando se detecta en la entrada la pieza o la secuencia corres-pondiente no se puede aplicar a piezas sintaacutecticas anoacutenimas (autodefinidas con comillas)bull accioacuten sintaacutectica bloque de coacutedigo Java asociado a un punto de la estructura sintaacutectica (el punto indica-do por el sitio de la produccioacuten donde se inserta el bloque) se ejecuta cuando el anaacutelisis de la entrada pa-sa por ese punto de la estructura

ↄ Valor de una pieza sintaacutectica comunicadaEl analizador lexicograacutefico comunica al analizador sintaacutectico una pieza a traveacutes de un valor de la clase Token (fichero Tokenjava) un objeto de la clase Token representa las caracteriacutesticas de la pieza comunicada En la clase Token entre otros se tienen los campos

String imagelexema de la pieza

int beginLine beginColumn endLine endColumnposicioacuten del lexema en el fichero analizado (indicada por los nuacutemeros de fila y de columna de su comienzo y de su terminacioacuten)

ↄ Valor asociado a un siacutembolo

34

En las producciones que especifican la sintaxis se pueden incluir asignaciones que sirven para dejar ano-tado el valor asociado a un siacutembolo

bull siacutembolo no terminal valor = nombreSimbolo()el meacutetodo de anaacutelisis asociado a nombreSimbolo se ha declarado de un cierto tipo (distinto del tipo void) valor ha de ser una variable del mismo tipo que el meacutetodo

bull siacutembolo terminal dato = lt nombrePieza gtel valor asignado es el valor de la clase Token representativo de la pieza sintaacutectica (nominal) dato ha de ser una variable de tipo Token

ↄ Miradas sobre la pieza sintaacutectica comunicadaLa pieza sintaacutectica comunicada puede mirarse desde cada uno de los dos analizadores

bull analizador sintaacutecticola pieza comunicada (desde el analizador lexicograacutefico) se tiene anotada en un campo de la clase del analizador sintaacutectico declarado como

(static) public Token token

este valor es accesible desde el coacutedigo de todos los meacutetodos de anaacutelisis sintaacutectico el contenido del campo variacutea cuando se recibe una nueva pieza sintaacutectica

bull analizador lexicograacuteficola pieza dispuesta para ser comunicada (al analizador sintaacutectico) se tiene anotada en una variable local declarada como

Token matchedToken

este valor es accesible para el coacutedigo de todas las acciones lexicograacuteficas

ↄ El lexema de la pieza comunicadaEl lexema de la pieza sintaacutectica comunicada se encuentra disponible en dos sitios distintos que compar-ten el mismo nombre pero que son de distinto tipo y se usan de distinta manera

bull String image

es un campo de la clase TOKEN puede consultarse en relacioacuten con un objeto de esa clase que esteacute disponible

bull StringBuffer image

es un campo de la clase del analizador lexicograacutefico ( ∙ ∙ ∙ ∙ TokenManagerjava) es accesible desde el coacutedigo de la clase y por lo tanto desde todas las acciones lexicograacuteficas (bloques de coacute-digo que estaacuten incorporados al analizador lexicograacutefico)

se trata de un valor disponible tanto para las piezas sintaacutecticas nominales (TOKEN) como para las secuencias que se saltan (SKIP)

ↄ Resumen relativo a la lexicografiacutea

TOKEN pieza sintaacutectica nominal∙ ∙ ∙ pieza sintaacutectica anoacutenimaSKIP secuencia de caracteres que no forman pieza

35

Precisiones sobre el analizador sintaacutectico generadoPuede decirse que en su funcionamiento maacutes sencillo cuando no se emplean las posibilidades de examen por adelantado (ldquolookaheadrdquo) JavaCC comprueba que las reglas sintaacutecticas cumplen la condicioacuten LL(1) y generan un analizador descendente-predictivo-recursivo Pero esta apreciacioacuten hay que matizarla ya que el analizador sintaacutectico generado no se ajusta en sentido estricto al modelo de analizador LL(1)

En el modelo de anaacutelisis LL(1) el orden del examen de las distintas alternativas de expansioacuten de un siacutembo-lo no terminal es indiferente ya que se conocen los siacutembolos directores de cada una de ellas (que resultan ser conjuntos disjuntos) en la implementacioacuten del subprograma de anaacutelisis asociado al siacutembolo no terminal ltNombreSimbgt cuyas producciones son

ltNombreSimbgt = α1

| α2

∙ ∙ ∙

| αn

se podriacutea preguntar en cualquier orden si la pieza actual pertenece al conjunto de siacutembolos directores de las distintas reglas Sin embargo en el analizador sintaacutectico generado por JavaCC la implementacioacuten del meacute-todo asociado al siacutembolo no terminal ltNombreSimbgt no sigue esa pauta

Si las anteriores reglas sintaacutecticas se transcriben en JavaCC de la forma

void nombreSimb () α1-jcc

| α2-jcc

∙ ∙ ∙

| αn-jcc

1

accioacutenlexicograacutefica

comuacuten

Commom -TokenAction2

accioacutenlexicograacutefica

propia

(accioacuten ligadaa una pieza)3

bloque dedeclaracioneslexicograacuteficas

TOKEN_MGR_DECLS4

declaracioneslexicograacuteficaspredefinidas

(disponibilidad

de uso)TOKENsisisisi∙ ∙ ∙sino--SKIPnosisisi

Las indicaciones si de las columnas 3 y 4 son consecuencia de las indicaciones de la columna 2

La accioacuten lexicograacutefica comuacuten se ejecuta despueacutes de la accioacuten lexicograacutefica propia

36

el meacutetodo de anaacutelisis generado procede de la siguiente manera

se van considerando las alternativas sucesivamente en el mismo orden en el que estaacuten especifica-das primero la de α1 (α1-jcc) despueacutes la de α2 (α2-jcc) etc

para cada alternativa seleccionada se comprueba si la pieza por adelantado coincide con alguno de los siacutembolos iniciales de la parte derecha y si hay coincidencia se prosigue el anaacutelisis con ella si no hay coincidencia se pasa a la alternativa siguiente

siguiendo con este orden si se llega al final de las alternativas sin haberse encontrado coincidencia alguna se produce un error sintaacutectico

En el analizador generado por JavaCC hay una peculiaridad que no se tiene en los analizadores LL(1) es-trictamente considerados la palabra vaciacutea siacute se considera como posible siacutembolo inicial de la parte derecha una regla (esto ocurre siempre que la parte derecha es anulable) De esta peculiaridad se deriva una conse-cuencia que ha de tenerse en cuenta al escribir la especificacioacuten dado que siempre es posible considerar la palabra vaciacutea como la siguiente pieza por adelantado si se llega a examinar la alternativa correspondiente a una produccioacuten anulable el analizador generado siempre seleccionaraacute esa alternativa con independencia de la siguiente pieza que esteacute presente en la entrada que queda por analizar

Asiacute pues si alguna de las producciones que definen un siacutembolo no terminal es anulable es importante el or-den en que se escriben las alternativas en una especificacioacuten sintaacutectica JavaCC el funcionamiento del ana-lizador generado siacute depende de la colocacioacuten elegida para las reglas

Para ilustrar estas peculiaridades del analizador sintaacutectico generado por JavaCC a continuacioacuten se expone un ejemplo

∆ Ejemplo 9

Se quiere obtener un analizador leacutexico-sintaacutectico de textos que se ajustan al siguiente formato una se-cuencia de uno o maacutes nombres seguida del siacutembolo igual seguido de un nuacutemero entero si la secuencia tiene maacutes de un nombre han de estar separados bien por una coma en todos los casos o bien por el siacutem-bolo dos puntos en todos los casos esto es son correctas las secuencias

uno dos tres = 123exclusivo = 0Abajo Arriba = 57

y son incorrectas las secuenciasuno dos tres = 123

exclusivo inclusive = 0

fuera dentro = 56

Una gramaacutetica que define las secuencias con este formato es

ltListagt = nombre ltOtrosNombresgt = numero

ltOtrosNombresgt = nombre nombre | nombre nombre | ε

se trata de una gramaacutetica que cumple la condicioacuten LL(1)- el siacutembolo director para la alternativa vaciacutea es el siacutembolo igual es evidente pues que las tres reglas de ltOtrosNombresgt tienen siacutembolos directores distintos

37

- la decisioacuten de abandonar la repeticioacuten del anaacutelisis de un nuevo nombre (en las dos primeras alterna-tivas de ltOtrosNombresgt) se puede tomar ante la presencia del siacutembolo igual

En la implementacioacuten de un analizador sintaacutectico LL(1) en sentido estricto la seleccioacuten de la alternativa vaciacutea para expandir el siacutembolo ltOtrosNombresgt se hariacutea tras comprobar expliacutecitamente que la pie-za sintaacutectica por adelantado es el siacutembolo igual

El funcionamiento del analizador generado por JavaCC depende del orden de colocacioacuten de las reglas A continuacioacuten se exponen varias posibilidades de especificacioacuten y se comenta el analizador generado en cada caso

bull Especificacioacuten primeraSe transcriben literalmente las reglas tal y como se han escrito antes en la notacioacuten BNF noacutetese que para poder codificar la parte derecha de la regla vaciacutea ha de incluirse una accioacuten sintaacutectica vaciacutea

PARSER_BEGIN (Igualdad)

public class Igualdad

public static void main (String[] argum) throws ParseException Igualdad analisis = new Igualdad(Systemin) analisissecuencia() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (Igualdad)

void secuencia() lista() ltEOFgt

void lista() lt nombre gt otrosNombres() = lt numero gt

void otrosNombres() lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt ) |

TOKEN lt nombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN lt numero ( [ 0 - 9 ] )+ gt

SKIP | r | n

38

El analizador generado funciona correctamente puesto que en el meacutetodo otrosNombres se selec-ciona la alternativa vaciacutea despueacutes de haber comprobado que la pieza por adelantado no es siacutembolo ini-cial de ninguna de las dos alternativas precedentes (no es una coma ni un siacutembolo dos puntos)

bull Especificacioacuten segundaSe considera la misma especificacioacuten anterior pero cambiando el orden de colocacioacuten de las reglas ahora la produccioacuten vaciacutea se coloca como primera alternativa (soacutelo se muestran las partes modifica-das)

void otrosNombres() | lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt )

Con la entrada de esta especificacioacuten el generador JavaCC produce un mensaje en el que se avisa de que las dos alternativas que estaacuten colocadas detraacutes de la alternativa vaciacutea nunca podraacuten seleccionarse aun asiacute se genera un analizador Pero es un analizador que funciona incorrectamente asiacute por ejemplo la entrada

mxa nyz = 57

se considera incorrecta despueacutes de visto el primer nombre se intenta aplicar la produccioacuten vaciacutea (se espera encontrar el siacutembolo igual) pero habriacutea que aplicar la regla cuyo siacutembolo director es la coma

bull Especificacioacuten terceraSe utilizan las posibilidades de la notacioacuten BNF-Ampliada para prescindir de la especificacioacuten expliacuteci-ta de la palabra vaciacutea se aprovecha que JavaCC admite el operador + para indicar repeticiones de una o maacutes veces la alternativa vaciacutea estaacute impliacutecitamente contemplada en la posibilidad de cero repeti-ciones que contempla el operador (soacutelo se muestran las partes modificadas en la especificacioacuten)

void otrosNombres() ( lt nombre gt )+ | ( lt nombre gt )

El meacutetodo otrosNombres() generado hace las comprobaciones en el siguiente orden

- se comprueba si la pieza por adelantado es una coma (siacutembolo inicial de la primera alternativa) y en este caso se selecciona la primera regla- si no es una coma se comprueba si es un siacutembolo dos puntos (siacutembolo inicial de la segunda alter-nativa) y en este caso se selecciona la segunda regla- si no es un siacutembolo dos puntos se considera la palabra vaciacutea como siacutembolo inicial (siempre posi-ble impliacutecitamente) para la segunda alternativa en este caso para que no haya error la pieza por adelantado ha de ser un siacutembolo igual

Se trata pues de un analizador que funciona correctamente

bull Especificacioacuten cuarta

39

Se pone la misma especificacioacuten anterior pero cambiando el orden de consideracioacuten de la regla vaciacutea ahora se incluye impliacutecitamente en la primera alternativa (soacutelo se muestran las partes modificadas)

void otrosNombres() ( lt nombre gt ) | ( lt nombre gt )+

Para esta especificacioacuten el generador JavaCC emite un aviso en el que se indica que la segunda alter -nativa nunca se seleccionaraacute no obstante se genera un analizador que no funciona como se supone que deberiacutea hacerlo por ejemplo el anaacutelisis de la entrada

nyz mxa = 1

produce un error despueacutes de tratado el primer nombre se intenta aplicar la produccioacuten vaciacutea (corres-pondiente a la repeticioacuten de cero veces contemplada en la primera regla) por lo que no se encuentra el esperado siacutembolo igual

40

Page 31: JavaCC

numero++ nLineaCte = matchedTokenbeginLine nColumnaCte = matchedTokenbeginColumn grabarLexema(numero image) grabarPosicion(nLineaCte nColumnaCte)

SKIP | n | t | r

La especificacioacuten precedente se puede escribir de otra manera maacutes escueta como se indica a continua-cioacuten (soacutelo se muestran las modificaciones) sobre esta nueva especificacioacuten puede mencionarse- se han eliminado por innecesarias las variables declaradas dentro de las acciones lexicograacuteficas- el nombre image que aparece en el bloque de las declaraciones compartidas se refiere al campo de la clase Token noacutetese que en efecto se aplica a un objeto de esa clase y se comunica como un paraacute -metro de tipo String

TOKEN_MGR_DECLS

static int numero = 0

static void grabarLexema (int n String lexema) Systemoutprint(n + - + lexema + )

static void grabarPosicion(int nL int nC) Systemoutprint(linea + nL + ) Systemoutprintln(columna + nC + n)

static void grabarDatosPieza(int n Token pieza) grabarLexema(n piezaimage) grabarPosicion(piezabeginLine piezabeginColumn)

TOKEN lt variable [a-z] gt

numero++ grabarDatosPieza(numero matchedToken)

TOKEN lt constante ( [0-9] ) + gt

numero++ grabarDatosPieza(numero matchedToken)

loz Accioacuten lexicograacutefica comuacutenSi hubiera que realizar la misma tarea tras la deteccioacuten de todas y cada una de las piezas sintaacutecticas una posibilidad incoacutemoda seriacutea repetir el mismo coacutedigo en todas las acciones lexicograacuteficas para evitar tanta reiteracioacuten se tiene la opcioacuten Common_Token_Action su valor por defecto es false pero si se le asigna el valor true es posible definir una ldquoaccioacuten lexicograacutefica comuacutenrdquo que se escribe una uacutenica vez y se aplica a todas las piezas sintaacutecticas

30

Cuando la opcioacuten Common_Token_Action se establece con el valor true es obligado incluir el blo-que de declaraciones lexicograacuteficas compartidas (TOKEN_MGR_DECLS) Ademaacutes en ese bloque se preci-sa la definicioacuten de un meacutetodo cuyo encabezamiento sea

[ static ] void CommonTokenAction(Token t)

el coacutedigo de este meacutetodo constituye la accioacuten lexicograacutefica comuacuten que se ejecutaraacute despueacutes de la deteccioacuten de cualquier pieza sintaacutectica el meacutetodo tiene un paraacutemetro de tipo Token mediante el que se puede co-municar la informacioacuten correspondiente a la pieza que se acaba de encontrar en la entrada analizada

Si una pieza sintaacutectica tiene asociada una accioacuten lexicograacutefica propia la accioacuten lexicograacutefica comuacuten (si es-taacute definida) se ejecutaraacute inmediatamente despueacutes de realizar la accioacuten propia

La accioacuten lexicograacutefica comuacuten se aplica a todas las piezas sintaacutecticas definidas como TOKEN bien sea no-minalmente (en las reglas lexicograacuteficas) o bien anoacutenimamente (incorporadas dentro de las reglas sintaacutecti-cas) sin embargo no se aplica cuando la secuencia acoplada se corresponde con una expresioacuten regular es-pecificada como SKIP (noacutetese que por el contrario una especificacioacuten SKIP siacute puede tener asociada una accioacuten lexicograacutefica propia)

El coacutedigo del meacutetodo CommonTokenAction se traslada como un componente maacutes del bloque de decla-raciones compartidas (TOKEN_MGR_DECLS) al principio de la clase del analizador lexicograacutefico

∆ Ejemplo 7

Se pretende ahora ampliar el ejemplo de presentacioacuten con la obtencioacuten de una relacioacuten numerada de los lexemas de las piezas sintaacutecticas encontradas en la entrada analizada en el caso de las variables y de las constantes antes del lexema se mostraraacute un indicativo (Var Const) por ejemplo si la expresioacuten de la entrada es

( a + 987 ) b

se grabaraacute la salida

( 1Var a 2+ 3Const 987 4) 5 6Var b 7 8

En la solucioacuten que se propone a continuacioacuten se muestran la definicioacuten de las opciones y las reglas sin-taacutecticas y lexicograacuteficas (en el resto no hay modificaciones) puede resaltarse lo siguiente- las llamadas al meacutetodo CommonTokenAction se incorporan automaacuteticamente no hay que incluir-las en la especificacioacuten como paraacutemetro de llamada a este meacutetodo siempre se emplea el objeto que re -presenta la pieza que se acaba de detectar- la accioacuten lexicograacutefica comuacuten realiza la actualizacioacuten del contador de piezas y la grabacioacuten del lexe-ma de la pieza detectada con su nuacutemero de orden- la accioacuten lexicograacutefica comuacuten tambieacuten se aplica a las piezas sintaacutecticas anoacutenimas (pareacutentesis y ope-radores) y a la pieza especial que representa el final del fichero (EOF)

- las piezas sintaacutecticas ltconstantegt y ltvariablegt tienen asociada una accioacuten lexicograacutefica propia que se ejecuta antes de la accioacuten lexicograacutefica comuacuten- los caracteres que se saltan (SKIP) no estaacuten afectados por la accioacuten lexicograacutefica comuacuten

options Ignore_Case = true Common_Token_Action = true

31

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarIndicativo(String indicativo) Systemoutprint(indicativo + )

static void grabarLexema (int num String lexema) Systemoutprint(lexema + + num + n)

static void CommonTokenAction(Token pieza) numero++ grabarLexema(numero piezaimage)

TOKEN lt variable [a-z] gt

grabarIndicativo(Var)

32

TOKEN lt constante ( [0-9] ) + gt

grabarIndicativo(Const)

SKIP | n | t | r

A modo de ejemplo de aplicacioacuten de la accioacuten lexicograacutefica comuacuten en lo que sigue se ofrece una solucioacuten para la grabacioacuten (en el fichero predefinido de salida) del texto analizado con las liacuteneas numeradas

∆ Ejemplo 8 [ Listado del texto analizado ]Se desea completar el ejemplo de presentacioacuten con la grabacioacuten de un listado con las liacuteneas numeradas del texto analizado (noacutetese que una expresioacuten puede escribirse ocupando varias liacuteneas) No se muestra la especificacioacuten sintaacutectica debido a que no hay ninguna modificacioacuten en ella Los aspectos resentildeables de la solucioacuten que se propone son- se usa la variable numLin como contador de liacuteneas

- en la variable linea se van yuxtaponiendo los lexemas de las piezas sintaacutecticas de una liacutenea esta yuxtaposicioacuten se realiza mediante la accioacuten lexicograacutefica comuacuten- las secuencias reconocidas con los patrones especificados como SKIP no implican la ejecucioacuten de la accioacuten lexicograacutefica comuacuten por ello su lexema (almacenado en la variable image) ha de yuxtaponer-se expliacutecitamente- hay que separar el patroacuten correspondiente al fin de liacutenea para detectar el momento en que ha de gra-barse la liacutenea completa cuyo final se ha alcanzado

options Ignore_Case = true Common_Token_Action = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(nnAnalisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

TOKEN_MGR_DECLS static int numLin = 0 static String linea =

33

static void CommonTokenAction(Token pieza) linea = linea + piezaimage

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP lt | t | r gt linea = linea + image

SKIP n numLin++ Systemoutprintln(numLin + + linea) linea =

A modo de recapitulacioacutenↄ Accioacuten lexicograacutefica Accioacuten sintaacutectica

bull accioacuten lexicograacutefica bloque de coacutedigo Java asociado a una pieza sintaacutectica nominal (TOKEN) o a una secuencia de caracteres (SKIP) se ejecuta cuando se detecta en la entrada la pieza o la secuencia corres-pondiente no se puede aplicar a piezas sintaacutecticas anoacutenimas (autodefinidas con comillas)bull accioacuten sintaacutectica bloque de coacutedigo Java asociado a un punto de la estructura sintaacutectica (el punto indica-do por el sitio de la produccioacuten donde se inserta el bloque) se ejecuta cuando el anaacutelisis de la entrada pa-sa por ese punto de la estructura

ↄ Valor de una pieza sintaacutectica comunicadaEl analizador lexicograacutefico comunica al analizador sintaacutectico una pieza a traveacutes de un valor de la clase Token (fichero Tokenjava) un objeto de la clase Token representa las caracteriacutesticas de la pieza comunicada En la clase Token entre otros se tienen los campos

String imagelexema de la pieza

int beginLine beginColumn endLine endColumnposicioacuten del lexema en el fichero analizado (indicada por los nuacutemeros de fila y de columna de su comienzo y de su terminacioacuten)

ↄ Valor asociado a un siacutembolo

34

En las producciones que especifican la sintaxis se pueden incluir asignaciones que sirven para dejar ano-tado el valor asociado a un siacutembolo

bull siacutembolo no terminal valor = nombreSimbolo()el meacutetodo de anaacutelisis asociado a nombreSimbolo se ha declarado de un cierto tipo (distinto del tipo void) valor ha de ser una variable del mismo tipo que el meacutetodo

bull siacutembolo terminal dato = lt nombrePieza gtel valor asignado es el valor de la clase Token representativo de la pieza sintaacutectica (nominal) dato ha de ser una variable de tipo Token

ↄ Miradas sobre la pieza sintaacutectica comunicadaLa pieza sintaacutectica comunicada puede mirarse desde cada uno de los dos analizadores

bull analizador sintaacutecticola pieza comunicada (desde el analizador lexicograacutefico) se tiene anotada en un campo de la clase del analizador sintaacutectico declarado como

(static) public Token token

este valor es accesible desde el coacutedigo de todos los meacutetodos de anaacutelisis sintaacutectico el contenido del campo variacutea cuando se recibe una nueva pieza sintaacutectica

bull analizador lexicograacuteficola pieza dispuesta para ser comunicada (al analizador sintaacutectico) se tiene anotada en una variable local declarada como

Token matchedToken

este valor es accesible para el coacutedigo de todas las acciones lexicograacuteficas

ↄ El lexema de la pieza comunicadaEl lexema de la pieza sintaacutectica comunicada se encuentra disponible en dos sitios distintos que compar-ten el mismo nombre pero que son de distinto tipo y se usan de distinta manera

bull String image

es un campo de la clase TOKEN puede consultarse en relacioacuten con un objeto de esa clase que esteacute disponible

bull StringBuffer image

es un campo de la clase del analizador lexicograacutefico ( ∙ ∙ ∙ ∙ TokenManagerjava) es accesible desde el coacutedigo de la clase y por lo tanto desde todas las acciones lexicograacuteficas (bloques de coacute-digo que estaacuten incorporados al analizador lexicograacutefico)

se trata de un valor disponible tanto para las piezas sintaacutecticas nominales (TOKEN) como para las secuencias que se saltan (SKIP)

ↄ Resumen relativo a la lexicografiacutea

TOKEN pieza sintaacutectica nominal∙ ∙ ∙ pieza sintaacutectica anoacutenimaSKIP secuencia de caracteres que no forman pieza

35

Precisiones sobre el analizador sintaacutectico generadoPuede decirse que en su funcionamiento maacutes sencillo cuando no se emplean las posibilidades de examen por adelantado (ldquolookaheadrdquo) JavaCC comprueba que las reglas sintaacutecticas cumplen la condicioacuten LL(1) y generan un analizador descendente-predictivo-recursivo Pero esta apreciacioacuten hay que matizarla ya que el analizador sintaacutectico generado no se ajusta en sentido estricto al modelo de analizador LL(1)

En el modelo de anaacutelisis LL(1) el orden del examen de las distintas alternativas de expansioacuten de un siacutembo-lo no terminal es indiferente ya que se conocen los siacutembolos directores de cada una de ellas (que resultan ser conjuntos disjuntos) en la implementacioacuten del subprograma de anaacutelisis asociado al siacutembolo no terminal ltNombreSimbgt cuyas producciones son

ltNombreSimbgt = α1

| α2

∙ ∙ ∙

| αn

se podriacutea preguntar en cualquier orden si la pieza actual pertenece al conjunto de siacutembolos directores de las distintas reglas Sin embargo en el analizador sintaacutectico generado por JavaCC la implementacioacuten del meacute-todo asociado al siacutembolo no terminal ltNombreSimbgt no sigue esa pauta

Si las anteriores reglas sintaacutecticas se transcriben en JavaCC de la forma

void nombreSimb () α1-jcc

| α2-jcc

∙ ∙ ∙

| αn-jcc

1

accioacutenlexicograacutefica

comuacuten

Commom -TokenAction2

accioacutenlexicograacutefica

propia

(accioacuten ligadaa una pieza)3

bloque dedeclaracioneslexicograacuteficas

TOKEN_MGR_DECLS4

declaracioneslexicograacuteficaspredefinidas

(disponibilidad

de uso)TOKENsisisisi∙ ∙ ∙sino--SKIPnosisisi

Las indicaciones si de las columnas 3 y 4 son consecuencia de las indicaciones de la columna 2

La accioacuten lexicograacutefica comuacuten se ejecuta despueacutes de la accioacuten lexicograacutefica propia

36

el meacutetodo de anaacutelisis generado procede de la siguiente manera

se van considerando las alternativas sucesivamente en el mismo orden en el que estaacuten especifica-das primero la de α1 (α1-jcc) despueacutes la de α2 (α2-jcc) etc

para cada alternativa seleccionada se comprueba si la pieza por adelantado coincide con alguno de los siacutembolos iniciales de la parte derecha y si hay coincidencia se prosigue el anaacutelisis con ella si no hay coincidencia se pasa a la alternativa siguiente

siguiendo con este orden si se llega al final de las alternativas sin haberse encontrado coincidencia alguna se produce un error sintaacutectico

En el analizador generado por JavaCC hay una peculiaridad que no se tiene en los analizadores LL(1) es-trictamente considerados la palabra vaciacutea siacute se considera como posible siacutembolo inicial de la parte derecha una regla (esto ocurre siempre que la parte derecha es anulable) De esta peculiaridad se deriva una conse-cuencia que ha de tenerse en cuenta al escribir la especificacioacuten dado que siempre es posible considerar la palabra vaciacutea como la siguiente pieza por adelantado si se llega a examinar la alternativa correspondiente a una produccioacuten anulable el analizador generado siempre seleccionaraacute esa alternativa con independencia de la siguiente pieza que esteacute presente en la entrada que queda por analizar

Asiacute pues si alguna de las producciones que definen un siacutembolo no terminal es anulable es importante el or-den en que se escriben las alternativas en una especificacioacuten sintaacutectica JavaCC el funcionamiento del ana-lizador generado siacute depende de la colocacioacuten elegida para las reglas

Para ilustrar estas peculiaridades del analizador sintaacutectico generado por JavaCC a continuacioacuten se expone un ejemplo

∆ Ejemplo 9

Se quiere obtener un analizador leacutexico-sintaacutectico de textos que se ajustan al siguiente formato una se-cuencia de uno o maacutes nombres seguida del siacutembolo igual seguido de un nuacutemero entero si la secuencia tiene maacutes de un nombre han de estar separados bien por una coma en todos los casos o bien por el siacutem-bolo dos puntos en todos los casos esto es son correctas las secuencias

uno dos tres = 123exclusivo = 0Abajo Arriba = 57

y son incorrectas las secuenciasuno dos tres = 123

exclusivo inclusive = 0

fuera dentro = 56

Una gramaacutetica que define las secuencias con este formato es

ltListagt = nombre ltOtrosNombresgt = numero

ltOtrosNombresgt = nombre nombre | nombre nombre | ε

se trata de una gramaacutetica que cumple la condicioacuten LL(1)- el siacutembolo director para la alternativa vaciacutea es el siacutembolo igual es evidente pues que las tres reglas de ltOtrosNombresgt tienen siacutembolos directores distintos

37

- la decisioacuten de abandonar la repeticioacuten del anaacutelisis de un nuevo nombre (en las dos primeras alterna-tivas de ltOtrosNombresgt) se puede tomar ante la presencia del siacutembolo igual

En la implementacioacuten de un analizador sintaacutectico LL(1) en sentido estricto la seleccioacuten de la alternativa vaciacutea para expandir el siacutembolo ltOtrosNombresgt se hariacutea tras comprobar expliacutecitamente que la pie-za sintaacutectica por adelantado es el siacutembolo igual

El funcionamiento del analizador generado por JavaCC depende del orden de colocacioacuten de las reglas A continuacioacuten se exponen varias posibilidades de especificacioacuten y se comenta el analizador generado en cada caso

bull Especificacioacuten primeraSe transcriben literalmente las reglas tal y como se han escrito antes en la notacioacuten BNF noacutetese que para poder codificar la parte derecha de la regla vaciacutea ha de incluirse una accioacuten sintaacutectica vaciacutea

PARSER_BEGIN (Igualdad)

public class Igualdad

public static void main (String[] argum) throws ParseException Igualdad analisis = new Igualdad(Systemin) analisissecuencia() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (Igualdad)

void secuencia() lista() ltEOFgt

void lista() lt nombre gt otrosNombres() = lt numero gt

void otrosNombres() lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt ) |

TOKEN lt nombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN lt numero ( [ 0 - 9 ] )+ gt

SKIP | r | n

38

El analizador generado funciona correctamente puesto que en el meacutetodo otrosNombres se selec-ciona la alternativa vaciacutea despueacutes de haber comprobado que la pieza por adelantado no es siacutembolo ini-cial de ninguna de las dos alternativas precedentes (no es una coma ni un siacutembolo dos puntos)

bull Especificacioacuten segundaSe considera la misma especificacioacuten anterior pero cambiando el orden de colocacioacuten de las reglas ahora la produccioacuten vaciacutea se coloca como primera alternativa (soacutelo se muestran las partes modifica-das)

void otrosNombres() | lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt )

Con la entrada de esta especificacioacuten el generador JavaCC produce un mensaje en el que se avisa de que las dos alternativas que estaacuten colocadas detraacutes de la alternativa vaciacutea nunca podraacuten seleccionarse aun asiacute se genera un analizador Pero es un analizador que funciona incorrectamente asiacute por ejemplo la entrada

mxa nyz = 57

se considera incorrecta despueacutes de visto el primer nombre se intenta aplicar la produccioacuten vaciacutea (se espera encontrar el siacutembolo igual) pero habriacutea que aplicar la regla cuyo siacutembolo director es la coma

bull Especificacioacuten terceraSe utilizan las posibilidades de la notacioacuten BNF-Ampliada para prescindir de la especificacioacuten expliacuteci-ta de la palabra vaciacutea se aprovecha que JavaCC admite el operador + para indicar repeticiones de una o maacutes veces la alternativa vaciacutea estaacute impliacutecitamente contemplada en la posibilidad de cero repeti-ciones que contempla el operador (soacutelo se muestran las partes modificadas en la especificacioacuten)

void otrosNombres() ( lt nombre gt )+ | ( lt nombre gt )

El meacutetodo otrosNombres() generado hace las comprobaciones en el siguiente orden

- se comprueba si la pieza por adelantado es una coma (siacutembolo inicial de la primera alternativa) y en este caso se selecciona la primera regla- si no es una coma se comprueba si es un siacutembolo dos puntos (siacutembolo inicial de la segunda alter-nativa) y en este caso se selecciona la segunda regla- si no es un siacutembolo dos puntos se considera la palabra vaciacutea como siacutembolo inicial (siempre posi-ble impliacutecitamente) para la segunda alternativa en este caso para que no haya error la pieza por adelantado ha de ser un siacutembolo igual

Se trata pues de un analizador que funciona correctamente

bull Especificacioacuten cuarta

39

Se pone la misma especificacioacuten anterior pero cambiando el orden de consideracioacuten de la regla vaciacutea ahora se incluye impliacutecitamente en la primera alternativa (soacutelo se muestran las partes modificadas)

void otrosNombres() ( lt nombre gt ) | ( lt nombre gt )+

Para esta especificacioacuten el generador JavaCC emite un aviso en el que se indica que la segunda alter -nativa nunca se seleccionaraacute no obstante se genera un analizador que no funciona como se supone que deberiacutea hacerlo por ejemplo el anaacutelisis de la entrada

nyz mxa = 1

produce un error despueacutes de tratado el primer nombre se intenta aplicar la produccioacuten vaciacutea (corres-pondiente a la repeticioacuten de cero veces contemplada en la primera regla) por lo que no se encuentra el esperado siacutembolo igual

40

Page 32: JavaCC

Cuando la opcioacuten Common_Token_Action se establece con el valor true es obligado incluir el blo-que de declaraciones lexicograacuteficas compartidas (TOKEN_MGR_DECLS) Ademaacutes en ese bloque se preci-sa la definicioacuten de un meacutetodo cuyo encabezamiento sea

[ static ] void CommonTokenAction(Token t)

el coacutedigo de este meacutetodo constituye la accioacuten lexicograacutefica comuacuten que se ejecutaraacute despueacutes de la deteccioacuten de cualquier pieza sintaacutectica el meacutetodo tiene un paraacutemetro de tipo Token mediante el que se puede co-municar la informacioacuten correspondiente a la pieza que se acaba de encontrar en la entrada analizada

Si una pieza sintaacutectica tiene asociada una accioacuten lexicograacutefica propia la accioacuten lexicograacutefica comuacuten (si es-taacute definida) se ejecutaraacute inmediatamente despueacutes de realizar la accioacuten propia

La accioacuten lexicograacutefica comuacuten se aplica a todas las piezas sintaacutecticas definidas como TOKEN bien sea no-minalmente (en las reglas lexicograacuteficas) o bien anoacutenimamente (incorporadas dentro de las reglas sintaacutecti-cas) sin embargo no se aplica cuando la secuencia acoplada se corresponde con una expresioacuten regular es-pecificada como SKIP (noacutetese que por el contrario una especificacioacuten SKIP siacute puede tener asociada una accioacuten lexicograacutefica propia)

El coacutedigo del meacutetodo CommonTokenAction se traslada como un componente maacutes del bloque de decla-raciones compartidas (TOKEN_MGR_DECLS) al principio de la clase del analizador lexicograacutefico

∆ Ejemplo 7

Se pretende ahora ampliar el ejemplo de presentacioacuten con la obtencioacuten de una relacioacuten numerada de los lexemas de las piezas sintaacutecticas encontradas en la entrada analizada en el caso de las variables y de las constantes antes del lexema se mostraraacute un indicativo (Var Const) por ejemplo si la expresioacuten de la entrada es

( a + 987 ) b

se grabaraacute la salida

( 1Var a 2+ 3Const 987 4) 5 6Var b 7 8

En la solucioacuten que se propone a continuacioacuten se muestran la definicioacuten de las opciones y las reglas sin-taacutecticas y lexicograacuteficas (en el resto no hay modificaciones) puede resaltarse lo siguiente- las llamadas al meacutetodo CommonTokenAction se incorporan automaacuteticamente no hay que incluir-las en la especificacioacuten como paraacutemetro de llamada a este meacutetodo siempre se emplea el objeto que re -presenta la pieza que se acaba de detectar- la accioacuten lexicograacutefica comuacuten realiza la actualizacioacuten del contador de piezas y la grabacioacuten del lexe-ma de la pieza detectada con su nuacutemero de orden- la accioacuten lexicograacutefica comuacuten tambieacuten se aplica a las piezas sintaacutecticas anoacutenimas (pareacutentesis y ope-radores) y a la pieza especial que representa el final del fichero (EOF)

- las piezas sintaacutecticas ltconstantegt y ltvariablegt tienen asociada una accioacuten lexicograacutefica propia que se ejecuta antes de la accioacuten lexicograacutefica comuacuten- los caracteres que se saltan (SKIP) no estaacuten afectados por la accioacuten lexicograacutefica comuacuten

options Ignore_Case = true Common_Token_Action = true

31

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarIndicativo(String indicativo) Systemoutprint(indicativo + )

static void grabarLexema (int num String lexema) Systemoutprint(lexema + + num + n)

static void CommonTokenAction(Token pieza) numero++ grabarLexema(numero piezaimage)

TOKEN lt variable [a-z] gt

grabarIndicativo(Var)

32

TOKEN lt constante ( [0-9] ) + gt

grabarIndicativo(Const)

SKIP | n | t | r

A modo de ejemplo de aplicacioacuten de la accioacuten lexicograacutefica comuacuten en lo que sigue se ofrece una solucioacuten para la grabacioacuten (en el fichero predefinido de salida) del texto analizado con las liacuteneas numeradas

∆ Ejemplo 8 [ Listado del texto analizado ]Se desea completar el ejemplo de presentacioacuten con la grabacioacuten de un listado con las liacuteneas numeradas del texto analizado (noacutetese que una expresioacuten puede escribirse ocupando varias liacuteneas) No se muestra la especificacioacuten sintaacutectica debido a que no hay ninguna modificacioacuten en ella Los aspectos resentildeables de la solucioacuten que se propone son- se usa la variable numLin como contador de liacuteneas

- en la variable linea se van yuxtaponiendo los lexemas de las piezas sintaacutecticas de una liacutenea esta yuxtaposicioacuten se realiza mediante la accioacuten lexicograacutefica comuacuten- las secuencias reconocidas con los patrones especificados como SKIP no implican la ejecucioacuten de la accioacuten lexicograacutefica comuacuten por ello su lexema (almacenado en la variable image) ha de yuxtaponer-se expliacutecitamente- hay que separar el patroacuten correspondiente al fin de liacutenea para detectar el momento en que ha de gra-barse la liacutenea completa cuyo final se ha alcanzado

options Ignore_Case = true Common_Token_Action = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(nnAnalisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

TOKEN_MGR_DECLS static int numLin = 0 static String linea =

33

static void CommonTokenAction(Token pieza) linea = linea + piezaimage

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP lt | t | r gt linea = linea + image

SKIP n numLin++ Systemoutprintln(numLin + + linea) linea =

A modo de recapitulacioacutenↄ Accioacuten lexicograacutefica Accioacuten sintaacutectica

bull accioacuten lexicograacutefica bloque de coacutedigo Java asociado a una pieza sintaacutectica nominal (TOKEN) o a una secuencia de caracteres (SKIP) se ejecuta cuando se detecta en la entrada la pieza o la secuencia corres-pondiente no se puede aplicar a piezas sintaacutecticas anoacutenimas (autodefinidas con comillas)bull accioacuten sintaacutectica bloque de coacutedigo Java asociado a un punto de la estructura sintaacutectica (el punto indica-do por el sitio de la produccioacuten donde se inserta el bloque) se ejecuta cuando el anaacutelisis de la entrada pa-sa por ese punto de la estructura

ↄ Valor de una pieza sintaacutectica comunicadaEl analizador lexicograacutefico comunica al analizador sintaacutectico una pieza a traveacutes de un valor de la clase Token (fichero Tokenjava) un objeto de la clase Token representa las caracteriacutesticas de la pieza comunicada En la clase Token entre otros se tienen los campos

String imagelexema de la pieza

int beginLine beginColumn endLine endColumnposicioacuten del lexema en el fichero analizado (indicada por los nuacutemeros de fila y de columna de su comienzo y de su terminacioacuten)

ↄ Valor asociado a un siacutembolo

34

En las producciones que especifican la sintaxis se pueden incluir asignaciones que sirven para dejar ano-tado el valor asociado a un siacutembolo

bull siacutembolo no terminal valor = nombreSimbolo()el meacutetodo de anaacutelisis asociado a nombreSimbolo se ha declarado de un cierto tipo (distinto del tipo void) valor ha de ser una variable del mismo tipo que el meacutetodo

bull siacutembolo terminal dato = lt nombrePieza gtel valor asignado es el valor de la clase Token representativo de la pieza sintaacutectica (nominal) dato ha de ser una variable de tipo Token

ↄ Miradas sobre la pieza sintaacutectica comunicadaLa pieza sintaacutectica comunicada puede mirarse desde cada uno de los dos analizadores

bull analizador sintaacutecticola pieza comunicada (desde el analizador lexicograacutefico) se tiene anotada en un campo de la clase del analizador sintaacutectico declarado como

(static) public Token token

este valor es accesible desde el coacutedigo de todos los meacutetodos de anaacutelisis sintaacutectico el contenido del campo variacutea cuando se recibe una nueva pieza sintaacutectica

bull analizador lexicograacuteficola pieza dispuesta para ser comunicada (al analizador sintaacutectico) se tiene anotada en una variable local declarada como

Token matchedToken

este valor es accesible para el coacutedigo de todas las acciones lexicograacuteficas

ↄ El lexema de la pieza comunicadaEl lexema de la pieza sintaacutectica comunicada se encuentra disponible en dos sitios distintos que compar-ten el mismo nombre pero que son de distinto tipo y se usan de distinta manera

bull String image

es un campo de la clase TOKEN puede consultarse en relacioacuten con un objeto de esa clase que esteacute disponible

bull StringBuffer image

es un campo de la clase del analizador lexicograacutefico ( ∙ ∙ ∙ ∙ TokenManagerjava) es accesible desde el coacutedigo de la clase y por lo tanto desde todas las acciones lexicograacuteficas (bloques de coacute-digo que estaacuten incorporados al analizador lexicograacutefico)

se trata de un valor disponible tanto para las piezas sintaacutecticas nominales (TOKEN) como para las secuencias que se saltan (SKIP)

ↄ Resumen relativo a la lexicografiacutea

TOKEN pieza sintaacutectica nominal∙ ∙ ∙ pieza sintaacutectica anoacutenimaSKIP secuencia de caracteres que no forman pieza

35

Precisiones sobre el analizador sintaacutectico generadoPuede decirse que en su funcionamiento maacutes sencillo cuando no se emplean las posibilidades de examen por adelantado (ldquolookaheadrdquo) JavaCC comprueba que las reglas sintaacutecticas cumplen la condicioacuten LL(1) y generan un analizador descendente-predictivo-recursivo Pero esta apreciacioacuten hay que matizarla ya que el analizador sintaacutectico generado no se ajusta en sentido estricto al modelo de analizador LL(1)

En el modelo de anaacutelisis LL(1) el orden del examen de las distintas alternativas de expansioacuten de un siacutembo-lo no terminal es indiferente ya que se conocen los siacutembolos directores de cada una de ellas (que resultan ser conjuntos disjuntos) en la implementacioacuten del subprograma de anaacutelisis asociado al siacutembolo no terminal ltNombreSimbgt cuyas producciones son

ltNombreSimbgt = α1

| α2

∙ ∙ ∙

| αn

se podriacutea preguntar en cualquier orden si la pieza actual pertenece al conjunto de siacutembolos directores de las distintas reglas Sin embargo en el analizador sintaacutectico generado por JavaCC la implementacioacuten del meacute-todo asociado al siacutembolo no terminal ltNombreSimbgt no sigue esa pauta

Si las anteriores reglas sintaacutecticas se transcriben en JavaCC de la forma

void nombreSimb () α1-jcc

| α2-jcc

∙ ∙ ∙

| αn-jcc

1

accioacutenlexicograacutefica

comuacuten

Commom -TokenAction2

accioacutenlexicograacutefica

propia

(accioacuten ligadaa una pieza)3

bloque dedeclaracioneslexicograacuteficas

TOKEN_MGR_DECLS4

declaracioneslexicograacuteficaspredefinidas

(disponibilidad

de uso)TOKENsisisisi∙ ∙ ∙sino--SKIPnosisisi

Las indicaciones si de las columnas 3 y 4 son consecuencia de las indicaciones de la columna 2

La accioacuten lexicograacutefica comuacuten se ejecuta despueacutes de la accioacuten lexicograacutefica propia

36

el meacutetodo de anaacutelisis generado procede de la siguiente manera

se van considerando las alternativas sucesivamente en el mismo orden en el que estaacuten especifica-das primero la de α1 (α1-jcc) despueacutes la de α2 (α2-jcc) etc

para cada alternativa seleccionada se comprueba si la pieza por adelantado coincide con alguno de los siacutembolos iniciales de la parte derecha y si hay coincidencia se prosigue el anaacutelisis con ella si no hay coincidencia se pasa a la alternativa siguiente

siguiendo con este orden si se llega al final de las alternativas sin haberse encontrado coincidencia alguna se produce un error sintaacutectico

En el analizador generado por JavaCC hay una peculiaridad que no se tiene en los analizadores LL(1) es-trictamente considerados la palabra vaciacutea siacute se considera como posible siacutembolo inicial de la parte derecha una regla (esto ocurre siempre que la parte derecha es anulable) De esta peculiaridad se deriva una conse-cuencia que ha de tenerse en cuenta al escribir la especificacioacuten dado que siempre es posible considerar la palabra vaciacutea como la siguiente pieza por adelantado si se llega a examinar la alternativa correspondiente a una produccioacuten anulable el analizador generado siempre seleccionaraacute esa alternativa con independencia de la siguiente pieza que esteacute presente en la entrada que queda por analizar

Asiacute pues si alguna de las producciones que definen un siacutembolo no terminal es anulable es importante el or-den en que se escriben las alternativas en una especificacioacuten sintaacutectica JavaCC el funcionamiento del ana-lizador generado siacute depende de la colocacioacuten elegida para las reglas

Para ilustrar estas peculiaridades del analizador sintaacutectico generado por JavaCC a continuacioacuten se expone un ejemplo

∆ Ejemplo 9

Se quiere obtener un analizador leacutexico-sintaacutectico de textos que se ajustan al siguiente formato una se-cuencia de uno o maacutes nombres seguida del siacutembolo igual seguido de un nuacutemero entero si la secuencia tiene maacutes de un nombre han de estar separados bien por una coma en todos los casos o bien por el siacutem-bolo dos puntos en todos los casos esto es son correctas las secuencias

uno dos tres = 123exclusivo = 0Abajo Arriba = 57

y son incorrectas las secuenciasuno dos tres = 123

exclusivo inclusive = 0

fuera dentro = 56

Una gramaacutetica que define las secuencias con este formato es

ltListagt = nombre ltOtrosNombresgt = numero

ltOtrosNombresgt = nombre nombre | nombre nombre | ε

se trata de una gramaacutetica que cumple la condicioacuten LL(1)- el siacutembolo director para la alternativa vaciacutea es el siacutembolo igual es evidente pues que las tres reglas de ltOtrosNombresgt tienen siacutembolos directores distintos

37

- la decisioacuten de abandonar la repeticioacuten del anaacutelisis de un nuevo nombre (en las dos primeras alterna-tivas de ltOtrosNombresgt) se puede tomar ante la presencia del siacutembolo igual

En la implementacioacuten de un analizador sintaacutectico LL(1) en sentido estricto la seleccioacuten de la alternativa vaciacutea para expandir el siacutembolo ltOtrosNombresgt se hariacutea tras comprobar expliacutecitamente que la pie-za sintaacutectica por adelantado es el siacutembolo igual

El funcionamiento del analizador generado por JavaCC depende del orden de colocacioacuten de las reglas A continuacioacuten se exponen varias posibilidades de especificacioacuten y se comenta el analizador generado en cada caso

bull Especificacioacuten primeraSe transcriben literalmente las reglas tal y como se han escrito antes en la notacioacuten BNF noacutetese que para poder codificar la parte derecha de la regla vaciacutea ha de incluirse una accioacuten sintaacutectica vaciacutea

PARSER_BEGIN (Igualdad)

public class Igualdad

public static void main (String[] argum) throws ParseException Igualdad analisis = new Igualdad(Systemin) analisissecuencia() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (Igualdad)

void secuencia() lista() ltEOFgt

void lista() lt nombre gt otrosNombres() = lt numero gt

void otrosNombres() lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt ) |

TOKEN lt nombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN lt numero ( [ 0 - 9 ] )+ gt

SKIP | r | n

38

El analizador generado funciona correctamente puesto que en el meacutetodo otrosNombres se selec-ciona la alternativa vaciacutea despueacutes de haber comprobado que la pieza por adelantado no es siacutembolo ini-cial de ninguna de las dos alternativas precedentes (no es una coma ni un siacutembolo dos puntos)

bull Especificacioacuten segundaSe considera la misma especificacioacuten anterior pero cambiando el orden de colocacioacuten de las reglas ahora la produccioacuten vaciacutea se coloca como primera alternativa (soacutelo se muestran las partes modifica-das)

void otrosNombres() | lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt )

Con la entrada de esta especificacioacuten el generador JavaCC produce un mensaje en el que se avisa de que las dos alternativas que estaacuten colocadas detraacutes de la alternativa vaciacutea nunca podraacuten seleccionarse aun asiacute se genera un analizador Pero es un analizador que funciona incorrectamente asiacute por ejemplo la entrada

mxa nyz = 57

se considera incorrecta despueacutes de visto el primer nombre se intenta aplicar la produccioacuten vaciacutea (se espera encontrar el siacutembolo igual) pero habriacutea que aplicar la regla cuyo siacutembolo director es la coma

bull Especificacioacuten terceraSe utilizan las posibilidades de la notacioacuten BNF-Ampliada para prescindir de la especificacioacuten expliacuteci-ta de la palabra vaciacutea se aprovecha que JavaCC admite el operador + para indicar repeticiones de una o maacutes veces la alternativa vaciacutea estaacute impliacutecitamente contemplada en la posibilidad de cero repeti-ciones que contempla el operador (soacutelo se muestran las partes modificadas en la especificacioacuten)

void otrosNombres() ( lt nombre gt )+ | ( lt nombre gt )

El meacutetodo otrosNombres() generado hace las comprobaciones en el siguiente orden

- se comprueba si la pieza por adelantado es una coma (siacutembolo inicial de la primera alternativa) y en este caso se selecciona la primera regla- si no es una coma se comprueba si es un siacutembolo dos puntos (siacutembolo inicial de la segunda alter-nativa) y en este caso se selecciona la segunda regla- si no es un siacutembolo dos puntos se considera la palabra vaciacutea como siacutembolo inicial (siempre posi-ble impliacutecitamente) para la segunda alternativa en este caso para que no haya error la pieza por adelantado ha de ser un siacutembolo igual

Se trata pues de un analizador que funciona correctamente

bull Especificacioacuten cuarta

39

Se pone la misma especificacioacuten anterior pero cambiando el orden de consideracioacuten de la regla vaciacutea ahora se incluye impliacutecitamente en la primera alternativa (soacutelo se muestran las partes modificadas)

void otrosNombres() ( lt nombre gt ) | ( lt nombre gt )+

Para esta especificacioacuten el generador JavaCC emite un aviso en el que se indica que la segunda alter -nativa nunca se seleccionaraacute no obstante se genera un analizador que no funciona como se supone que deberiacutea hacerlo por ejemplo el anaacutelisis de la entrada

nyz mxa = 1

produce un error despueacutes de tratado el primer nombre se intenta aplicar la produccioacuten vaciacutea (corres-pondiente a la repeticioacuten de cero veces contemplada en la primera regla) por lo que no se encuentra el esperado siacutembolo igual

40

Page 33: JavaCC

void unaExpresion() expresion() ltEOFgt

void expresion() termino() ( + termino() )

void termino() factor() ( factor() )

void factor() ltconstantegt | ltvariablegt | ( expresion() )

TOKEN_MGR_DECLS

static int numero = 0

static void grabarIndicativo(String indicativo) Systemoutprint(indicativo + )

static void grabarLexema (int num String lexema) Systemoutprint(lexema + + num + n)

static void CommonTokenAction(Token pieza) numero++ grabarLexema(numero piezaimage)

TOKEN lt variable [a-z] gt

grabarIndicativo(Var)

32

TOKEN lt constante ( [0-9] ) + gt

grabarIndicativo(Const)

SKIP | n | t | r

A modo de ejemplo de aplicacioacuten de la accioacuten lexicograacutefica comuacuten en lo que sigue se ofrece una solucioacuten para la grabacioacuten (en el fichero predefinido de salida) del texto analizado con las liacuteneas numeradas

∆ Ejemplo 8 [ Listado del texto analizado ]Se desea completar el ejemplo de presentacioacuten con la grabacioacuten de un listado con las liacuteneas numeradas del texto analizado (noacutetese que una expresioacuten puede escribirse ocupando varias liacuteneas) No se muestra la especificacioacuten sintaacutectica debido a que no hay ninguna modificacioacuten en ella Los aspectos resentildeables de la solucioacuten que se propone son- se usa la variable numLin como contador de liacuteneas

- en la variable linea se van yuxtaponiendo los lexemas de las piezas sintaacutecticas de una liacutenea esta yuxtaposicioacuten se realiza mediante la accioacuten lexicograacutefica comuacuten- las secuencias reconocidas con los patrones especificados como SKIP no implican la ejecucioacuten de la accioacuten lexicograacutefica comuacuten por ello su lexema (almacenado en la variable image) ha de yuxtaponer-se expliacutecitamente- hay que separar el patroacuten correspondiente al fin de liacutenea para detectar el momento en que ha de gra-barse la liacutenea completa cuyo final se ha alcanzado

options Ignore_Case = true Common_Token_Action = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(nnAnalisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

TOKEN_MGR_DECLS static int numLin = 0 static String linea =

33

static void CommonTokenAction(Token pieza) linea = linea + piezaimage

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP lt | t | r gt linea = linea + image

SKIP n numLin++ Systemoutprintln(numLin + + linea) linea =

A modo de recapitulacioacutenↄ Accioacuten lexicograacutefica Accioacuten sintaacutectica

bull accioacuten lexicograacutefica bloque de coacutedigo Java asociado a una pieza sintaacutectica nominal (TOKEN) o a una secuencia de caracteres (SKIP) se ejecuta cuando se detecta en la entrada la pieza o la secuencia corres-pondiente no se puede aplicar a piezas sintaacutecticas anoacutenimas (autodefinidas con comillas)bull accioacuten sintaacutectica bloque de coacutedigo Java asociado a un punto de la estructura sintaacutectica (el punto indica-do por el sitio de la produccioacuten donde se inserta el bloque) se ejecuta cuando el anaacutelisis de la entrada pa-sa por ese punto de la estructura

ↄ Valor de una pieza sintaacutectica comunicadaEl analizador lexicograacutefico comunica al analizador sintaacutectico una pieza a traveacutes de un valor de la clase Token (fichero Tokenjava) un objeto de la clase Token representa las caracteriacutesticas de la pieza comunicada En la clase Token entre otros se tienen los campos

String imagelexema de la pieza

int beginLine beginColumn endLine endColumnposicioacuten del lexema en el fichero analizado (indicada por los nuacutemeros de fila y de columna de su comienzo y de su terminacioacuten)

ↄ Valor asociado a un siacutembolo

34

En las producciones que especifican la sintaxis se pueden incluir asignaciones que sirven para dejar ano-tado el valor asociado a un siacutembolo

bull siacutembolo no terminal valor = nombreSimbolo()el meacutetodo de anaacutelisis asociado a nombreSimbolo se ha declarado de un cierto tipo (distinto del tipo void) valor ha de ser una variable del mismo tipo que el meacutetodo

bull siacutembolo terminal dato = lt nombrePieza gtel valor asignado es el valor de la clase Token representativo de la pieza sintaacutectica (nominal) dato ha de ser una variable de tipo Token

ↄ Miradas sobre la pieza sintaacutectica comunicadaLa pieza sintaacutectica comunicada puede mirarse desde cada uno de los dos analizadores

bull analizador sintaacutecticola pieza comunicada (desde el analizador lexicograacutefico) se tiene anotada en un campo de la clase del analizador sintaacutectico declarado como

(static) public Token token

este valor es accesible desde el coacutedigo de todos los meacutetodos de anaacutelisis sintaacutectico el contenido del campo variacutea cuando se recibe una nueva pieza sintaacutectica

bull analizador lexicograacuteficola pieza dispuesta para ser comunicada (al analizador sintaacutectico) se tiene anotada en una variable local declarada como

Token matchedToken

este valor es accesible para el coacutedigo de todas las acciones lexicograacuteficas

ↄ El lexema de la pieza comunicadaEl lexema de la pieza sintaacutectica comunicada se encuentra disponible en dos sitios distintos que compar-ten el mismo nombre pero que son de distinto tipo y se usan de distinta manera

bull String image

es un campo de la clase TOKEN puede consultarse en relacioacuten con un objeto de esa clase que esteacute disponible

bull StringBuffer image

es un campo de la clase del analizador lexicograacutefico ( ∙ ∙ ∙ ∙ TokenManagerjava) es accesible desde el coacutedigo de la clase y por lo tanto desde todas las acciones lexicograacuteficas (bloques de coacute-digo que estaacuten incorporados al analizador lexicograacutefico)

se trata de un valor disponible tanto para las piezas sintaacutecticas nominales (TOKEN) como para las secuencias que se saltan (SKIP)

ↄ Resumen relativo a la lexicografiacutea

TOKEN pieza sintaacutectica nominal∙ ∙ ∙ pieza sintaacutectica anoacutenimaSKIP secuencia de caracteres que no forman pieza

35

Precisiones sobre el analizador sintaacutectico generadoPuede decirse que en su funcionamiento maacutes sencillo cuando no se emplean las posibilidades de examen por adelantado (ldquolookaheadrdquo) JavaCC comprueba que las reglas sintaacutecticas cumplen la condicioacuten LL(1) y generan un analizador descendente-predictivo-recursivo Pero esta apreciacioacuten hay que matizarla ya que el analizador sintaacutectico generado no se ajusta en sentido estricto al modelo de analizador LL(1)

En el modelo de anaacutelisis LL(1) el orden del examen de las distintas alternativas de expansioacuten de un siacutembo-lo no terminal es indiferente ya que se conocen los siacutembolos directores de cada una de ellas (que resultan ser conjuntos disjuntos) en la implementacioacuten del subprograma de anaacutelisis asociado al siacutembolo no terminal ltNombreSimbgt cuyas producciones son

ltNombreSimbgt = α1

| α2

∙ ∙ ∙

| αn

se podriacutea preguntar en cualquier orden si la pieza actual pertenece al conjunto de siacutembolos directores de las distintas reglas Sin embargo en el analizador sintaacutectico generado por JavaCC la implementacioacuten del meacute-todo asociado al siacutembolo no terminal ltNombreSimbgt no sigue esa pauta

Si las anteriores reglas sintaacutecticas se transcriben en JavaCC de la forma

void nombreSimb () α1-jcc

| α2-jcc

∙ ∙ ∙

| αn-jcc

1

accioacutenlexicograacutefica

comuacuten

Commom -TokenAction2

accioacutenlexicograacutefica

propia

(accioacuten ligadaa una pieza)3

bloque dedeclaracioneslexicograacuteficas

TOKEN_MGR_DECLS4

declaracioneslexicograacuteficaspredefinidas

(disponibilidad

de uso)TOKENsisisisi∙ ∙ ∙sino--SKIPnosisisi

Las indicaciones si de las columnas 3 y 4 son consecuencia de las indicaciones de la columna 2

La accioacuten lexicograacutefica comuacuten se ejecuta despueacutes de la accioacuten lexicograacutefica propia

36

el meacutetodo de anaacutelisis generado procede de la siguiente manera

se van considerando las alternativas sucesivamente en el mismo orden en el que estaacuten especifica-das primero la de α1 (α1-jcc) despueacutes la de α2 (α2-jcc) etc

para cada alternativa seleccionada se comprueba si la pieza por adelantado coincide con alguno de los siacutembolos iniciales de la parte derecha y si hay coincidencia se prosigue el anaacutelisis con ella si no hay coincidencia se pasa a la alternativa siguiente

siguiendo con este orden si se llega al final de las alternativas sin haberse encontrado coincidencia alguna se produce un error sintaacutectico

En el analizador generado por JavaCC hay una peculiaridad que no se tiene en los analizadores LL(1) es-trictamente considerados la palabra vaciacutea siacute se considera como posible siacutembolo inicial de la parte derecha una regla (esto ocurre siempre que la parte derecha es anulable) De esta peculiaridad se deriva una conse-cuencia que ha de tenerse en cuenta al escribir la especificacioacuten dado que siempre es posible considerar la palabra vaciacutea como la siguiente pieza por adelantado si se llega a examinar la alternativa correspondiente a una produccioacuten anulable el analizador generado siempre seleccionaraacute esa alternativa con independencia de la siguiente pieza que esteacute presente en la entrada que queda por analizar

Asiacute pues si alguna de las producciones que definen un siacutembolo no terminal es anulable es importante el or-den en que se escriben las alternativas en una especificacioacuten sintaacutectica JavaCC el funcionamiento del ana-lizador generado siacute depende de la colocacioacuten elegida para las reglas

Para ilustrar estas peculiaridades del analizador sintaacutectico generado por JavaCC a continuacioacuten se expone un ejemplo

∆ Ejemplo 9

Se quiere obtener un analizador leacutexico-sintaacutectico de textos que se ajustan al siguiente formato una se-cuencia de uno o maacutes nombres seguida del siacutembolo igual seguido de un nuacutemero entero si la secuencia tiene maacutes de un nombre han de estar separados bien por una coma en todos los casos o bien por el siacutem-bolo dos puntos en todos los casos esto es son correctas las secuencias

uno dos tres = 123exclusivo = 0Abajo Arriba = 57

y son incorrectas las secuenciasuno dos tres = 123

exclusivo inclusive = 0

fuera dentro = 56

Una gramaacutetica que define las secuencias con este formato es

ltListagt = nombre ltOtrosNombresgt = numero

ltOtrosNombresgt = nombre nombre | nombre nombre | ε

se trata de una gramaacutetica que cumple la condicioacuten LL(1)- el siacutembolo director para la alternativa vaciacutea es el siacutembolo igual es evidente pues que las tres reglas de ltOtrosNombresgt tienen siacutembolos directores distintos

37

- la decisioacuten de abandonar la repeticioacuten del anaacutelisis de un nuevo nombre (en las dos primeras alterna-tivas de ltOtrosNombresgt) se puede tomar ante la presencia del siacutembolo igual

En la implementacioacuten de un analizador sintaacutectico LL(1) en sentido estricto la seleccioacuten de la alternativa vaciacutea para expandir el siacutembolo ltOtrosNombresgt se hariacutea tras comprobar expliacutecitamente que la pie-za sintaacutectica por adelantado es el siacutembolo igual

El funcionamiento del analizador generado por JavaCC depende del orden de colocacioacuten de las reglas A continuacioacuten se exponen varias posibilidades de especificacioacuten y se comenta el analizador generado en cada caso

bull Especificacioacuten primeraSe transcriben literalmente las reglas tal y como se han escrito antes en la notacioacuten BNF noacutetese que para poder codificar la parte derecha de la regla vaciacutea ha de incluirse una accioacuten sintaacutectica vaciacutea

PARSER_BEGIN (Igualdad)

public class Igualdad

public static void main (String[] argum) throws ParseException Igualdad analisis = new Igualdad(Systemin) analisissecuencia() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (Igualdad)

void secuencia() lista() ltEOFgt

void lista() lt nombre gt otrosNombres() = lt numero gt

void otrosNombres() lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt ) |

TOKEN lt nombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN lt numero ( [ 0 - 9 ] )+ gt

SKIP | r | n

38

El analizador generado funciona correctamente puesto que en el meacutetodo otrosNombres se selec-ciona la alternativa vaciacutea despueacutes de haber comprobado que la pieza por adelantado no es siacutembolo ini-cial de ninguna de las dos alternativas precedentes (no es una coma ni un siacutembolo dos puntos)

bull Especificacioacuten segundaSe considera la misma especificacioacuten anterior pero cambiando el orden de colocacioacuten de las reglas ahora la produccioacuten vaciacutea se coloca como primera alternativa (soacutelo se muestran las partes modifica-das)

void otrosNombres() | lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt )

Con la entrada de esta especificacioacuten el generador JavaCC produce un mensaje en el que se avisa de que las dos alternativas que estaacuten colocadas detraacutes de la alternativa vaciacutea nunca podraacuten seleccionarse aun asiacute se genera un analizador Pero es un analizador que funciona incorrectamente asiacute por ejemplo la entrada

mxa nyz = 57

se considera incorrecta despueacutes de visto el primer nombre se intenta aplicar la produccioacuten vaciacutea (se espera encontrar el siacutembolo igual) pero habriacutea que aplicar la regla cuyo siacutembolo director es la coma

bull Especificacioacuten terceraSe utilizan las posibilidades de la notacioacuten BNF-Ampliada para prescindir de la especificacioacuten expliacuteci-ta de la palabra vaciacutea se aprovecha que JavaCC admite el operador + para indicar repeticiones de una o maacutes veces la alternativa vaciacutea estaacute impliacutecitamente contemplada en la posibilidad de cero repeti-ciones que contempla el operador (soacutelo se muestran las partes modificadas en la especificacioacuten)

void otrosNombres() ( lt nombre gt )+ | ( lt nombre gt )

El meacutetodo otrosNombres() generado hace las comprobaciones en el siguiente orden

- se comprueba si la pieza por adelantado es una coma (siacutembolo inicial de la primera alternativa) y en este caso se selecciona la primera regla- si no es una coma se comprueba si es un siacutembolo dos puntos (siacutembolo inicial de la segunda alter-nativa) y en este caso se selecciona la segunda regla- si no es un siacutembolo dos puntos se considera la palabra vaciacutea como siacutembolo inicial (siempre posi-ble impliacutecitamente) para la segunda alternativa en este caso para que no haya error la pieza por adelantado ha de ser un siacutembolo igual

Se trata pues de un analizador que funciona correctamente

bull Especificacioacuten cuarta

39

Se pone la misma especificacioacuten anterior pero cambiando el orden de consideracioacuten de la regla vaciacutea ahora se incluye impliacutecitamente en la primera alternativa (soacutelo se muestran las partes modificadas)

void otrosNombres() ( lt nombre gt ) | ( lt nombre gt )+

Para esta especificacioacuten el generador JavaCC emite un aviso en el que se indica que la segunda alter -nativa nunca se seleccionaraacute no obstante se genera un analizador que no funciona como se supone que deberiacutea hacerlo por ejemplo el anaacutelisis de la entrada

nyz mxa = 1

produce un error despueacutes de tratado el primer nombre se intenta aplicar la produccioacuten vaciacutea (corres-pondiente a la repeticioacuten de cero veces contemplada en la primera regla) por lo que no se encuentra el esperado siacutembolo igual

40

Page 34: JavaCC

TOKEN lt constante ( [0-9] ) + gt

grabarIndicativo(Const)

SKIP | n | t | r

A modo de ejemplo de aplicacioacuten de la accioacuten lexicograacutefica comuacuten en lo que sigue se ofrece una solucioacuten para la grabacioacuten (en el fichero predefinido de salida) del texto analizado con las liacuteneas numeradas

∆ Ejemplo 8 [ Listado del texto analizado ]Se desea completar el ejemplo de presentacioacuten con la grabacioacuten de un listado con las liacuteneas numeradas del texto analizado (noacutetese que una expresioacuten puede escribirse ocupando varias liacuteneas) No se muestra la especificacioacuten sintaacutectica debido a que no hay ninguna modificacioacuten en ella Los aspectos resentildeables de la solucioacuten que se propone son- se usa la variable numLin como contador de liacuteneas

- en la variable linea se van yuxtaponiendo los lexemas de las piezas sintaacutecticas de una liacutenea esta yuxtaposicioacuten se realiza mediante la accioacuten lexicograacutefica comuacuten- las secuencias reconocidas con los patrones especificados como SKIP no implican la ejecucioacuten de la accioacuten lexicograacutefica comuacuten por ello su lexema (almacenado en la variable image) ha de yuxtaponer-se expliacutecitamente- hay que separar el patroacuten correspondiente al fin de liacutenea para detectar el momento en que ha de gra-barse la liacutenea completa cuyo final se ha alcanzado

options Ignore_Case = true Common_Token_Action = true

PARSER_BEGIN (ExprMin)

public class ExprMin public static void main (String[] argum) throws ParseException ExprMin anLexSint = new ExprMin (Systemin) anLexSintunaExpresion() Systemoutprintln(nnAnalisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (ExprMin)

TOKEN_MGR_DECLS static int numLin = 0 static String linea =

33

static void CommonTokenAction(Token pieza) linea = linea + piezaimage

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP lt | t | r gt linea = linea + image

SKIP n numLin++ Systemoutprintln(numLin + + linea) linea =

A modo de recapitulacioacutenↄ Accioacuten lexicograacutefica Accioacuten sintaacutectica

bull accioacuten lexicograacutefica bloque de coacutedigo Java asociado a una pieza sintaacutectica nominal (TOKEN) o a una secuencia de caracteres (SKIP) se ejecuta cuando se detecta en la entrada la pieza o la secuencia corres-pondiente no se puede aplicar a piezas sintaacutecticas anoacutenimas (autodefinidas con comillas)bull accioacuten sintaacutectica bloque de coacutedigo Java asociado a un punto de la estructura sintaacutectica (el punto indica-do por el sitio de la produccioacuten donde se inserta el bloque) se ejecuta cuando el anaacutelisis de la entrada pa-sa por ese punto de la estructura

ↄ Valor de una pieza sintaacutectica comunicadaEl analizador lexicograacutefico comunica al analizador sintaacutectico una pieza a traveacutes de un valor de la clase Token (fichero Tokenjava) un objeto de la clase Token representa las caracteriacutesticas de la pieza comunicada En la clase Token entre otros se tienen los campos

String imagelexema de la pieza

int beginLine beginColumn endLine endColumnposicioacuten del lexema en el fichero analizado (indicada por los nuacutemeros de fila y de columna de su comienzo y de su terminacioacuten)

ↄ Valor asociado a un siacutembolo

34

En las producciones que especifican la sintaxis se pueden incluir asignaciones que sirven para dejar ano-tado el valor asociado a un siacutembolo

bull siacutembolo no terminal valor = nombreSimbolo()el meacutetodo de anaacutelisis asociado a nombreSimbolo se ha declarado de un cierto tipo (distinto del tipo void) valor ha de ser una variable del mismo tipo que el meacutetodo

bull siacutembolo terminal dato = lt nombrePieza gtel valor asignado es el valor de la clase Token representativo de la pieza sintaacutectica (nominal) dato ha de ser una variable de tipo Token

ↄ Miradas sobre la pieza sintaacutectica comunicadaLa pieza sintaacutectica comunicada puede mirarse desde cada uno de los dos analizadores

bull analizador sintaacutecticola pieza comunicada (desde el analizador lexicograacutefico) se tiene anotada en un campo de la clase del analizador sintaacutectico declarado como

(static) public Token token

este valor es accesible desde el coacutedigo de todos los meacutetodos de anaacutelisis sintaacutectico el contenido del campo variacutea cuando se recibe una nueva pieza sintaacutectica

bull analizador lexicograacuteficola pieza dispuesta para ser comunicada (al analizador sintaacutectico) se tiene anotada en una variable local declarada como

Token matchedToken

este valor es accesible para el coacutedigo de todas las acciones lexicograacuteficas

ↄ El lexema de la pieza comunicadaEl lexema de la pieza sintaacutectica comunicada se encuentra disponible en dos sitios distintos que compar-ten el mismo nombre pero que son de distinto tipo y se usan de distinta manera

bull String image

es un campo de la clase TOKEN puede consultarse en relacioacuten con un objeto de esa clase que esteacute disponible

bull StringBuffer image

es un campo de la clase del analizador lexicograacutefico ( ∙ ∙ ∙ ∙ TokenManagerjava) es accesible desde el coacutedigo de la clase y por lo tanto desde todas las acciones lexicograacuteficas (bloques de coacute-digo que estaacuten incorporados al analizador lexicograacutefico)

se trata de un valor disponible tanto para las piezas sintaacutecticas nominales (TOKEN) como para las secuencias que se saltan (SKIP)

ↄ Resumen relativo a la lexicografiacutea

TOKEN pieza sintaacutectica nominal∙ ∙ ∙ pieza sintaacutectica anoacutenimaSKIP secuencia de caracteres que no forman pieza

35

Precisiones sobre el analizador sintaacutectico generadoPuede decirse que en su funcionamiento maacutes sencillo cuando no se emplean las posibilidades de examen por adelantado (ldquolookaheadrdquo) JavaCC comprueba que las reglas sintaacutecticas cumplen la condicioacuten LL(1) y generan un analizador descendente-predictivo-recursivo Pero esta apreciacioacuten hay que matizarla ya que el analizador sintaacutectico generado no se ajusta en sentido estricto al modelo de analizador LL(1)

En el modelo de anaacutelisis LL(1) el orden del examen de las distintas alternativas de expansioacuten de un siacutembo-lo no terminal es indiferente ya que se conocen los siacutembolos directores de cada una de ellas (que resultan ser conjuntos disjuntos) en la implementacioacuten del subprograma de anaacutelisis asociado al siacutembolo no terminal ltNombreSimbgt cuyas producciones son

ltNombreSimbgt = α1

| α2

∙ ∙ ∙

| αn

se podriacutea preguntar en cualquier orden si la pieza actual pertenece al conjunto de siacutembolos directores de las distintas reglas Sin embargo en el analizador sintaacutectico generado por JavaCC la implementacioacuten del meacute-todo asociado al siacutembolo no terminal ltNombreSimbgt no sigue esa pauta

Si las anteriores reglas sintaacutecticas se transcriben en JavaCC de la forma

void nombreSimb () α1-jcc

| α2-jcc

∙ ∙ ∙

| αn-jcc

1

accioacutenlexicograacutefica

comuacuten

Commom -TokenAction2

accioacutenlexicograacutefica

propia

(accioacuten ligadaa una pieza)3

bloque dedeclaracioneslexicograacuteficas

TOKEN_MGR_DECLS4

declaracioneslexicograacuteficaspredefinidas

(disponibilidad

de uso)TOKENsisisisi∙ ∙ ∙sino--SKIPnosisisi

Las indicaciones si de las columnas 3 y 4 son consecuencia de las indicaciones de la columna 2

La accioacuten lexicograacutefica comuacuten se ejecuta despueacutes de la accioacuten lexicograacutefica propia

36

el meacutetodo de anaacutelisis generado procede de la siguiente manera

se van considerando las alternativas sucesivamente en el mismo orden en el que estaacuten especifica-das primero la de α1 (α1-jcc) despueacutes la de α2 (α2-jcc) etc

para cada alternativa seleccionada se comprueba si la pieza por adelantado coincide con alguno de los siacutembolos iniciales de la parte derecha y si hay coincidencia se prosigue el anaacutelisis con ella si no hay coincidencia se pasa a la alternativa siguiente

siguiendo con este orden si se llega al final de las alternativas sin haberse encontrado coincidencia alguna se produce un error sintaacutectico

En el analizador generado por JavaCC hay una peculiaridad que no se tiene en los analizadores LL(1) es-trictamente considerados la palabra vaciacutea siacute se considera como posible siacutembolo inicial de la parte derecha una regla (esto ocurre siempre que la parte derecha es anulable) De esta peculiaridad se deriva una conse-cuencia que ha de tenerse en cuenta al escribir la especificacioacuten dado que siempre es posible considerar la palabra vaciacutea como la siguiente pieza por adelantado si se llega a examinar la alternativa correspondiente a una produccioacuten anulable el analizador generado siempre seleccionaraacute esa alternativa con independencia de la siguiente pieza que esteacute presente en la entrada que queda por analizar

Asiacute pues si alguna de las producciones que definen un siacutembolo no terminal es anulable es importante el or-den en que se escriben las alternativas en una especificacioacuten sintaacutectica JavaCC el funcionamiento del ana-lizador generado siacute depende de la colocacioacuten elegida para las reglas

Para ilustrar estas peculiaridades del analizador sintaacutectico generado por JavaCC a continuacioacuten se expone un ejemplo

∆ Ejemplo 9

Se quiere obtener un analizador leacutexico-sintaacutectico de textos que se ajustan al siguiente formato una se-cuencia de uno o maacutes nombres seguida del siacutembolo igual seguido de un nuacutemero entero si la secuencia tiene maacutes de un nombre han de estar separados bien por una coma en todos los casos o bien por el siacutem-bolo dos puntos en todos los casos esto es son correctas las secuencias

uno dos tres = 123exclusivo = 0Abajo Arriba = 57

y son incorrectas las secuenciasuno dos tres = 123

exclusivo inclusive = 0

fuera dentro = 56

Una gramaacutetica que define las secuencias con este formato es

ltListagt = nombre ltOtrosNombresgt = numero

ltOtrosNombresgt = nombre nombre | nombre nombre | ε

se trata de una gramaacutetica que cumple la condicioacuten LL(1)- el siacutembolo director para la alternativa vaciacutea es el siacutembolo igual es evidente pues que las tres reglas de ltOtrosNombresgt tienen siacutembolos directores distintos

37

- la decisioacuten de abandonar la repeticioacuten del anaacutelisis de un nuevo nombre (en las dos primeras alterna-tivas de ltOtrosNombresgt) se puede tomar ante la presencia del siacutembolo igual

En la implementacioacuten de un analizador sintaacutectico LL(1) en sentido estricto la seleccioacuten de la alternativa vaciacutea para expandir el siacutembolo ltOtrosNombresgt se hariacutea tras comprobar expliacutecitamente que la pie-za sintaacutectica por adelantado es el siacutembolo igual

El funcionamiento del analizador generado por JavaCC depende del orden de colocacioacuten de las reglas A continuacioacuten se exponen varias posibilidades de especificacioacuten y se comenta el analizador generado en cada caso

bull Especificacioacuten primeraSe transcriben literalmente las reglas tal y como se han escrito antes en la notacioacuten BNF noacutetese que para poder codificar la parte derecha de la regla vaciacutea ha de incluirse una accioacuten sintaacutectica vaciacutea

PARSER_BEGIN (Igualdad)

public class Igualdad

public static void main (String[] argum) throws ParseException Igualdad analisis = new Igualdad(Systemin) analisissecuencia() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (Igualdad)

void secuencia() lista() ltEOFgt

void lista() lt nombre gt otrosNombres() = lt numero gt

void otrosNombres() lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt ) |

TOKEN lt nombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN lt numero ( [ 0 - 9 ] )+ gt

SKIP | r | n

38

El analizador generado funciona correctamente puesto que en el meacutetodo otrosNombres se selec-ciona la alternativa vaciacutea despueacutes de haber comprobado que la pieza por adelantado no es siacutembolo ini-cial de ninguna de las dos alternativas precedentes (no es una coma ni un siacutembolo dos puntos)

bull Especificacioacuten segundaSe considera la misma especificacioacuten anterior pero cambiando el orden de colocacioacuten de las reglas ahora la produccioacuten vaciacutea se coloca como primera alternativa (soacutelo se muestran las partes modifica-das)

void otrosNombres() | lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt )

Con la entrada de esta especificacioacuten el generador JavaCC produce un mensaje en el que se avisa de que las dos alternativas que estaacuten colocadas detraacutes de la alternativa vaciacutea nunca podraacuten seleccionarse aun asiacute se genera un analizador Pero es un analizador que funciona incorrectamente asiacute por ejemplo la entrada

mxa nyz = 57

se considera incorrecta despueacutes de visto el primer nombre se intenta aplicar la produccioacuten vaciacutea (se espera encontrar el siacutembolo igual) pero habriacutea que aplicar la regla cuyo siacutembolo director es la coma

bull Especificacioacuten terceraSe utilizan las posibilidades de la notacioacuten BNF-Ampliada para prescindir de la especificacioacuten expliacuteci-ta de la palabra vaciacutea se aprovecha que JavaCC admite el operador + para indicar repeticiones de una o maacutes veces la alternativa vaciacutea estaacute impliacutecitamente contemplada en la posibilidad de cero repeti-ciones que contempla el operador (soacutelo se muestran las partes modificadas en la especificacioacuten)

void otrosNombres() ( lt nombre gt )+ | ( lt nombre gt )

El meacutetodo otrosNombres() generado hace las comprobaciones en el siguiente orden

- se comprueba si la pieza por adelantado es una coma (siacutembolo inicial de la primera alternativa) y en este caso se selecciona la primera regla- si no es una coma se comprueba si es un siacutembolo dos puntos (siacutembolo inicial de la segunda alter-nativa) y en este caso se selecciona la segunda regla- si no es un siacutembolo dos puntos se considera la palabra vaciacutea como siacutembolo inicial (siempre posi-ble impliacutecitamente) para la segunda alternativa en este caso para que no haya error la pieza por adelantado ha de ser un siacutembolo igual

Se trata pues de un analizador que funciona correctamente

bull Especificacioacuten cuarta

39

Se pone la misma especificacioacuten anterior pero cambiando el orden de consideracioacuten de la regla vaciacutea ahora se incluye impliacutecitamente en la primera alternativa (soacutelo se muestran las partes modificadas)

void otrosNombres() ( lt nombre gt ) | ( lt nombre gt )+

Para esta especificacioacuten el generador JavaCC emite un aviso en el que se indica que la segunda alter -nativa nunca se seleccionaraacute no obstante se genera un analizador que no funciona como se supone que deberiacutea hacerlo por ejemplo el anaacutelisis de la entrada

nyz mxa = 1

produce un error despueacutes de tratado el primer nombre se intenta aplicar la produccioacuten vaciacutea (corres-pondiente a la repeticioacuten de cero veces contemplada en la primera regla) por lo que no se encuentra el esperado siacutembolo igual

40

Page 35: JavaCC

static void CommonTokenAction(Token pieza) linea = linea + piezaimage

TOKEN lt variable [a-z] gt

TOKEN lt constante ( [0-9] ) + gt

SKIP lt | t | r gt linea = linea + image

SKIP n numLin++ Systemoutprintln(numLin + + linea) linea =

A modo de recapitulacioacutenↄ Accioacuten lexicograacutefica Accioacuten sintaacutectica

bull accioacuten lexicograacutefica bloque de coacutedigo Java asociado a una pieza sintaacutectica nominal (TOKEN) o a una secuencia de caracteres (SKIP) se ejecuta cuando se detecta en la entrada la pieza o la secuencia corres-pondiente no se puede aplicar a piezas sintaacutecticas anoacutenimas (autodefinidas con comillas)bull accioacuten sintaacutectica bloque de coacutedigo Java asociado a un punto de la estructura sintaacutectica (el punto indica-do por el sitio de la produccioacuten donde se inserta el bloque) se ejecuta cuando el anaacutelisis de la entrada pa-sa por ese punto de la estructura

ↄ Valor de una pieza sintaacutectica comunicadaEl analizador lexicograacutefico comunica al analizador sintaacutectico una pieza a traveacutes de un valor de la clase Token (fichero Tokenjava) un objeto de la clase Token representa las caracteriacutesticas de la pieza comunicada En la clase Token entre otros se tienen los campos

String imagelexema de la pieza

int beginLine beginColumn endLine endColumnposicioacuten del lexema en el fichero analizado (indicada por los nuacutemeros de fila y de columna de su comienzo y de su terminacioacuten)

ↄ Valor asociado a un siacutembolo

34

En las producciones que especifican la sintaxis se pueden incluir asignaciones que sirven para dejar ano-tado el valor asociado a un siacutembolo

bull siacutembolo no terminal valor = nombreSimbolo()el meacutetodo de anaacutelisis asociado a nombreSimbolo se ha declarado de un cierto tipo (distinto del tipo void) valor ha de ser una variable del mismo tipo que el meacutetodo

bull siacutembolo terminal dato = lt nombrePieza gtel valor asignado es el valor de la clase Token representativo de la pieza sintaacutectica (nominal) dato ha de ser una variable de tipo Token

ↄ Miradas sobre la pieza sintaacutectica comunicadaLa pieza sintaacutectica comunicada puede mirarse desde cada uno de los dos analizadores

bull analizador sintaacutecticola pieza comunicada (desde el analizador lexicograacutefico) se tiene anotada en un campo de la clase del analizador sintaacutectico declarado como

(static) public Token token

este valor es accesible desde el coacutedigo de todos los meacutetodos de anaacutelisis sintaacutectico el contenido del campo variacutea cuando se recibe una nueva pieza sintaacutectica

bull analizador lexicograacuteficola pieza dispuesta para ser comunicada (al analizador sintaacutectico) se tiene anotada en una variable local declarada como

Token matchedToken

este valor es accesible para el coacutedigo de todas las acciones lexicograacuteficas

ↄ El lexema de la pieza comunicadaEl lexema de la pieza sintaacutectica comunicada se encuentra disponible en dos sitios distintos que compar-ten el mismo nombre pero que son de distinto tipo y se usan de distinta manera

bull String image

es un campo de la clase TOKEN puede consultarse en relacioacuten con un objeto de esa clase que esteacute disponible

bull StringBuffer image

es un campo de la clase del analizador lexicograacutefico ( ∙ ∙ ∙ ∙ TokenManagerjava) es accesible desde el coacutedigo de la clase y por lo tanto desde todas las acciones lexicograacuteficas (bloques de coacute-digo que estaacuten incorporados al analizador lexicograacutefico)

se trata de un valor disponible tanto para las piezas sintaacutecticas nominales (TOKEN) como para las secuencias que se saltan (SKIP)

ↄ Resumen relativo a la lexicografiacutea

TOKEN pieza sintaacutectica nominal∙ ∙ ∙ pieza sintaacutectica anoacutenimaSKIP secuencia de caracteres que no forman pieza

35

Precisiones sobre el analizador sintaacutectico generadoPuede decirse que en su funcionamiento maacutes sencillo cuando no se emplean las posibilidades de examen por adelantado (ldquolookaheadrdquo) JavaCC comprueba que las reglas sintaacutecticas cumplen la condicioacuten LL(1) y generan un analizador descendente-predictivo-recursivo Pero esta apreciacioacuten hay que matizarla ya que el analizador sintaacutectico generado no se ajusta en sentido estricto al modelo de analizador LL(1)

En el modelo de anaacutelisis LL(1) el orden del examen de las distintas alternativas de expansioacuten de un siacutembo-lo no terminal es indiferente ya que se conocen los siacutembolos directores de cada una de ellas (que resultan ser conjuntos disjuntos) en la implementacioacuten del subprograma de anaacutelisis asociado al siacutembolo no terminal ltNombreSimbgt cuyas producciones son

ltNombreSimbgt = α1

| α2

∙ ∙ ∙

| αn

se podriacutea preguntar en cualquier orden si la pieza actual pertenece al conjunto de siacutembolos directores de las distintas reglas Sin embargo en el analizador sintaacutectico generado por JavaCC la implementacioacuten del meacute-todo asociado al siacutembolo no terminal ltNombreSimbgt no sigue esa pauta

Si las anteriores reglas sintaacutecticas se transcriben en JavaCC de la forma

void nombreSimb () α1-jcc

| α2-jcc

∙ ∙ ∙

| αn-jcc

1

accioacutenlexicograacutefica

comuacuten

Commom -TokenAction2

accioacutenlexicograacutefica

propia

(accioacuten ligadaa una pieza)3

bloque dedeclaracioneslexicograacuteficas

TOKEN_MGR_DECLS4

declaracioneslexicograacuteficaspredefinidas

(disponibilidad

de uso)TOKENsisisisi∙ ∙ ∙sino--SKIPnosisisi

Las indicaciones si de las columnas 3 y 4 son consecuencia de las indicaciones de la columna 2

La accioacuten lexicograacutefica comuacuten se ejecuta despueacutes de la accioacuten lexicograacutefica propia

36

el meacutetodo de anaacutelisis generado procede de la siguiente manera

se van considerando las alternativas sucesivamente en el mismo orden en el que estaacuten especifica-das primero la de α1 (α1-jcc) despueacutes la de α2 (α2-jcc) etc

para cada alternativa seleccionada se comprueba si la pieza por adelantado coincide con alguno de los siacutembolos iniciales de la parte derecha y si hay coincidencia se prosigue el anaacutelisis con ella si no hay coincidencia se pasa a la alternativa siguiente

siguiendo con este orden si se llega al final de las alternativas sin haberse encontrado coincidencia alguna se produce un error sintaacutectico

En el analizador generado por JavaCC hay una peculiaridad que no se tiene en los analizadores LL(1) es-trictamente considerados la palabra vaciacutea siacute se considera como posible siacutembolo inicial de la parte derecha una regla (esto ocurre siempre que la parte derecha es anulable) De esta peculiaridad se deriva una conse-cuencia que ha de tenerse en cuenta al escribir la especificacioacuten dado que siempre es posible considerar la palabra vaciacutea como la siguiente pieza por adelantado si se llega a examinar la alternativa correspondiente a una produccioacuten anulable el analizador generado siempre seleccionaraacute esa alternativa con independencia de la siguiente pieza que esteacute presente en la entrada que queda por analizar

Asiacute pues si alguna de las producciones que definen un siacutembolo no terminal es anulable es importante el or-den en que se escriben las alternativas en una especificacioacuten sintaacutectica JavaCC el funcionamiento del ana-lizador generado siacute depende de la colocacioacuten elegida para las reglas

Para ilustrar estas peculiaridades del analizador sintaacutectico generado por JavaCC a continuacioacuten se expone un ejemplo

∆ Ejemplo 9

Se quiere obtener un analizador leacutexico-sintaacutectico de textos que se ajustan al siguiente formato una se-cuencia de uno o maacutes nombres seguida del siacutembolo igual seguido de un nuacutemero entero si la secuencia tiene maacutes de un nombre han de estar separados bien por una coma en todos los casos o bien por el siacutem-bolo dos puntos en todos los casos esto es son correctas las secuencias

uno dos tres = 123exclusivo = 0Abajo Arriba = 57

y son incorrectas las secuenciasuno dos tres = 123

exclusivo inclusive = 0

fuera dentro = 56

Una gramaacutetica que define las secuencias con este formato es

ltListagt = nombre ltOtrosNombresgt = numero

ltOtrosNombresgt = nombre nombre | nombre nombre | ε

se trata de una gramaacutetica que cumple la condicioacuten LL(1)- el siacutembolo director para la alternativa vaciacutea es el siacutembolo igual es evidente pues que las tres reglas de ltOtrosNombresgt tienen siacutembolos directores distintos

37

- la decisioacuten de abandonar la repeticioacuten del anaacutelisis de un nuevo nombre (en las dos primeras alterna-tivas de ltOtrosNombresgt) se puede tomar ante la presencia del siacutembolo igual

En la implementacioacuten de un analizador sintaacutectico LL(1) en sentido estricto la seleccioacuten de la alternativa vaciacutea para expandir el siacutembolo ltOtrosNombresgt se hariacutea tras comprobar expliacutecitamente que la pie-za sintaacutectica por adelantado es el siacutembolo igual

El funcionamiento del analizador generado por JavaCC depende del orden de colocacioacuten de las reglas A continuacioacuten se exponen varias posibilidades de especificacioacuten y se comenta el analizador generado en cada caso

bull Especificacioacuten primeraSe transcriben literalmente las reglas tal y como se han escrito antes en la notacioacuten BNF noacutetese que para poder codificar la parte derecha de la regla vaciacutea ha de incluirse una accioacuten sintaacutectica vaciacutea

PARSER_BEGIN (Igualdad)

public class Igualdad

public static void main (String[] argum) throws ParseException Igualdad analisis = new Igualdad(Systemin) analisissecuencia() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (Igualdad)

void secuencia() lista() ltEOFgt

void lista() lt nombre gt otrosNombres() = lt numero gt

void otrosNombres() lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt ) |

TOKEN lt nombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN lt numero ( [ 0 - 9 ] )+ gt

SKIP | r | n

38

El analizador generado funciona correctamente puesto que en el meacutetodo otrosNombres se selec-ciona la alternativa vaciacutea despueacutes de haber comprobado que la pieza por adelantado no es siacutembolo ini-cial de ninguna de las dos alternativas precedentes (no es una coma ni un siacutembolo dos puntos)

bull Especificacioacuten segundaSe considera la misma especificacioacuten anterior pero cambiando el orden de colocacioacuten de las reglas ahora la produccioacuten vaciacutea se coloca como primera alternativa (soacutelo se muestran las partes modifica-das)

void otrosNombres() | lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt )

Con la entrada de esta especificacioacuten el generador JavaCC produce un mensaje en el que se avisa de que las dos alternativas que estaacuten colocadas detraacutes de la alternativa vaciacutea nunca podraacuten seleccionarse aun asiacute se genera un analizador Pero es un analizador que funciona incorrectamente asiacute por ejemplo la entrada

mxa nyz = 57

se considera incorrecta despueacutes de visto el primer nombre se intenta aplicar la produccioacuten vaciacutea (se espera encontrar el siacutembolo igual) pero habriacutea que aplicar la regla cuyo siacutembolo director es la coma

bull Especificacioacuten terceraSe utilizan las posibilidades de la notacioacuten BNF-Ampliada para prescindir de la especificacioacuten expliacuteci-ta de la palabra vaciacutea se aprovecha que JavaCC admite el operador + para indicar repeticiones de una o maacutes veces la alternativa vaciacutea estaacute impliacutecitamente contemplada en la posibilidad de cero repeti-ciones que contempla el operador (soacutelo se muestran las partes modificadas en la especificacioacuten)

void otrosNombres() ( lt nombre gt )+ | ( lt nombre gt )

El meacutetodo otrosNombres() generado hace las comprobaciones en el siguiente orden

- se comprueba si la pieza por adelantado es una coma (siacutembolo inicial de la primera alternativa) y en este caso se selecciona la primera regla- si no es una coma se comprueba si es un siacutembolo dos puntos (siacutembolo inicial de la segunda alter-nativa) y en este caso se selecciona la segunda regla- si no es un siacutembolo dos puntos se considera la palabra vaciacutea como siacutembolo inicial (siempre posi-ble impliacutecitamente) para la segunda alternativa en este caso para que no haya error la pieza por adelantado ha de ser un siacutembolo igual

Se trata pues de un analizador que funciona correctamente

bull Especificacioacuten cuarta

39

Se pone la misma especificacioacuten anterior pero cambiando el orden de consideracioacuten de la regla vaciacutea ahora se incluye impliacutecitamente en la primera alternativa (soacutelo se muestran las partes modificadas)

void otrosNombres() ( lt nombre gt ) | ( lt nombre gt )+

Para esta especificacioacuten el generador JavaCC emite un aviso en el que se indica que la segunda alter -nativa nunca se seleccionaraacute no obstante se genera un analizador que no funciona como se supone que deberiacutea hacerlo por ejemplo el anaacutelisis de la entrada

nyz mxa = 1

produce un error despueacutes de tratado el primer nombre se intenta aplicar la produccioacuten vaciacutea (corres-pondiente a la repeticioacuten de cero veces contemplada en la primera regla) por lo que no se encuentra el esperado siacutembolo igual

40

Page 36: JavaCC

En las producciones que especifican la sintaxis se pueden incluir asignaciones que sirven para dejar ano-tado el valor asociado a un siacutembolo

bull siacutembolo no terminal valor = nombreSimbolo()el meacutetodo de anaacutelisis asociado a nombreSimbolo se ha declarado de un cierto tipo (distinto del tipo void) valor ha de ser una variable del mismo tipo que el meacutetodo

bull siacutembolo terminal dato = lt nombrePieza gtel valor asignado es el valor de la clase Token representativo de la pieza sintaacutectica (nominal) dato ha de ser una variable de tipo Token

ↄ Miradas sobre la pieza sintaacutectica comunicadaLa pieza sintaacutectica comunicada puede mirarse desde cada uno de los dos analizadores

bull analizador sintaacutecticola pieza comunicada (desde el analizador lexicograacutefico) se tiene anotada en un campo de la clase del analizador sintaacutectico declarado como

(static) public Token token

este valor es accesible desde el coacutedigo de todos los meacutetodos de anaacutelisis sintaacutectico el contenido del campo variacutea cuando se recibe una nueva pieza sintaacutectica

bull analizador lexicograacuteficola pieza dispuesta para ser comunicada (al analizador sintaacutectico) se tiene anotada en una variable local declarada como

Token matchedToken

este valor es accesible para el coacutedigo de todas las acciones lexicograacuteficas

ↄ El lexema de la pieza comunicadaEl lexema de la pieza sintaacutectica comunicada se encuentra disponible en dos sitios distintos que compar-ten el mismo nombre pero que son de distinto tipo y se usan de distinta manera

bull String image

es un campo de la clase TOKEN puede consultarse en relacioacuten con un objeto de esa clase que esteacute disponible

bull StringBuffer image

es un campo de la clase del analizador lexicograacutefico ( ∙ ∙ ∙ ∙ TokenManagerjava) es accesible desde el coacutedigo de la clase y por lo tanto desde todas las acciones lexicograacuteficas (bloques de coacute-digo que estaacuten incorporados al analizador lexicograacutefico)

se trata de un valor disponible tanto para las piezas sintaacutecticas nominales (TOKEN) como para las secuencias que se saltan (SKIP)

ↄ Resumen relativo a la lexicografiacutea

TOKEN pieza sintaacutectica nominal∙ ∙ ∙ pieza sintaacutectica anoacutenimaSKIP secuencia de caracteres que no forman pieza

35

Precisiones sobre el analizador sintaacutectico generadoPuede decirse que en su funcionamiento maacutes sencillo cuando no se emplean las posibilidades de examen por adelantado (ldquolookaheadrdquo) JavaCC comprueba que las reglas sintaacutecticas cumplen la condicioacuten LL(1) y generan un analizador descendente-predictivo-recursivo Pero esta apreciacioacuten hay que matizarla ya que el analizador sintaacutectico generado no se ajusta en sentido estricto al modelo de analizador LL(1)

En el modelo de anaacutelisis LL(1) el orden del examen de las distintas alternativas de expansioacuten de un siacutembo-lo no terminal es indiferente ya que se conocen los siacutembolos directores de cada una de ellas (que resultan ser conjuntos disjuntos) en la implementacioacuten del subprograma de anaacutelisis asociado al siacutembolo no terminal ltNombreSimbgt cuyas producciones son

ltNombreSimbgt = α1

| α2

∙ ∙ ∙

| αn

se podriacutea preguntar en cualquier orden si la pieza actual pertenece al conjunto de siacutembolos directores de las distintas reglas Sin embargo en el analizador sintaacutectico generado por JavaCC la implementacioacuten del meacute-todo asociado al siacutembolo no terminal ltNombreSimbgt no sigue esa pauta

Si las anteriores reglas sintaacutecticas se transcriben en JavaCC de la forma

void nombreSimb () α1-jcc

| α2-jcc

∙ ∙ ∙

| αn-jcc

1

accioacutenlexicograacutefica

comuacuten

Commom -TokenAction2

accioacutenlexicograacutefica

propia

(accioacuten ligadaa una pieza)3

bloque dedeclaracioneslexicograacuteficas

TOKEN_MGR_DECLS4

declaracioneslexicograacuteficaspredefinidas

(disponibilidad

de uso)TOKENsisisisi∙ ∙ ∙sino--SKIPnosisisi

Las indicaciones si de las columnas 3 y 4 son consecuencia de las indicaciones de la columna 2

La accioacuten lexicograacutefica comuacuten se ejecuta despueacutes de la accioacuten lexicograacutefica propia

36

el meacutetodo de anaacutelisis generado procede de la siguiente manera

se van considerando las alternativas sucesivamente en el mismo orden en el que estaacuten especifica-das primero la de α1 (α1-jcc) despueacutes la de α2 (α2-jcc) etc

para cada alternativa seleccionada se comprueba si la pieza por adelantado coincide con alguno de los siacutembolos iniciales de la parte derecha y si hay coincidencia se prosigue el anaacutelisis con ella si no hay coincidencia se pasa a la alternativa siguiente

siguiendo con este orden si se llega al final de las alternativas sin haberse encontrado coincidencia alguna se produce un error sintaacutectico

En el analizador generado por JavaCC hay una peculiaridad que no se tiene en los analizadores LL(1) es-trictamente considerados la palabra vaciacutea siacute se considera como posible siacutembolo inicial de la parte derecha una regla (esto ocurre siempre que la parte derecha es anulable) De esta peculiaridad se deriva una conse-cuencia que ha de tenerse en cuenta al escribir la especificacioacuten dado que siempre es posible considerar la palabra vaciacutea como la siguiente pieza por adelantado si se llega a examinar la alternativa correspondiente a una produccioacuten anulable el analizador generado siempre seleccionaraacute esa alternativa con independencia de la siguiente pieza que esteacute presente en la entrada que queda por analizar

Asiacute pues si alguna de las producciones que definen un siacutembolo no terminal es anulable es importante el or-den en que se escriben las alternativas en una especificacioacuten sintaacutectica JavaCC el funcionamiento del ana-lizador generado siacute depende de la colocacioacuten elegida para las reglas

Para ilustrar estas peculiaridades del analizador sintaacutectico generado por JavaCC a continuacioacuten se expone un ejemplo

∆ Ejemplo 9

Se quiere obtener un analizador leacutexico-sintaacutectico de textos que se ajustan al siguiente formato una se-cuencia de uno o maacutes nombres seguida del siacutembolo igual seguido de un nuacutemero entero si la secuencia tiene maacutes de un nombre han de estar separados bien por una coma en todos los casos o bien por el siacutem-bolo dos puntos en todos los casos esto es son correctas las secuencias

uno dos tres = 123exclusivo = 0Abajo Arriba = 57

y son incorrectas las secuenciasuno dos tres = 123

exclusivo inclusive = 0

fuera dentro = 56

Una gramaacutetica que define las secuencias con este formato es

ltListagt = nombre ltOtrosNombresgt = numero

ltOtrosNombresgt = nombre nombre | nombre nombre | ε

se trata de una gramaacutetica que cumple la condicioacuten LL(1)- el siacutembolo director para la alternativa vaciacutea es el siacutembolo igual es evidente pues que las tres reglas de ltOtrosNombresgt tienen siacutembolos directores distintos

37

- la decisioacuten de abandonar la repeticioacuten del anaacutelisis de un nuevo nombre (en las dos primeras alterna-tivas de ltOtrosNombresgt) se puede tomar ante la presencia del siacutembolo igual

En la implementacioacuten de un analizador sintaacutectico LL(1) en sentido estricto la seleccioacuten de la alternativa vaciacutea para expandir el siacutembolo ltOtrosNombresgt se hariacutea tras comprobar expliacutecitamente que la pie-za sintaacutectica por adelantado es el siacutembolo igual

El funcionamiento del analizador generado por JavaCC depende del orden de colocacioacuten de las reglas A continuacioacuten se exponen varias posibilidades de especificacioacuten y se comenta el analizador generado en cada caso

bull Especificacioacuten primeraSe transcriben literalmente las reglas tal y como se han escrito antes en la notacioacuten BNF noacutetese que para poder codificar la parte derecha de la regla vaciacutea ha de incluirse una accioacuten sintaacutectica vaciacutea

PARSER_BEGIN (Igualdad)

public class Igualdad

public static void main (String[] argum) throws ParseException Igualdad analisis = new Igualdad(Systemin) analisissecuencia() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (Igualdad)

void secuencia() lista() ltEOFgt

void lista() lt nombre gt otrosNombres() = lt numero gt

void otrosNombres() lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt ) |

TOKEN lt nombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN lt numero ( [ 0 - 9 ] )+ gt

SKIP | r | n

38

El analizador generado funciona correctamente puesto que en el meacutetodo otrosNombres se selec-ciona la alternativa vaciacutea despueacutes de haber comprobado que la pieza por adelantado no es siacutembolo ini-cial de ninguna de las dos alternativas precedentes (no es una coma ni un siacutembolo dos puntos)

bull Especificacioacuten segundaSe considera la misma especificacioacuten anterior pero cambiando el orden de colocacioacuten de las reglas ahora la produccioacuten vaciacutea se coloca como primera alternativa (soacutelo se muestran las partes modifica-das)

void otrosNombres() | lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt )

Con la entrada de esta especificacioacuten el generador JavaCC produce un mensaje en el que se avisa de que las dos alternativas que estaacuten colocadas detraacutes de la alternativa vaciacutea nunca podraacuten seleccionarse aun asiacute se genera un analizador Pero es un analizador que funciona incorrectamente asiacute por ejemplo la entrada

mxa nyz = 57

se considera incorrecta despueacutes de visto el primer nombre se intenta aplicar la produccioacuten vaciacutea (se espera encontrar el siacutembolo igual) pero habriacutea que aplicar la regla cuyo siacutembolo director es la coma

bull Especificacioacuten terceraSe utilizan las posibilidades de la notacioacuten BNF-Ampliada para prescindir de la especificacioacuten expliacuteci-ta de la palabra vaciacutea se aprovecha que JavaCC admite el operador + para indicar repeticiones de una o maacutes veces la alternativa vaciacutea estaacute impliacutecitamente contemplada en la posibilidad de cero repeti-ciones que contempla el operador (soacutelo se muestran las partes modificadas en la especificacioacuten)

void otrosNombres() ( lt nombre gt )+ | ( lt nombre gt )

El meacutetodo otrosNombres() generado hace las comprobaciones en el siguiente orden

- se comprueba si la pieza por adelantado es una coma (siacutembolo inicial de la primera alternativa) y en este caso se selecciona la primera regla- si no es una coma se comprueba si es un siacutembolo dos puntos (siacutembolo inicial de la segunda alter-nativa) y en este caso se selecciona la segunda regla- si no es un siacutembolo dos puntos se considera la palabra vaciacutea como siacutembolo inicial (siempre posi-ble impliacutecitamente) para la segunda alternativa en este caso para que no haya error la pieza por adelantado ha de ser un siacutembolo igual

Se trata pues de un analizador que funciona correctamente

bull Especificacioacuten cuarta

39

Se pone la misma especificacioacuten anterior pero cambiando el orden de consideracioacuten de la regla vaciacutea ahora se incluye impliacutecitamente en la primera alternativa (soacutelo se muestran las partes modificadas)

void otrosNombres() ( lt nombre gt ) | ( lt nombre gt )+

Para esta especificacioacuten el generador JavaCC emite un aviso en el que se indica que la segunda alter -nativa nunca se seleccionaraacute no obstante se genera un analizador que no funciona como se supone que deberiacutea hacerlo por ejemplo el anaacutelisis de la entrada

nyz mxa = 1

produce un error despueacutes de tratado el primer nombre se intenta aplicar la produccioacuten vaciacutea (corres-pondiente a la repeticioacuten de cero veces contemplada en la primera regla) por lo que no se encuentra el esperado siacutembolo igual

40

Page 37: JavaCC

Precisiones sobre el analizador sintaacutectico generadoPuede decirse que en su funcionamiento maacutes sencillo cuando no se emplean las posibilidades de examen por adelantado (ldquolookaheadrdquo) JavaCC comprueba que las reglas sintaacutecticas cumplen la condicioacuten LL(1) y generan un analizador descendente-predictivo-recursivo Pero esta apreciacioacuten hay que matizarla ya que el analizador sintaacutectico generado no se ajusta en sentido estricto al modelo de analizador LL(1)

En el modelo de anaacutelisis LL(1) el orden del examen de las distintas alternativas de expansioacuten de un siacutembo-lo no terminal es indiferente ya que se conocen los siacutembolos directores de cada una de ellas (que resultan ser conjuntos disjuntos) en la implementacioacuten del subprograma de anaacutelisis asociado al siacutembolo no terminal ltNombreSimbgt cuyas producciones son

ltNombreSimbgt = α1

| α2

∙ ∙ ∙

| αn

se podriacutea preguntar en cualquier orden si la pieza actual pertenece al conjunto de siacutembolos directores de las distintas reglas Sin embargo en el analizador sintaacutectico generado por JavaCC la implementacioacuten del meacute-todo asociado al siacutembolo no terminal ltNombreSimbgt no sigue esa pauta

Si las anteriores reglas sintaacutecticas se transcriben en JavaCC de la forma

void nombreSimb () α1-jcc

| α2-jcc

∙ ∙ ∙

| αn-jcc

1

accioacutenlexicograacutefica

comuacuten

Commom -TokenAction2

accioacutenlexicograacutefica

propia

(accioacuten ligadaa una pieza)3

bloque dedeclaracioneslexicograacuteficas

TOKEN_MGR_DECLS4

declaracioneslexicograacuteficaspredefinidas

(disponibilidad

de uso)TOKENsisisisi∙ ∙ ∙sino--SKIPnosisisi

Las indicaciones si de las columnas 3 y 4 son consecuencia de las indicaciones de la columna 2

La accioacuten lexicograacutefica comuacuten se ejecuta despueacutes de la accioacuten lexicograacutefica propia

36

el meacutetodo de anaacutelisis generado procede de la siguiente manera

se van considerando las alternativas sucesivamente en el mismo orden en el que estaacuten especifica-das primero la de α1 (α1-jcc) despueacutes la de α2 (α2-jcc) etc

para cada alternativa seleccionada se comprueba si la pieza por adelantado coincide con alguno de los siacutembolos iniciales de la parte derecha y si hay coincidencia se prosigue el anaacutelisis con ella si no hay coincidencia se pasa a la alternativa siguiente

siguiendo con este orden si se llega al final de las alternativas sin haberse encontrado coincidencia alguna se produce un error sintaacutectico

En el analizador generado por JavaCC hay una peculiaridad que no se tiene en los analizadores LL(1) es-trictamente considerados la palabra vaciacutea siacute se considera como posible siacutembolo inicial de la parte derecha una regla (esto ocurre siempre que la parte derecha es anulable) De esta peculiaridad se deriva una conse-cuencia que ha de tenerse en cuenta al escribir la especificacioacuten dado que siempre es posible considerar la palabra vaciacutea como la siguiente pieza por adelantado si se llega a examinar la alternativa correspondiente a una produccioacuten anulable el analizador generado siempre seleccionaraacute esa alternativa con independencia de la siguiente pieza que esteacute presente en la entrada que queda por analizar

Asiacute pues si alguna de las producciones que definen un siacutembolo no terminal es anulable es importante el or-den en que se escriben las alternativas en una especificacioacuten sintaacutectica JavaCC el funcionamiento del ana-lizador generado siacute depende de la colocacioacuten elegida para las reglas

Para ilustrar estas peculiaridades del analizador sintaacutectico generado por JavaCC a continuacioacuten se expone un ejemplo

∆ Ejemplo 9

Se quiere obtener un analizador leacutexico-sintaacutectico de textos que se ajustan al siguiente formato una se-cuencia de uno o maacutes nombres seguida del siacutembolo igual seguido de un nuacutemero entero si la secuencia tiene maacutes de un nombre han de estar separados bien por una coma en todos los casos o bien por el siacutem-bolo dos puntos en todos los casos esto es son correctas las secuencias

uno dos tres = 123exclusivo = 0Abajo Arriba = 57

y son incorrectas las secuenciasuno dos tres = 123

exclusivo inclusive = 0

fuera dentro = 56

Una gramaacutetica que define las secuencias con este formato es

ltListagt = nombre ltOtrosNombresgt = numero

ltOtrosNombresgt = nombre nombre | nombre nombre | ε

se trata de una gramaacutetica que cumple la condicioacuten LL(1)- el siacutembolo director para la alternativa vaciacutea es el siacutembolo igual es evidente pues que las tres reglas de ltOtrosNombresgt tienen siacutembolos directores distintos

37

- la decisioacuten de abandonar la repeticioacuten del anaacutelisis de un nuevo nombre (en las dos primeras alterna-tivas de ltOtrosNombresgt) se puede tomar ante la presencia del siacutembolo igual

En la implementacioacuten de un analizador sintaacutectico LL(1) en sentido estricto la seleccioacuten de la alternativa vaciacutea para expandir el siacutembolo ltOtrosNombresgt se hariacutea tras comprobar expliacutecitamente que la pie-za sintaacutectica por adelantado es el siacutembolo igual

El funcionamiento del analizador generado por JavaCC depende del orden de colocacioacuten de las reglas A continuacioacuten se exponen varias posibilidades de especificacioacuten y se comenta el analizador generado en cada caso

bull Especificacioacuten primeraSe transcriben literalmente las reglas tal y como se han escrito antes en la notacioacuten BNF noacutetese que para poder codificar la parte derecha de la regla vaciacutea ha de incluirse una accioacuten sintaacutectica vaciacutea

PARSER_BEGIN (Igualdad)

public class Igualdad

public static void main (String[] argum) throws ParseException Igualdad analisis = new Igualdad(Systemin) analisissecuencia() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (Igualdad)

void secuencia() lista() ltEOFgt

void lista() lt nombre gt otrosNombres() = lt numero gt

void otrosNombres() lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt ) |

TOKEN lt nombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN lt numero ( [ 0 - 9 ] )+ gt

SKIP | r | n

38

El analizador generado funciona correctamente puesto que en el meacutetodo otrosNombres se selec-ciona la alternativa vaciacutea despueacutes de haber comprobado que la pieza por adelantado no es siacutembolo ini-cial de ninguna de las dos alternativas precedentes (no es una coma ni un siacutembolo dos puntos)

bull Especificacioacuten segundaSe considera la misma especificacioacuten anterior pero cambiando el orden de colocacioacuten de las reglas ahora la produccioacuten vaciacutea se coloca como primera alternativa (soacutelo se muestran las partes modifica-das)

void otrosNombres() | lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt )

Con la entrada de esta especificacioacuten el generador JavaCC produce un mensaje en el que se avisa de que las dos alternativas que estaacuten colocadas detraacutes de la alternativa vaciacutea nunca podraacuten seleccionarse aun asiacute se genera un analizador Pero es un analizador que funciona incorrectamente asiacute por ejemplo la entrada

mxa nyz = 57

se considera incorrecta despueacutes de visto el primer nombre se intenta aplicar la produccioacuten vaciacutea (se espera encontrar el siacutembolo igual) pero habriacutea que aplicar la regla cuyo siacutembolo director es la coma

bull Especificacioacuten terceraSe utilizan las posibilidades de la notacioacuten BNF-Ampliada para prescindir de la especificacioacuten expliacuteci-ta de la palabra vaciacutea se aprovecha que JavaCC admite el operador + para indicar repeticiones de una o maacutes veces la alternativa vaciacutea estaacute impliacutecitamente contemplada en la posibilidad de cero repeti-ciones que contempla el operador (soacutelo se muestran las partes modificadas en la especificacioacuten)

void otrosNombres() ( lt nombre gt )+ | ( lt nombre gt )

El meacutetodo otrosNombres() generado hace las comprobaciones en el siguiente orden

- se comprueba si la pieza por adelantado es una coma (siacutembolo inicial de la primera alternativa) y en este caso se selecciona la primera regla- si no es una coma se comprueba si es un siacutembolo dos puntos (siacutembolo inicial de la segunda alter-nativa) y en este caso se selecciona la segunda regla- si no es un siacutembolo dos puntos se considera la palabra vaciacutea como siacutembolo inicial (siempre posi-ble impliacutecitamente) para la segunda alternativa en este caso para que no haya error la pieza por adelantado ha de ser un siacutembolo igual

Se trata pues de un analizador que funciona correctamente

bull Especificacioacuten cuarta

39

Se pone la misma especificacioacuten anterior pero cambiando el orden de consideracioacuten de la regla vaciacutea ahora se incluye impliacutecitamente en la primera alternativa (soacutelo se muestran las partes modificadas)

void otrosNombres() ( lt nombre gt ) | ( lt nombre gt )+

Para esta especificacioacuten el generador JavaCC emite un aviso en el que se indica que la segunda alter -nativa nunca se seleccionaraacute no obstante se genera un analizador que no funciona como se supone que deberiacutea hacerlo por ejemplo el anaacutelisis de la entrada

nyz mxa = 1

produce un error despueacutes de tratado el primer nombre se intenta aplicar la produccioacuten vaciacutea (corres-pondiente a la repeticioacuten de cero veces contemplada en la primera regla) por lo que no se encuentra el esperado siacutembolo igual

40

Page 38: JavaCC

el meacutetodo de anaacutelisis generado procede de la siguiente manera

se van considerando las alternativas sucesivamente en el mismo orden en el que estaacuten especifica-das primero la de α1 (α1-jcc) despueacutes la de α2 (α2-jcc) etc

para cada alternativa seleccionada se comprueba si la pieza por adelantado coincide con alguno de los siacutembolos iniciales de la parte derecha y si hay coincidencia se prosigue el anaacutelisis con ella si no hay coincidencia se pasa a la alternativa siguiente

siguiendo con este orden si se llega al final de las alternativas sin haberse encontrado coincidencia alguna se produce un error sintaacutectico

En el analizador generado por JavaCC hay una peculiaridad que no se tiene en los analizadores LL(1) es-trictamente considerados la palabra vaciacutea siacute se considera como posible siacutembolo inicial de la parte derecha una regla (esto ocurre siempre que la parte derecha es anulable) De esta peculiaridad se deriva una conse-cuencia que ha de tenerse en cuenta al escribir la especificacioacuten dado que siempre es posible considerar la palabra vaciacutea como la siguiente pieza por adelantado si se llega a examinar la alternativa correspondiente a una produccioacuten anulable el analizador generado siempre seleccionaraacute esa alternativa con independencia de la siguiente pieza que esteacute presente en la entrada que queda por analizar

Asiacute pues si alguna de las producciones que definen un siacutembolo no terminal es anulable es importante el or-den en que se escriben las alternativas en una especificacioacuten sintaacutectica JavaCC el funcionamiento del ana-lizador generado siacute depende de la colocacioacuten elegida para las reglas

Para ilustrar estas peculiaridades del analizador sintaacutectico generado por JavaCC a continuacioacuten se expone un ejemplo

∆ Ejemplo 9

Se quiere obtener un analizador leacutexico-sintaacutectico de textos que se ajustan al siguiente formato una se-cuencia de uno o maacutes nombres seguida del siacutembolo igual seguido de un nuacutemero entero si la secuencia tiene maacutes de un nombre han de estar separados bien por una coma en todos los casos o bien por el siacutem-bolo dos puntos en todos los casos esto es son correctas las secuencias

uno dos tres = 123exclusivo = 0Abajo Arriba = 57

y son incorrectas las secuenciasuno dos tres = 123

exclusivo inclusive = 0

fuera dentro = 56

Una gramaacutetica que define las secuencias con este formato es

ltListagt = nombre ltOtrosNombresgt = numero

ltOtrosNombresgt = nombre nombre | nombre nombre | ε

se trata de una gramaacutetica que cumple la condicioacuten LL(1)- el siacutembolo director para la alternativa vaciacutea es el siacutembolo igual es evidente pues que las tres reglas de ltOtrosNombresgt tienen siacutembolos directores distintos

37

- la decisioacuten de abandonar la repeticioacuten del anaacutelisis de un nuevo nombre (en las dos primeras alterna-tivas de ltOtrosNombresgt) se puede tomar ante la presencia del siacutembolo igual

En la implementacioacuten de un analizador sintaacutectico LL(1) en sentido estricto la seleccioacuten de la alternativa vaciacutea para expandir el siacutembolo ltOtrosNombresgt se hariacutea tras comprobar expliacutecitamente que la pie-za sintaacutectica por adelantado es el siacutembolo igual

El funcionamiento del analizador generado por JavaCC depende del orden de colocacioacuten de las reglas A continuacioacuten se exponen varias posibilidades de especificacioacuten y se comenta el analizador generado en cada caso

bull Especificacioacuten primeraSe transcriben literalmente las reglas tal y como se han escrito antes en la notacioacuten BNF noacutetese que para poder codificar la parte derecha de la regla vaciacutea ha de incluirse una accioacuten sintaacutectica vaciacutea

PARSER_BEGIN (Igualdad)

public class Igualdad

public static void main (String[] argum) throws ParseException Igualdad analisis = new Igualdad(Systemin) analisissecuencia() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (Igualdad)

void secuencia() lista() ltEOFgt

void lista() lt nombre gt otrosNombres() = lt numero gt

void otrosNombres() lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt ) |

TOKEN lt nombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN lt numero ( [ 0 - 9 ] )+ gt

SKIP | r | n

38

El analizador generado funciona correctamente puesto que en el meacutetodo otrosNombres se selec-ciona la alternativa vaciacutea despueacutes de haber comprobado que la pieza por adelantado no es siacutembolo ini-cial de ninguna de las dos alternativas precedentes (no es una coma ni un siacutembolo dos puntos)

bull Especificacioacuten segundaSe considera la misma especificacioacuten anterior pero cambiando el orden de colocacioacuten de las reglas ahora la produccioacuten vaciacutea se coloca como primera alternativa (soacutelo se muestran las partes modifica-das)

void otrosNombres() | lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt )

Con la entrada de esta especificacioacuten el generador JavaCC produce un mensaje en el que se avisa de que las dos alternativas que estaacuten colocadas detraacutes de la alternativa vaciacutea nunca podraacuten seleccionarse aun asiacute se genera un analizador Pero es un analizador que funciona incorrectamente asiacute por ejemplo la entrada

mxa nyz = 57

se considera incorrecta despueacutes de visto el primer nombre se intenta aplicar la produccioacuten vaciacutea (se espera encontrar el siacutembolo igual) pero habriacutea que aplicar la regla cuyo siacutembolo director es la coma

bull Especificacioacuten terceraSe utilizan las posibilidades de la notacioacuten BNF-Ampliada para prescindir de la especificacioacuten expliacuteci-ta de la palabra vaciacutea se aprovecha que JavaCC admite el operador + para indicar repeticiones de una o maacutes veces la alternativa vaciacutea estaacute impliacutecitamente contemplada en la posibilidad de cero repeti-ciones que contempla el operador (soacutelo se muestran las partes modificadas en la especificacioacuten)

void otrosNombres() ( lt nombre gt )+ | ( lt nombre gt )

El meacutetodo otrosNombres() generado hace las comprobaciones en el siguiente orden

- se comprueba si la pieza por adelantado es una coma (siacutembolo inicial de la primera alternativa) y en este caso se selecciona la primera regla- si no es una coma se comprueba si es un siacutembolo dos puntos (siacutembolo inicial de la segunda alter-nativa) y en este caso se selecciona la segunda regla- si no es un siacutembolo dos puntos se considera la palabra vaciacutea como siacutembolo inicial (siempre posi-ble impliacutecitamente) para la segunda alternativa en este caso para que no haya error la pieza por adelantado ha de ser un siacutembolo igual

Se trata pues de un analizador que funciona correctamente

bull Especificacioacuten cuarta

39

Se pone la misma especificacioacuten anterior pero cambiando el orden de consideracioacuten de la regla vaciacutea ahora se incluye impliacutecitamente en la primera alternativa (soacutelo se muestran las partes modificadas)

void otrosNombres() ( lt nombre gt ) | ( lt nombre gt )+

Para esta especificacioacuten el generador JavaCC emite un aviso en el que se indica que la segunda alter -nativa nunca se seleccionaraacute no obstante se genera un analizador que no funciona como se supone que deberiacutea hacerlo por ejemplo el anaacutelisis de la entrada

nyz mxa = 1

produce un error despueacutes de tratado el primer nombre se intenta aplicar la produccioacuten vaciacutea (corres-pondiente a la repeticioacuten de cero veces contemplada en la primera regla) por lo que no se encuentra el esperado siacutembolo igual

40

Page 39: JavaCC

- la decisioacuten de abandonar la repeticioacuten del anaacutelisis de un nuevo nombre (en las dos primeras alterna-tivas de ltOtrosNombresgt) se puede tomar ante la presencia del siacutembolo igual

En la implementacioacuten de un analizador sintaacutectico LL(1) en sentido estricto la seleccioacuten de la alternativa vaciacutea para expandir el siacutembolo ltOtrosNombresgt se hariacutea tras comprobar expliacutecitamente que la pie-za sintaacutectica por adelantado es el siacutembolo igual

El funcionamiento del analizador generado por JavaCC depende del orden de colocacioacuten de las reglas A continuacioacuten se exponen varias posibilidades de especificacioacuten y se comenta el analizador generado en cada caso

bull Especificacioacuten primeraSe transcriben literalmente las reglas tal y como se han escrito antes en la notacioacuten BNF noacutetese que para poder codificar la parte derecha de la regla vaciacutea ha de incluirse una accioacuten sintaacutectica vaciacutea

PARSER_BEGIN (Igualdad)

public class Igualdad

public static void main (String[] argum) throws ParseException Igualdad analisis = new Igualdad(Systemin) analisissecuencia() Systemoutprintln(Analisis terminado) Systemoutprintln (no se han hallado errores leacutexico-sintaacutecticos)

PARSER_END (Igualdad)

void secuencia() lista() ltEOFgt

void lista() lt nombre gt otrosNombres() = lt numero gt

void otrosNombres() lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt ) |

TOKEN lt nombre ( [ a - z A - Z ntilde Ntilde ] )+ gt

TOKEN lt numero ( [ 0 - 9 ] )+ gt

SKIP | r | n

38

El analizador generado funciona correctamente puesto que en el meacutetodo otrosNombres se selec-ciona la alternativa vaciacutea despueacutes de haber comprobado que la pieza por adelantado no es siacutembolo ini-cial de ninguna de las dos alternativas precedentes (no es una coma ni un siacutembolo dos puntos)

bull Especificacioacuten segundaSe considera la misma especificacioacuten anterior pero cambiando el orden de colocacioacuten de las reglas ahora la produccioacuten vaciacutea se coloca como primera alternativa (soacutelo se muestran las partes modifica-das)

void otrosNombres() | lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt )

Con la entrada de esta especificacioacuten el generador JavaCC produce un mensaje en el que se avisa de que las dos alternativas que estaacuten colocadas detraacutes de la alternativa vaciacutea nunca podraacuten seleccionarse aun asiacute se genera un analizador Pero es un analizador que funciona incorrectamente asiacute por ejemplo la entrada

mxa nyz = 57

se considera incorrecta despueacutes de visto el primer nombre se intenta aplicar la produccioacuten vaciacutea (se espera encontrar el siacutembolo igual) pero habriacutea que aplicar la regla cuyo siacutembolo director es la coma

bull Especificacioacuten terceraSe utilizan las posibilidades de la notacioacuten BNF-Ampliada para prescindir de la especificacioacuten expliacuteci-ta de la palabra vaciacutea se aprovecha que JavaCC admite el operador + para indicar repeticiones de una o maacutes veces la alternativa vaciacutea estaacute impliacutecitamente contemplada en la posibilidad de cero repeti-ciones que contempla el operador (soacutelo se muestran las partes modificadas en la especificacioacuten)

void otrosNombres() ( lt nombre gt )+ | ( lt nombre gt )

El meacutetodo otrosNombres() generado hace las comprobaciones en el siguiente orden

- se comprueba si la pieza por adelantado es una coma (siacutembolo inicial de la primera alternativa) y en este caso se selecciona la primera regla- si no es una coma se comprueba si es un siacutembolo dos puntos (siacutembolo inicial de la segunda alter-nativa) y en este caso se selecciona la segunda regla- si no es un siacutembolo dos puntos se considera la palabra vaciacutea como siacutembolo inicial (siempre posi-ble impliacutecitamente) para la segunda alternativa en este caso para que no haya error la pieza por adelantado ha de ser un siacutembolo igual

Se trata pues de un analizador que funciona correctamente

bull Especificacioacuten cuarta

39

Se pone la misma especificacioacuten anterior pero cambiando el orden de consideracioacuten de la regla vaciacutea ahora se incluye impliacutecitamente en la primera alternativa (soacutelo se muestran las partes modificadas)

void otrosNombres() ( lt nombre gt ) | ( lt nombre gt )+

Para esta especificacioacuten el generador JavaCC emite un aviso en el que se indica que la segunda alter -nativa nunca se seleccionaraacute no obstante se genera un analizador que no funciona como se supone que deberiacutea hacerlo por ejemplo el anaacutelisis de la entrada

nyz mxa = 1

produce un error despueacutes de tratado el primer nombre se intenta aplicar la produccioacuten vaciacutea (corres-pondiente a la repeticioacuten de cero veces contemplada en la primera regla) por lo que no se encuentra el esperado siacutembolo igual

40

Page 40: JavaCC

El analizador generado funciona correctamente puesto que en el meacutetodo otrosNombres se selec-ciona la alternativa vaciacutea despueacutes de haber comprobado que la pieza por adelantado no es siacutembolo ini-cial de ninguna de las dos alternativas precedentes (no es una coma ni un siacutembolo dos puntos)

bull Especificacioacuten segundaSe considera la misma especificacioacuten anterior pero cambiando el orden de colocacioacuten de las reglas ahora la produccioacuten vaciacutea se coloca como primera alternativa (soacutelo se muestran las partes modifica-das)

void otrosNombres() | lt nombre gt ( lt nombre gt ) | lt nombre gt ( lt nombre gt )

Con la entrada de esta especificacioacuten el generador JavaCC produce un mensaje en el que se avisa de que las dos alternativas que estaacuten colocadas detraacutes de la alternativa vaciacutea nunca podraacuten seleccionarse aun asiacute se genera un analizador Pero es un analizador que funciona incorrectamente asiacute por ejemplo la entrada

mxa nyz = 57

se considera incorrecta despueacutes de visto el primer nombre se intenta aplicar la produccioacuten vaciacutea (se espera encontrar el siacutembolo igual) pero habriacutea que aplicar la regla cuyo siacutembolo director es la coma

bull Especificacioacuten terceraSe utilizan las posibilidades de la notacioacuten BNF-Ampliada para prescindir de la especificacioacuten expliacuteci-ta de la palabra vaciacutea se aprovecha que JavaCC admite el operador + para indicar repeticiones de una o maacutes veces la alternativa vaciacutea estaacute impliacutecitamente contemplada en la posibilidad de cero repeti-ciones que contempla el operador (soacutelo se muestran las partes modificadas en la especificacioacuten)

void otrosNombres() ( lt nombre gt )+ | ( lt nombre gt )

El meacutetodo otrosNombres() generado hace las comprobaciones en el siguiente orden

- se comprueba si la pieza por adelantado es una coma (siacutembolo inicial de la primera alternativa) y en este caso se selecciona la primera regla- si no es una coma se comprueba si es un siacutembolo dos puntos (siacutembolo inicial de la segunda alter-nativa) y en este caso se selecciona la segunda regla- si no es un siacutembolo dos puntos se considera la palabra vaciacutea como siacutembolo inicial (siempre posi-ble impliacutecitamente) para la segunda alternativa en este caso para que no haya error la pieza por adelantado ha de ser un siacutembolo igual

Se trata pues de un analizador que funciona correctamente

bull Especificacioacuten cuarta

39

Se pone la misma especificacioacuten anterior pero cambiando el orden de consideracioacuten de la regla vaciacutea ahora se incluye impliacutecitamente en la primera alternativa (soacutelo se muestran las partes modificadas)

void otrosNombres() ( lt nombre gt ) | ( lt nombre gt )+

Para esta especificacioacuten el generador JavaCC emite un aviso en el que se indica que la segunda alter -nativa nunca se seleccionaraacute no obstante se genera un analizador que no funciona como se supone que deberiacutea hacerlo por ejemplo el anaacutelisis de la entrada

nyz mxa = 1

produce un error despueacutes de tratado el primer nombre se intenta aplicar la produccioacuten vaciacutea (corres-pondiente a la repeticioacuten de cero veces contemplada en la primera regla) por lo que no se encuentra el esperado siacutembolo igual

40

Page 41: JavaCC

Se pone la misma especificacioacuten anterior pero cambiando el orden de consideracioacuten de la regla vaciacutea ahora se incluye impliacutecitamente en la primera alternativa (soacutelo se muestran las partes modificadas)

void otrosNombres() ( lt nombre gt ) | ( lt nombre gt )+

Para esta especificacioacuten el generador JavaCC emite un aviso en el que se indica que la segunda alter -nativa nunca se seleccionaraacute no obstante se genera un analizador que no funciona como se supone que deberiacutea hacerlo por ejemplo el anaacutelisis de la entrada

nyz mxa = 1

produce un error despueacutes de tratado el primer nombre se intenta aplicar la produccioacuten vaciacutea (corres-pondiente a la repeticioacuten de cero veces contemplada en la primera regla) por lo que no se encuentra el esperado siacutembolo igual

40