Capítulo 3: Gramáticas en JavaCC
Las grámaticas en JavaCC son propiamente funciones, que tienen el trabajo de leer los tokens, y analizarlos gramaticalmente. Estas funciones leen (dependiendo de un parámetro que es pasado a JavaCC y de acuerdo al código de Java) un archivo o una línea escrita en ese momento, pero esto lo veremos en el siguiente capítulo. Ahora lo que nos interesa es saber como se expresan las Gramáticas en JavaCC.
A continuación veremos la forma en la que se expresa una Gramática:
|
Tipo Nombre (Parámetros): |
Como una función, tipo es uno de los tipos de variables de Java que pueden ser regresados, si en la gramática el tipo es void no regresará nada, en otro caso, la gramática deberá contener al menos un return. También, nombre es un id que sigue las reglas de Java. En parámetros se colocan parámetros si se creen necesarios, con el mismo formato que Java, en el bloque de variables se definen los tipos de variable, los tipos de variable pueden ser tanto de Java como de JavaCC (más sobre esto en el siguiente capítulo). Para acabar, en la parte de producción se pone la producción de la gramática.
Ahora si recordamos el capítulo 1, recordaremos que las gramáticas utilizan ciertos símbolos, bueno, pues aquí les pondre los equivalentes de cada símbolo:
|
Simbolo de Gramática
|
Función
|
Equivalente en JavaCC
|
| ID |
Nombre de no terminal, da el nombre de un no terminal |
Nombre de función o llamada a función
|
| | | Or |
| |
| ::= |
Produce
|
El segundo bloque de llaves en la función de la gramática. |
| <E.R.> |
Utilización de una expresión regular (terminal)
|
<Nombre de la E.R.> o "E.R."
|
| " " |
Terminales o símbolos constantes
|
En JavaCC no existen estos, si se utiliza una palabra entre " "
se infiere que ya fue declarada como E.R. y la busca si no lo encuentra
lo tomará como un token de algun tipo ya definido. |
| € |
Símbolo de nada
|
En JavaCC € no existe, así que mediante los mismos simbolos
que vimos y definimos en las E.R.'s, se llega a producir €; No
existe forma de colocar € directamente en una gramática, sin
embargo existe la posbilidad de represntar el caso en el que no encuentre
lo que busca. |
| ( ) |
Agrupación
|
( )
|
|
Repetición
|
Tanto en gramáticas como en JavaCC la repetición, no es
más que una llamada recursiva a la función (gramática) |
Ahora con estos
símbolos procedemos a crear la producción; la producción
también tiene un formato específico, y este es:
|
Producción |
Producción y Otra Producción no es más que una regla gramatical (en un momento lo veremos), sólo que Otra Producción es opcional pues una gramática puede estar formada por una sola producción, después de la producción se define que pasa si la encuentra, por lo regular se hace un return para regresar algún valor específico. Es importante que se note que cada producción va separada por un or "|" y que cada vez que se define una producción se debe de indicar que pasa si encuentra esa producción, excepto si las dos o más producciones deben de regresar el mismo valor, pues basta con agrupar y colocar los or necesarios (más sobre esto en un momento). JavaCC compila con WARNINGS, pero hay un problema, Java NO, entonces si JavaCC manda WARNINGS es mejor componer los errorer que intentar correr eso en Java, de todas formas Java mandará error.
Definición de Reglas Gramáticales.
La forma en que JavaCC reconoce las reglas gramáticales, no está muy lejos de lo que se ve en autómatas o en teoría de la computación; pues sólo se deberá colocar como se quieren encontrar los componentes léxicos (E.R.'s). Veamos algunos ejemplos:
|
<write>"("<cadena>")"";" |
Esta regla gramatical acepta la instrucción write, nótese
que los paréntesis y el punto y coma están entre comillas,
esto es válido, si y solo si, se definió una E.R. que aceptara
"(" una que aceptara ")" y una que aceptara ";". |
| <if>condicion( )<then> |
Esta regla gramatical acepta if seguido de una condición,
la cual es otra gramática, y seguido de then. Aquí es importante
que se note cómo se hace la llamada a otra gramática |
| <for>asignacion( )(<to>|<downto>)<do> |
Aquí vemos como con la agrupación podemos elegir
entre to o downto. Esto mismo se puede hacer con toda la función,
veamos el siguiente ejemplo |
| (<id><asignacion><numero>";" | <id>";") | Como vemos son dos producciónes diferentes, sin embargo es posible que se requiera mandar el mismo resultado cuando se encuentre éste. |
Ya para finalizar el capítulo veremos como se juntan todos los conocimientos aquí adquiridos:
|
String Write( ): |
Gramática sencilla de la sentencia write, regresa una
cadena que dice "encontré la sentencia write". |
|
String Write2( ):{} void Contenido( ):{} |
En esta Gramática notamos cómo es que se definen dos o mas gramáticas, haciendo uso de los conocimientos de Java, podemos decir que el orden en el que aparezcan las gramáticas(desde el punto de vista que son funciones) no importa. También, notamos un |{return;} bueno este es nuestro equivalente al €, no es precisamente €, significa si existe otra cosa que no sea <cadena> ó <id> regresa. El uso de este "Truco" puede ser perjudicial, puesto que si lo notan, ahí nos dice que esta gramática acepta cualquier cosa dentro de los paréntesis, cosa que está mal. Se preguntarán ¿para qué nos enseña algo
que está mal? es simple, para que ustedes no cometan el mismo error
que yo cometí. Ahora les diré cual es el código correcto
de contenido para forzarlo a aceptar € |