El analizador sintáctico es una de las fases
de un compilador donde se tiene por
finalidad verificar el ordenar los tokens ,
obtenidos del analizador lexicográfico, a
partir de una gramática de libre contexto.
ANALIZADORSINTÁCTICO
ROQUE CALDAS DOMINGUEZ
ANALIZADOR SINTÁCTICO
1. INTRODUCCIÓN
El analizador léxico tiene como entrada el código fuente en forma de una sucesión de caracteres.
El analizador sintáctico tiene como entrada los lexemas que le suministra el analizador léxico y
su función es comprobar que están ordenados de forma correcta (dependiendo del lenguaje que
queramos procesar). Los dos analizadores suelen trabajar unidos e incluso el léxico suele ser una
subrutina del sintáctico. Al analizador sintáctico se le suele llamar párser. El párser genera de
manera teórica un árbol sintáctico. Este árbol se puede ver como una estructura jerárquica que
para su construcción utiliza reglas recursivas. La estructuración de este árbol hace posible
diferenciar entre aplicar unos operadores antes de otros en la evaluación de expresiones. Es
decir, si tenemos esta expresión en Java:
El valor de x dependerá de si aplicamos antes el operador producto que el operador suma. Una
manera adecuada de saber qué operador aplicamos antes es elegir qué árbol sintáctico generar
de los dos posibles.
x = x * y – 2;
Figura 1.1|Árbol sintáctico
Figura 1.2|Árbol sintáctico
ANALIZADOR SINTÁCTICO
En resumen, la tarea del analizador sintáctico es procesar los lexemas que le suministra el
analizador léxico, comprobar que están bien ordenados, y si no lo están, generar los informes
de error correspondientes. Si la ordenación es correcta, se generará un árbol sintáctico teórico.
2. FUNCIONES DEL ANALIZADOR SINTÁCTICO
La función principal del analizador sintáctico es determinar si una cadena de componentes
léxicos puede ser generada por una gramática.
Controla el flujo de tokens reconocidos por parte del analizador léxico y comprueba si la cadena
pude ser generada por la gramática del lenguaje fuente.
Informa de la naturaleza de los errores sintácticos que encuentra e intenta recuperarse de ellos
para continuar la compilación.
3. DISEÑO DE GRAMATICA
Para que un analizador sintáctico funcione, debemos especificar el lenguaje que debe poder
leer. Para especificar este lenguaje, debemos representarlo con unas reglas únicas y bien
formadas de manera que el párser (Analizador sintáctico) funcione de una manera bien definida.
Es decir, el lenguaje debe ser formal (tener unas reglas bien definidas). A estas reglas se le llama
gramática. Por lo tanto, el primer paso para poder implementar un analizador sintáctico es
definir la gramática que debe ser capaz de analizar.
Figura 3.1|Entradas y salidas de las dos primeras fases de la etapa de análisis.
Las gramáticas ofrecen ventajas significativas:
Una gramática da una especificación sintáctica precisa y fácil de entender de un lenguaje
de programación.
ANALIZADOR SINTÁCTICO
A partir de algunas clases de gramáticas se puede construir automáticamente un
analizador sintáctico eficiente que determine si un programa fuente está
sintácticamente bien formado.
El proceso de construcción del analizador sintáctico puede revelar ambigüedades
sintácticas y otras construcciones difíciles de analizar que de otro modo podrían pasar
sin detectar en la fase inicial de diseño de un lenguaje y de su compilador.
La gramática que acepta el analizador sintáctico es una gramática de libre contexto, una
gramática de libre contexto es una especificación para la estructura sintáctica de un lenguaje de
programación. Una especificación así es muy similar a la especificación de la estructura léxica de
un lenguaje utilizando expresiones regulares(patrón), excepto que una gramática libre de
contexto involucra reglas de recursividad. Una gramática describe de forma natural la estructura
jerárquica de muchas construcciones de los lenguajes de programación. Una gramática libre de
contexto G queda definida por una tupla de cuatro elementos (N, T, P, S) donde:
Las gramáticas libres de contexto permiten describir la mayoría de lenguajes de programación,
de hecho, la sintaxis de la mayoría de lenguajes de programación está definida mediante
gramáticas libres de contextos.
Pongamos un ejemplo de gramática e identificaremos cada uno de los elementos. Sea la
gramática:
E E+T | T
T T*F|F
F id | F |(E)
E, T, F son las no terminales.
id, +, *, (,) son terminales.
Hay tres reglas de producción
E es el axioma inicial.
N = No terminales. Elementos del lado izquierdo de producción, antes de la flecha
T= Terminales. Elementos que no generan nada.
P= Reglas de producción. Sentencias que se escriben en la gramática. Cada regla o producción
consta de:
Cabeza: no terminales (lado izquierdo de la producción).
: símbolo de producción.se lee “se define como” o “puede tener la forma”
Cuerpo: cadena de 0 o más símbolos terminales y/o no terminales (lado derecho de
la producción).
S= Axioma inicial. Primer elemento de la gramática.
ANALIZADOR SINTÁCTICO
Esta gramática reconoce expresiones aritméticas con los operadores de suma y producto.
Vemos también que hay tres reglas y el axioma inicial es el antecedente de la primera regla de
producción. E es recursiva porque se presenta en ambos lados de la producción.
Ahora veamos con un ejemplo cómo podemos crear una gramática. Para esto recodemos como
se declara una variable en java:
A. DERIVACIONES UTILIZANDO UNA GRAMÁTICA
Una regla de producción puede considerarse como equivalente a una regla de reescritura,
donde el no terminal de la izquierda es sustituida por la pseudocadena del lado derecho de la
producción. Podemos considerar que una pseudocadena es cualquier secuencia de terminales
y/o no terminales.
Dependiendo de por dónde comencemos a reescribir en la pseudocadena, tendremos una
derivación por la izquierda (si comenzamos por la izquierda) o por la derecha (si comenzamos
por la derecha). Si queremos construir una cadena de tokens que sean generadas por una
gramática concreta, podremos hacerlo aplicando las reglas de la gramática según vayan
concordando con los tokens.
Por ejemplo, supongamos que tenemos la siguiente gramática:
𝐸 → 𝐸 + 𝐸ȁ𝐸 ∗ 𝐸ȁ𝑛𝑢𝑚ȁ𝑖𝑑ȁ(𝐸)
Derivación por la izquierda
𝐸 → 𝐸 ∗ 𝐸 → 𝐸 + 𝐸 ∗ 𝐸 → 𝑖𝑑1 + 𝐸 ∗ 𝐸 → 𝑖𝑑1 + 𝑖𝑑2 ∗ 𝐸 → 𝑖𝑑1 + 𝑖𝑑2 ∗ 𝑖𝑑3
Es aquella en la que la reescritura se realiza sobre el no terminal más a la izquierda de la
pseudocadena de partida.
Derivación por la derecha
𝐸 → 𝐸 ∗ 𝐸 → 𝐸 ∗ 𝑖𝑑3 → 𝐸 + 𝐸 ∗ 𝑖𝑑3 → 𝐸 + 𝑖𝑑2 ∗ 𝑖𝑑3 → 𝑖𝑑1 + 𝑖𝑑2 ∗ 𝑖𝑑3
int a;
double b;
1) VAR -> TIPO ID;
2) TIPO -> int | double
3) ID - > id
DERIVACIÓN: Aplicación de las reglas de producciones de una gramática para obtener una
cadena de terminales.
ANALIZADOR SINTÁCTICO
Es aquella en la que la reescritura se realiza sobre el no terminal más a la derecha de la
pseudocadena de partida.
A partir de estas derivaciones se puede construir sus árboles sintácticos. Pero hay cosas en que
cada posible derivación dará lugar a un árbol sintáctico diferente. Esto significa que la gramática
es ambigua.
Veamos un ejemplo. Supongamos que tenemos la gramática anterior y queremos procesar estos
tokens:
𝑖𝑑1 + 𝑖𝑑2 + 𝑖𝑑3
Hay dos posibles árboles sintácticos:
Por lo tanto, deduciremos que la gramática es ambigua. Para la implementación de esta
gramática, es necesario esta ambigüedad. El orden en que vamos creando las ramas y las hojas
no da la idea del orden en que se irán procesando las reglas. Por lo tanto, tenemos un
mecanismo secuencial de procesamiento.
Figura 3.2|Derivación por la izquierda
Figura 3.3| Derivación por la derecha
ANALIZADOR SINTÁCTICO
La raíz del árbol es el axioma inicial y, según nos convenga, lo dibujaremos en la cima o en el
fondo del árbol. Como nodos internos del árbol, se sitúan los elementos no terminales de las
reglas de producción que vayamos aplicando, y cada uno de ellos poseerá tantos hijos como
símbolos existan en la parte derecha de la regla aplicada.
4. DIFICULTADES PARA LA CREACIÓN DE GRAMÁTICAS
A. RECURSIVIDAD La recursividad se expresa por medio de una o más reglas no recursivas, que son la base, y una
o más reglas que son recursivas y que permiten hacer crecer la estructura del lenguaje
aplicándose a sí mismas una y otra vez. Con un ejemplo, lo entenderemos mejor:
Supongamos que queremos expresar la estructura de un número entero compuesto por su signo
seguido por un número indeterminado de números entre el 0 y el 9. Lo podríamos expresar con
estas reglas:
𝐸𝑛𝑡𝑒𝑟𝑜 → 𝑠𝑖𝑔𝑛𝑜
𝐸𝑛𝑡𝑒𝑟𝑜 → 𝐸𝑛𝑡𝑒𝑟𝑜 𝑑𝑖𝑔𝑖𝑡𝑜
Donde dígito representa cualquiera de los números del 0 al 9. Mediante esas dos reglas
podemos representar la estructura de cualquier número entero sea de la longitud que sea.
Una gramática se llama recursiva si es de la forma:
𝐴 → 𝑎 𝐴 𝑏
Donde A es un no terminal y a y b son terminales o no terminales. Al ter minal A le llamamos t
erminal recursivo. Si no existe el término a, se trata de una recursividad por la izquierda y si no
existe b es una recursividad por la derecha.
B. LA AMBIGÜEDAD Cuando una gramática contiene una cadena para la que hay más de un árbol de análisis
sintáctico se dice que es ambigua. Debido a que una gramática de estas características permite
que a partir del mismo código fuente se puedan obtener diferentes códigos intermedios, no es
válida para construir un compilador (habría que ayudar con otras técnicas más complicadas).
Si una gramática tiene alguna de estas características, podremos afirmar que es ambigua:
Gramáticas con ciclos:
𝑆 → 𝐴
ANALIZADOR SINTÁCTICO
𝑆 → 𝑎
𝐴 → 𝑆
Gramáticas con alguna regla de la forma:
𝐸 → 𝐸 … . 𝐸
Gramáticas con unas reglas que ofrezcan caminos alternativos entre dos puntos.
Por ejemplo:
𝑆 → 𝐵
𝑆 → 𝐶
𝐵 − 𝐶
Producciones recursivas en las que las variables no recursivas de la producción
puedan derivar a la cadena vacía.
Por ejemplo:
𝑆 → 𝐴 𝐵 𝑆
𝑆 → 𝑆
𝐴 → 𝑎 ȁ€
𝐵 → 𝑏 ȁ€
Símbolos no terminales que puedan derivar a la cadena vacía y a la misma cadena de
terminales, y que aparezcan juntas en la parte derecha de una regla o en alguna forma
sentencia.
Por ejemplo:
𝐴 → 𝐴 𝐵
𝐴 → 𝑎 ȁ €
𝐵 → 𝑏 ȁ𝑎ȁ€
ANALIZADOR SINTÁCTICO
SUPRESION DE LA AMBIGÜEDAD
A veces, una gramática ambigua se puede reescribir para eliminar la ambigüedad. Como
ejemplo, se eliminará la ambigüedad de la siguiente gramática con “else ambiguo”:
𝑃𝑅𝑂𝑃 → 𝑖𝑓 𝐸𝑋𝑃𝑅 𝑡ℎ𝑒𝑛 𝑃𝑅𝑂𝑃 ห 𝑖𝑓 𝐸𝑋𝑃𝑅 𝑡ℎ𝑒𝑛 𝑃𝑅𝑂𝑃 𝑒𝑙𝑠𝑒 𝑃𝑅𝑂𝑃 ห 𝑜𝑡𝑟𝑎
Aquí, “otra” representa cualquier otra proposición. Esta gramática es ambigua, puesto que la
cadena
𝑖𝑓 𝐸1 𝑡ℎ𝑒𝑛 𝑖𝑓 𝐸2 𝑡ℎ𝑒𝑛 𝑆1 𝑒𝑙𝑠𝑒 𝑆2
Tiene los dos árboles de análisis sintáctico que se muestra a continuación
Figura 4.1|Árbol de análisis sintáctico para la proposición condicional
Figura 4.2| Dos árboles de análisis sintáctico para una frase ambigua
ANALIZADOR SINTÁCTICO
En todo lenguaje de programación con proposiciones condicionales de esta forma, se prefiere
el primer árbol de análisis sintáctico. La regla general es, “emparejar cada else con el then sin
emparejar anterior más cercano”. Esta regla para eliminar ambigüedades se puede incorporar
directamente a la gramatica. La ide es que una proposición que aparezca entre un then si
emparejar seguido de cualquier proposición, porque entonces el else estaría obligado a
concordar con este then no emparejado. Una proposición emparejada es o una proposición if-
then-else que no contenga proposiciones sin emparejar o cualquier otra clase de proposición
no condicional. Así, se puede utilizar la gramática
𝑃𝑅𝑂𝑃 → 𝑃𝑅𝑂𝐸𝑀𝑃𝐴𝑅𝐸𝐽𝐴𝐷𝐴ȁ 𝑃𝑅𝑂𝑃𝑁𝑂𝐸𝑀𝑃𝐴𝑅𝐸𝐽𝐴𝐷𝐴
𝑃𝑅𝑂𝑃𝐸𝑀𝑃𝐴𝑅𝐸𝐽𝐴𝐷𝐴 → 𝑖𝑓 𝑒𝑥𝑝𝑟 𝑡ℎ𝑒𝑛 𝑃𝑅𝑂𝑃𝐸𝑀𝑃𝐴𝑅𝐸𝐽𝐴𝐷𝐴 𝑒𝑙𝑠𝑒 𝑃𝑅𝑂𝑃𝐸𝑀𝑃𝐴𝑅𝐸𝐽𝐴𝐷𝐴 ȁ 𝑜𝑡𝑟𝑎
𝑃𝑅𝑂𝑃𝑁𝑂𝐸𝑀𝑃𝐴𝑅𝐸𝐽𝐴𝐷𝐴 → 𝑖𝑓 𝐸𝑋𝑃𝑅 𝑡ℎ𝑒𝑛 𝑃𝑅𝑂𝑃 ȁ 𝑖𝑓 𝐸𝑋𝑃𝑅 𝑡ℎ𝑒𝑛 𝑃𝑅𝑂𝑃𝑁𝑂𝐸𝑀𝑃𝐴𝑅𝐸𝐽𝐴𝐷𝐴 𝑒𝑙𝑠𝑒 𝑃𝑅𝑂𝑃𝑁𝑂𝐸𝑀𝑃𝐴𝑅𝐸𝐽𝐴𝐷𝐴
C. LA ASOCIATIVIDAD La asociatividad es un concepto que aparece cuando se operan tres o más operandos. La
asociatividad de un operador es por la izquierda si cuando aparecen tres o más operandos se
evalúan de izquierda a derecha. Si es de derecha a izquierda, la asociatividad es por la derecha.
Por ejemplo, si tenemos “6/3/2”, por convención es equivalente a (6/3) /2. Cuando un operando
como 3 tiene operadores a su izquierda y derecha, se necesitan convenciones para decir qué
operador considera ese operando. si el operador “/” tiene asociatividad por la izquierda,
primero se opera “6/3” el resultado se opera con “2” es 1.
Algunos operadores comunes, como el exponenciación. Son asociativos por la derecha. Otro
ejemplo análogo, el operador de asignación “=” en java es asociativo por la derecha: en java, la
expresión a=b=c se trata igual que la expresión a=(b=c).
Las cadenas como a=b=c. Con un operador asociativo por la derecha. Son generados por la
siguiente gramática:
𝐷𝐸𝑅𝐸𝐶𝐻𝐴 → 𝐿𝐸𝑇𝑅𝐴 = 𝐷𝐸𝑅𝐸𝐶𝐻𝐴ȁ𝐿𝐸𝑇𝑅𝐴
𝐿𝐸𝑇𝑅𝐴 → 𝑎ȁ𝑏ȁ𝑐 … ȁ𝑧
El contraste entre un árbol de análisis sintáctico para un operador asociativo por la izquierda
como “/”, y un árbol de análisis sintáctico para un operador asociativo por la derecha como “=”,
El árbol de análisis sintáctico para 6/3/2 desciende hacia la izquierda, mientras que el árbol de
análisis sintáctico a=b=c desciende hacia la derecha.
La manera de reflejar la asociatividad de un operador en una gramática es poniendo
recursividad del mismo lado que el operador en la regla sintáctica donde interviene dicho
operador.
ANALIZADOR SINTÁCTICO
D. LA PRECEDENCIA La precedencia de un operador indica el orden en que se aplicará respecto a los demás
operadores en caso de poder aplicar más de uno. Es decir, si en una regla podemos aplicar más
de un operador, comenzaremos aplicando el de más precedencia y terminaremos por aplicar el
de menor precedencia. La manera de reflejar la precedencia en una gramática es utilizar para
cada operador una variable en la gramática y situarla más cerca del símbolo inicial cuanto menor
sea la precedencia.
_____________________________________________________________________________
Ejemplo: Sintaxis de expresiones
utilizando una tabla que muestre la asociatividad y procedencia de operadores se puede
construir una gramática para expresiones aritmética. Se empieza con los cuatro operadores
aritmético básico y una tabla de precedencias, mostrando los operadores en orden de
procedencia creciente, con los operadores de la misma precedencia en la misma línea.
asociativos por la izquierda: + -
asociativos por la derecha: * /
SOLUCIÓN A LA PROCEDENCIA:
Las unidades básicas de las expresiones son de momento dígitos y expresiones entre paréntesis.
𝐹𝐴𝐶𝑇𝑂𝑅 → 𝑑𝑖𝑔𝑖𝑡𝑜 ȁ (𝐸𝑋𝑃𝑅)
ahora, considérese los operadores binarios * y /, que tienen mayor procedencia.
𝑇𝐸𝑅𝑀𝐼𝑁𝑂 → 𝑇𝐸𝑅𝑀𝐼𝑁𝑂"*"𝐹𝐴𝐶𝑇𝑂𝑅ห 𝑇𝐸𝑅𝑀𝐼𝑁𝑂"/"𝐹𝐴𝐶𝑇𝑂𝑅ห𝐹𝐴𝐶𝑇𝑂𝑅
de manera similar, <expr> genera listas de términos separados
EXPR→ 𝐸𝑋𝑃𝑅 " + " 𝑇𝐸𝑅𝑀𝐼𝑁𝑂ȁ 𝑇𝐸𝑅𝑀𝐼𝑁𝑂" − "𝑇𝐸𝑅𝑀𝐼𝑁𝑂ȁ𝑇𝐸𝑅𝑀𝐼𝑁𝑂
por lo tanto, la gramática resultante es:
𝐸𝑋𝑃𝑅 → 𝐸𝑋𝑃𝑅" + "𝑇𝐸𝑅𝑀𝐼𝑁𝑂ȁ𝐸𝑋𝑃𝑅" − "𝑇𝐸𝑅𝑀𝐼𝑁𝑂ȁ𝑇𝐸𝑅𝑀𝐼𝑁𝑂
𝑇𝐸𝑅𝑀𝐼𝑁𝑂 → 𝑇𝐸𝑅𝑀𝐼𝑁𝑂" ∗ "𝐹𝐴𝐶𝑇𝑂𝑅 ห 𝑇𝐸𝑅𝑀𝐼𝑁𝑂"/"𝐹𝐴𝐶𝑇𝑂𝑅 ห𝐹𝐴𝐶𝑇𝑂𝑅
𝐹𝐴𝐶𝑇𝑂𝑅 → 𝑑𝑖𝑔𝑖𝑡𝑜 ȁ(𝐸𝑋𝑃𝑅)
es posible que un analizador sintáctico descendiente recursivo entre en un bucle indefinido.
𝐸𝑋𝑃𝑅−> 𝐸𝑋𝑃𝑅 + 𝑇𝐸𝑅𝑀𝐼𝑁𝑂
se crean dos no terminales EXPR y TERMINO para los dos niveles de precedencia, y un no
terminal adicional factor para generar unidades básicas en las expresiones. Ponemos primero
las reglas con menor procedencia “+” y “- “y al final de mayor precedencia “*” y “/”
ANALIZADOR SINTÁCTICO
LA GRAMÁTICA PRESENTA RECURSIVIDAD:
Hay un problema con producciones recursivas por la izquierda en la que el símbolo situado más
a la izquierda del lado derecho de la producción es el mismo que el no terminal del lado izquierdo
de la producción.
SOLUCIÓN A LA RECURSIVIDAD POR LA IZQUIERDA:
se puede eliminar una producción recursiva por la izquierda reescribiendo la producción
𝐴 → 𝐴𝑎 ȁ𝑏
donde a y b son secuencias de terminales y no terminales que no comienzan con A como en
nuestro ejemplo
𝐸𝑋𝑃𝑅 → 𝐸𝑋𝑃𝑅"+"𝑇𝐸𝑅𝑀𝐼𝑁𝑂ȁ𝑇𝐸𝑅𝑀𝐼𝑁𝑂
𝐴 = 𝐸𝑋𝑃𝑅 , 𝑎 = " + "𝑇𝐸𝑅𝑀𝐼𝑁𝑂 𝑦 𝑏 = 𝑇𝐸𝑅𝑀𝐼𝑁𝑂
se remplaza A por b
𝐸𝑋𝑃𝑅 → 𝑇𝐸𝑅𝑀𝐼𝑁𝑂 "+ " TERMINO
EL RESULTADO FINAL:
𝐸𝑋𝑃𝑅 → 𝑇𝐸𝑅𝑀𝐼𝑁𝑂" + "𝑇𝐸𝑅𝑀𝐼𝑁𝑂ȁ𝑇𝐸𝑅𝑀𝐼𝑁𝑂" − "𝑇𝐸𝑅𝑀𝐼𝑁𝑂ȁ𝑇𝐸𝑅𝑀𝐼𝑁𝑂
𝑇𝐸𝑅𝑀𝐼𝑁𝑂 → 𝐹𝐴𝐶𝑇𝑂𝑅" ∗ "𝐹𝐴𝐶𝑇𝑂𝑅 ห 𝐹𝐴𝐶𝑇𝑂𝑅"/"𝐹𝐴𝐶𝑇𝑂𝑅 ห𝐹𝐴𝐶𝑇𝑂𝑅
𝐹𝐴𝐶𝑇𝑂𝑅 → 𝑑𝑖𝑔𝑖𝑡𝑜 ȁ(𝐸𝑋𝑃𝑅)
5. ANALIZADOR SINTÁCTICO EN JAVACC
Se trata de una herramienta que facilita la construcción de analizadores léxicos y sintácticos por
el método de las funciones recursivas, aunque permite una notación relajada muy parecida a la
BNF.
Genera analizadores descendentes, permitiendo el uso de gramáticas de propósito
general y la utilización de atributos tanto sintetizados como heredados durante la
construcción del árbol sintáctico.
Las especificaciones léxicas y sintácticas se ubican en un solo archivo. De esta manera la
gramática puede ser leída y mantenida más fácilmente. No obstante, cuando se
introducen acciones semánticas, recomendamos el uso de ciertos comentarios para
mejorar la legibilidad.
ANALIZADOR SINTÁCTICO
Los programas JavaCC se suelen almacenar en ficheros con extensión .jj
Al aplicar el comando javacc Ejemplo.jj produce ficheros de salida relacionados con el analizador
sintáctico:
Ejemplo.java: es el analizador sintáctico.
EjemploTokenManager.java: es el analizador lexicográfico.
Token.java: clase que implementa el objeto a través del cual se comunican el analizador
léxico y el sintáctico.
AREA DE FUNCIONES BNF: Como ya se ha comentado, JavaCC genera un analizador
sintáctico descendente implementado a base de funciones recursivas, de manera que cada
no terminal de nuestra gramática será convertido una función diferente, cuya
implementación será generado por javaCC. Esta tiene la siguiente estructura:
𝑡𝑖𝑝𝑜𝑅𝑒𝑡𝑜𝑟𝑛𝑜1 𝑓𝑢𝑛𝑐𝑖𝑜𝑛𝐽𝑎𝑣𝑎 (𝑝𝑎𝑟𝑎𝑚1):
{ 𝑐𝑜𝑑𝑖𝑔𝑜𝐽𝑎𝑣𝑎1 }
{ 𝑒𝑥𝑝𝑟𝐵𝑁𝐹1 }
𝑡𝑖𝑝𝑜𝑅𝑒𝑡𝑜𝑟𝑛𝑜2 𝑓𝑢𝑛𝑐𝑖𝑜𝑛𝐽𝑎𝑣𝑎 (𝑝𝑎𝑟𝑎𝑚2):
{ 𝑐𝑜𝑑𝑖𝑔𝑜𝐽𝑎𝑣𝑎2 }
{ 𝑒𝑥𝑝𝑟𝐵𝑁𝐹2 }
Dado que cada no terminal se convertirá en una función en Java, el desarrollador no sólo
debe indicar su nombre, sino la cabecera completa de dicha función. Este hecho es de
fundamental importancia puesto que:
JavaCC permite el intercambio de atributos entre reglas BNF mediante el paso de
parámetros y la obtención de resultados en el momento de hacer uso de un no terminal (o
lo que es lo mismo invocar a la función que lo implementa) lo que equivale,
respectivamente, a enviar atributos hacia abajo y hacia arriba en el árbol sintáctico.
6. REFERENCIAS
Compiladores Principios, técnicas y herramientas. Alfred V. Aho, Ravi Sethi,
Jeffrey D. Ullman Adisson Wesley.
Compiladores Conceptos fundamentales. Teufel, Schmidt, Teufel. Adisson
Wesley Iberoamericana.
Modern compiler implementation in C. Appel. Cambridge
Top Related