Implementación de los Nodos

Antes de hacer el código que construye el árbol, hay que tener ya implementados todos los nodos. Eso es lo que se va a hacer en esta sección.

Y, para ello, basta seguir las indicaciones de la gramática abstracta.

Nota

📌 Las gramáticas abstractas son independientes del lenguaje de programación, es decir, describen cómo tienen que ser los nodos pero no en qué lenguaje tienen que ser implementados. Aquí se mostrará cómo implementarlos en Java, pero la gramática abstracta sería la misma aunque el compilador se hiciera en otro lenguaje

Para obtener el código de cada nodo, hay que seguir el siguiente proceso:

  • Se crea un interfaz AST que será el tipo común de todos los nodos.
  • Por cada categoría sintáctica se crea un interfaz con el mismo nombre. Dicho interfaz derivará del interfaz AST.
  • Por cada regla (es decir, por cada nodo) se crea una clase del mismo nombre que el nodo. Dicha clase implementará los interfaces de las categorías sintácticas a las que pertenezca (o AST si no pertenece a ninguna).
  • La parte derecha de cada regla indica los atributos que debe tener la clase Java creada para dicha regla.
    • Por cada hijo, se crea una propiedad Java del tipo del mismo.
    • Si no se indica el nombre de un hijo (ya que sólo su tipo es obligatorio), se puede poner cualquier nombre suficientemente explicativo.
    • Si el hijo tiene '*' asociado a su tipo, se crea una lista de dicho tipo. En caso contrario, se añade simplemente una referencia a un sólo objeto.

Por ejemplo, supóngase la siguiente gramática abstracta.

programa ⟶ sentencias*

asigna:sentencia ⟶ variable expr
while:sentencia ⟶ expr sentencias*
lectura:sentencia ⟶ variable

exprBinaria:expr ⟶ left:expr operador:string right:expr
variable:expr ⟶ nombre:string
literalEntero:expr ⟶ valor:string

Su implementación sería:

interface AST { }

interface Sentencia extends AST  { }
interface Expr extends AST  { }

class Programa implements AST {
    List<Sentencia> sentencias;
}

class Asigna implements Sentencia {
    Variable variable;
    Expr expr;
}

class While implements Sentencia {
    Expr expr;
    List<Sentencia> sentencias;
}

class Lectura implements Sentencia {
    Variable variable;
}

class ExprBinaria implements Expr {
    Expr left;
    String operador;
    Expr right;
}

class Variable implements Expr {
    String nombre;
}

class LiteralEntero implements Expr {
    String valor;
}

Se ha omitido, para no extender demasiado el ejemplo, los constructores de todas las clases y los getters y setters de todas las propiedades.

Aunque es opcional en este momento, también suele ser habitual, anticipándose a lo que vendrá en posteriores capítulos, crear una clase abstracta por cada categoría sintáctica. En esta clase, en un futuro, se pondrán las propiedades comunes a todos los nodos de la misma categoría.





 







...

interface Expr extends AST  { }

class AbstractExpr implements Expr {
    /* Las propiedades comunes de las expresiones vendrán aquí */
}

class ExprBinaria extends AbstractExpr { ... }
class Variable extends AbstractExpr { ... }
class LiteralEntero extends AbstractExpr { ... }