Analisis sintactico

14
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. ANALIZADOR SINTÁCTICO ROQUE CALDAS DOMINGUEZ

Transcript of Analisis sintactico

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

ANALIZADOR SINTÁCTICO

Teoría de Autómatas y Lenguajes Formales. Dean Kelley. PrenticeHall Cambridge