La descarga está en progreso. Por favor, espere

La descarga está en progreso. Por favor, espere

PROGRAMACION DE SISTEMAS

Presentaciones similares


Presentación del tema: "PROGRAMACION DE SISTEMAS"— Transcripción de la presentación:

1 PROGRAMACION DE SISTEMAS
JJTREE y JavaCC

2 Introducción JJTree, es una herramienta para construir un árbol de representación del mismo parser. JJTree se acciona para emitir un parser el cual su principal trabajo durante su ejecución no es ejecutar acciones incrustadas de java, sino construir una representación independiente de árbol del análisis sintáctico de la expresión que se analiza por el compilador resultante.

3 ¿Qué hacer con el árbol que produce JJTree?
JJTree permite capturar el estado de la sesión del análisis sintáctico en un solo árbol que es fácil de recorrer y verificar en tiempo de ejecución, independientemente del código de análisis sintáctico que lo produce.

4 Arbol de análisis sintáctico
Al árbol generado por JJTree, se le denomina formalmente como Árbol de análisis sintáctico. Para trabajar con JJTree se requiere lo siguiente: Crear un script con extensión .jjt el cual JJTree toma como entrada Escribir el código del lado cliente para recorrer y evaluar el árbol sintáctico que se produce en la ejecución.

5 Aspectos básicos de JJTree
JJTree es un preprocesador y genera un parser para un BNF particular. Es un proceso fácil de dos pasos: Ejecutar JJTree contra un archivo denominado .jjt; este producirá un archivo .jj Compilar el archivo .jj con JavaCC Y por último, compilar el archivo .java resultante

6 Sintaxis del archivo .jjt
La estructura de un archivo .jjt es de menor extensión que el formato .jj La principal diferencia es que JJTree agrega un nuevo constructor sintáctico (node-constructor) el cual permite especificar donde y bajo que condiciones se tienen que generar los nodos del árbol sintáctico durante el análisis sintáctico.

7 Una gramática simple SKIP:{“ “|”\t”|”\n”|”\r” }
TOKEN:{<INT : ([“0”-”9”])+ > } void simpleLeng():{} {addExpr() <EOF>} void addExpr():{} {integerLit() (“+” integerLit() ) ? } void integerLit():{} {<INT>} En ésta gramática se establece que una expresión válida consiste de: Una sola literal entera o Una literal entera seguida del signo más y seguido por otra literal entera. A continuación, ligeramente modificada para emplear JJTree (se indican los cambios) SimpleNode simpleLeng():#Root{} {addExpr()<EOF> {return jjtThis;}} void addExpr():{} {integerLit() (“+” integerLit() #Add(2) ) ? } void integerLit():#IntLit{} {<INT>}

8 Paso a paso por la gramática JJTree
La invocación a un parser sin emplear JJTree sería como: SimpleParser parser = new SimpleParser(new StringReader(expresion)); parser.simpleLeng(); Ahora la invocación a un parser empleando JJTree: SimpleNode rootNode = parser.simpleLeng();

9 Descripción del ejemplo
Las etiqueta insertada de la forma #Root, indican que se insertará un nodo raíz (de tipo SimpleNode) en el árbol creado y referenciado en todo momento por jjtThis. SimpleNode simpleLeng():#Root{} {addExpr()<EOF> {return jjThis;}}

10 Descripción del ejemplo
La etiqueta #Add(2) dentro de la producción addExpr() difiere de la etiqueta #Root en varios aspectos: Es parametrizada. El constructor del árbol utiliza una pila de nodos durante la construcción; el comportamiento normal para un constructor de nodos sin parámetros es colocarse en el tope del árbol sintáctico bajo construcción, sacando todos los nodos de la pila de nodos que fueron creados en su mismo alcance y colocando a dicho nodo como el nodo padre. El argumento 2 indica que el nuevo nodo padre (en éste caso #Add) adoptará exactamente dos nodos hijo, los dos sub nodos #IntLit descritos en la siguiente construcción.

