JavaCC - JJTree: La clase node: Tipos de nodos

Tipos de Nodos

JJTree provee decoraciones para dos variedades básicas de nodos:

también provee algunas abreviaturas sintácticas para que su uso sea conveniente.

Un nodo definido se construye con un número específico de hijos y puede definirse de la siguiente manera:

#ADefiniteNode (INTEGER EXPRESSION)

La expresión descriptora de un nodo definido puede ser cualquier expresión entera, la cual es la representación de la cantidad de hijos que poseerá el nodo.

Un nodo condicionalse contruye con todos los hijos que fueron apilados en la pila dentro de su alcance de nodo, si y sólo si la evaluación de su condición es verdadera (el alcance del nodo está dado por la expansión de su producción). Si el resultado de dicha evaluación es falso, el nodo no se construye, y todos los hijos permanecen en la pila de nodos. Un nodo condicional puede definirse de la siguiente manera:

#ConditionalNode (BOOLEAN EXPRESSION)

La expresión descriptora de un nodo condicional es una expresión booleana. Existen dos abreviaturas comunes para los nodos condicionales:

Nodos indefinidos:

#IndefiniteNodepor #IndefiniteNode(true)

Nodos Greater-than (mayores que):

#GTNode(>1)por #GTNode(jjtree.arity() > 1)

El nodo indefinido puede traer ambigüedades en el fuente JJTree cuando es seguido por una expansión encerrada entre paréntesis. En esos casos, la abreviatura debe reemplazarse por la expresión completa. Por ejemplo:

( ... ) #N ( a() )

es ambigua; debe usarse una condición explícita:

( ... ) #N(true) ( a())

La expresión descriptora del nodo no debería tener efectos colaterales. JJTree no especifica cuántas veces se evaluará la expresión.
Por defecto, JJTree trata cada no terminal como un nodo indefinido y deriva el nombre del nodo del nombre de su producción.
Se le pueden asignar diferentes nombres de acuerdo a la siguiente sintaxis:

void P1() #MyNode : { ... } { ... }

donde P1 es un no terminal y MyNode es el nombre que se le asigna a P1. El construtor #Name actúa como un operador postfijo, y su alcance es la unidad de expansión inmediatamente precedente.


Cuando el parser reconoce un no terminal P1 crea un nodo indefinido con nombre MyNode. Luego marca la pila, para que cualquier nodo creado y apilado por no terminales en la expansión de P1 sea despilado y se transforme en hijo del nodo MyNode.
Para evitar que una producción cree un nodo, puede usarse la siguiente sintaxis:

void P2() #void : { ... } { ... }

Ahora, cualquier nodo apilado por no terminales en la expansión de P2, permanecerá en la pila para ser desapilado y luego construir los hijos de una producción hacia arriba en el árbol. Por defecto, puede lograrse este comportamiento para nodos no decorados usando la opción NODE_DEFAULT_VOID

void P3() : {}
{
P4() ( P5() )+ P6()
}

En este ejemplo, un nodo indefinido P3 comenzaría, marcando la pila y luego se parsearían un nodo P4, uno o más nodos P5 y un nodo P6. Cualesquiera nodos que se apilen, se desapilarían y transformarían en hijos de P3. El árbol generado puede personalizarse:

NODE_DEFAULT_VOID option.

void P3() : {}
{
P4() ( P5() )+ #ListOfP5s P6()
}


Ahora, el nodo P3 tendrá un nodo P4, una lista de nodos ListOfP5s, y un nodo P6 como hijos.