11 Descripción del ejemplo
De igual importancia, la colocación de la directiva #Root fuera del cuerpo de la producción significa que el nodo #Root es generado cada vez que se cumple la producción (lo cual en éste caso sólo sucede una vez). Sin embargo, la colocación de la directiva #Add(2) dentro de un término “cero o uno” significa que un nodo #Add se crea condicionalmente si la cláusula opcional se cumple, es decir si ésta producción representa una verdadera operación de suma.

12 Descripción del ejemplo
Siguiendo las reglas de producción, integerLit() se cumple dos veces, agregando al árbol un nodo #IntLit en cada invocación. Ambos nodos #IntLit se convierten en nodos hijos del nodo #Add que los invoca. Sin embargo si la expresión a analizar sintácticamente es de un solo entero, entonces el nodo resultante se convierte directamente en nodo hijo de #Root

13 Representación del árbol
Árbol sintáctico para una expresión de un solo entero. Árbol sintáctico para una operación de suma.

14 Trabajando sobre el árbol resultante
Cada nodo que se declara en un archivo .jjt instruye al parser a generar una subclase del tipo SimpleNode de JJTree. SimpleNode en turno, implementa una interface Java denominada Node. Los archivos fuente para estas clases son generadas automáticamente por cada archivo JJTree, acompañando al archivo .jj JJTree también emite archivos fuente para sus propias clases – Root, Add y IntLit del ejemplo – así como otras clases de apoyo.

15 Desplegando el contenido del árbol.
Todas las subclases de SimpleNode heredan un comportamiento útil. El método dump() de SimpleNode es uno de estos. Las siguientes tres líneas de código inician el parser, lo invocan, obtienen en árbol sintáctico e imprimen directamente una representación textual del árbol en la consola.

16 Trabajando sobre el árbol resultante
Código dentro del método main() o método de la clase “Cliente” del parser. SimpleParser parser = new SimpleParser(new StringReader(expresion)); SimpleNode rootNode = parser.simpleLeng(); rootNode.dump(“ “); Representación del árbol de acuerdo al ejemplo Root Add IntLit

17 Navegando a través del árbol
Otro método útil de SimpleNode es jjtGetChild(int). De acuerdo al ejemplo, cuando se navega a través del árbol sintáctico y se encuentre un nodo #Add, seguramente se buscará obtener sus nodos hijos tipo #IntLit, extraer sus valores enteros que representan y sumarlos.

18 Navegando a través del árbol
Asumiendo que addNode, mostrado en el siguiente fragmento de código, es una variable que representa un nodo de tipo Add, podemos acceder a los dos nodos hijos de addNode. SimpleNode izq = addNode.jjtGetChild(0); SimpleNode der = addNode.jjtGetChild(1); Es importante aclarar que los dos nodos IntLit todavía no contienen los valores enteros que pretenden representar.

19 Almacenando y recuperando el estado.
Para almacenar el valor de los tokens identificados en los nodos apropiados, hay que realizar las siguientes modificaciones a SimpleNode: public class SimpleNode extends Node{ String m_text; public void setText(String text) {m_text = text;} public String getText() { return m_text; } }

20 Almacenar y recuperar el estado.
Cambiar la siguiente producción en el archivo .jjt void integerLit(): #IntLit {} {<INT>} por ésto void integerLit(): #IntLit {Token t} { t=<INT>{ jjtThis.setText(t.image);} } Esta producción obtiene el texto crudo del valor del entero encontrado en t.image y usa el método setText() para almacenar la cadena en el nodo actual.

21 Desplegando los cambios
Fácilmente se puede modificar el método SimpleNode.dump() para emitir el valor de m_text para cualquier nodo que sea almacenado durante el análisis. Por ejemplo, se puede modificar para visualizar la salida de dump(), para la expresión “42 + 1” y obtener el siguiente resultado: Root Add IntLit[42] IntLit[1]

22 Implementando la precedencia de operadores.
Ordenando correctamente las reglas de producción, colocando en la parte inferior aquellas reglas que tienen mayor prioridad, se cubre la precedencia de los operadores ya que la representación del árbol sintáctico controlará efectivamente el orden correcto de evaluación.

23 Ejemplo completo. Revisar el archivo Calc.jjt y realizar las siguientes modificaciones:


Descargar ppt "PROGRAMACION DE SISTEMAS"

Presentaciones similares


Anuncios Google