Skip to content

Implementación Multivisitor

Estructura

En una implementación multivisitor, se crea una clase visitor por cada función de código que se haya definido en la misma. Cada uno de estos visitors implementará sólo las plantillas de código de su función.

Así, en la especificación anterior, habrá que crear cinco visitors: Run.java, Execute.java, Value.java, Address.java y Metadata.java.

java
run = new Run();
execute = new Execute();
value = new Value();
address = new Address();
metadata = new Metadata();

A la hora de implementar una plantilla, cuando se quiera invocar a una función, habrá que indicar qué visitor de los anteriores se quiere que recorra el nodo indicado.

Esto cambia la forma de usar el método accept. Es decir, hasta ahora, la forma habitual de recorrer un nodo era usando this como primer parámetro. Por tanto, se usaba el visitor actual (el único que había) para recorrer el nodo.

java
node.accept(this, param);

Pero ahora, que hay varios visitors, a la hora de aplicar una función a un nodo, habrá que sustituir this por el visitor correspondiente. Por ejemplo, supóngase la siguiente plantilla:

execute⟦statement⟧ execute⟦assignment → left:expression right:expression⟧ =
     address⟦left⟧
     value⟦right⟧
     STORE

Habría que implementar esta plantilla, suponiendo los visitors creados anteriormente, de la siguiente forma (nótese que se sustituyen los this por los visitors correspondientes address y value):

java
left.accept(address, null);
right.accept(value, null);
out("store")

Creación de la Infraestructura

A la hora de crear la infraestructura multivisitor, aunque se puede hacer a mano, en este capítulo se usará de nuevo VGen. En cualquier caso, como ha ocurrido en todas las situaciones anteriores, el resultado final será similar al que se hubiera hecho a mano.

Para indicar a VGen que genere la infraestructura multivisitor para una especificación de código, se añade lo siguiente a AbstractGrammar.txt.

java
CATEGORIES
... // ommited for brevity

NODES
...

ATTRIBUTE GRAMMAR Identification
...

ATTRIBUTE GRAMMAR TypeChecking
...

ATTRIBUTE GRAMMAR MemoryAllocation
...

CODE SPECIFICATION Mapl
run[program]
execute[statement]
value[expression]
address[expression]
metadata[program]
metadata[varDefinition]

En el fichero anterior, se ha creado una nueva sección CODE SPECIFICATION en la que se definen las funciones de la especificación. Nótese que es simplemente copiar la columna de la derecha de la especificación de código.

Con lo anterior, después de pasar por VGen, se obtendrán las siguientes clases en /vgen.output/skeletons.codefunctions/

bash
 codegeneration
    └── mapl
        ├─  MaplCodeSpecification.java
        ├─  AbstractCodeFunction.java
        └── codefunctions
               ├─ Address.java
               ├─ Execute.java
               ├─ Metadata.java
               ├─ Run.java
               └─ Value.java

Estas clases forman el esqueleto de la infraestructura multivisitor para la especificación de código. Para aprovecharse de todo el código generado, el primer paso es copiar estas clases a su sitio definitivo en el proyecto. Para ello, hay que copiar la carpeta /vgen.output/skeletons.codefunctions/codegeneration/ a su nueva ubicación en /src/codegeneration/, sobreescribiendo las clases que haya con el mismo nombre.

A continuación se verá brevemente el contenido de cada una de las clases copiadas. Aunque, si se desea, se puede ignorar el resto de este capítulo y pasar directamente a la implementación en el siguiente.

TIP

Usando varias secciones CODE SPECIFICATION se podría generar código para distintas especificaciones de código. Se podría, así, generar código para distintas arquitecturas destino (cada una se estaría en su propio package).

Clases de las Funciones

Las clases con el mismo nombre que una función de código son el lugar donde habrá que implementar las plantillas de la función correspondiente.

Como muestra de todas ellos, se incluye aquí el código de la clase Execute.java.

java
public class Execute extends AbstractCodeFunction {
    public Execute(MaplCodeSpecification specification) {
        super(specification);
    }

	// class Print(Expression expression)
	public Object visit(Print print, Object param) {

		// value(print.getExpression());
		// address(print.getExpression());

		out("<instruction>");

		return null;
	}

	// class Assignment(Expression left, Expression right)
	public Object visit(Assignment assignment, Object param) {

		// value(assignment.getLeft());
		// address(assignment.getLeft());

		// value(assignment.getRight());
		// address(assignment.getRight());

		out("<instruction>");

		return null;
	}

}

Para agilizar la implementación, VGen ha generado en cada clase:

  • Un método visit por cada nodo que pertenezca al dominio de la función. Por ejemplo, en Execute.java se han generados métodos visit solo para Print y para Assignment. No son necesarios los demás visit. De hecho, si se invocan, lanza automáticamente una excepción.
  • Dentro de cada método visit, se recuerda, mediante comentarios, qué funciones se podrían aplicar a los hijos del nodo, es decir, a los dominios de qué funciones pertenecen los hijos. Por ejemplo, en el método visit de Assignment se recuerda que se podrían aplicar las funciones value y address a los hijos del nodo.
  • Se añade también un ejemplo de invocación del método out para escribir en el fichero de salida.

Clase AbstractCodeFunction.java

La clase AbstractCodeFunction es la clase padre de todas las clases de las funciones de código, es decir, de las clases del apartado anterior. En ella está el código común a todas ellas. Principalmente:

  • El método out para escribir en el fichero de salida.
  • Un método para invocar a cada función de código (métodos run, execute, value, address y metadata).
java
public abstract class AbstractCodeFunction extends ExceptionThrowerVisitor {

    // Métodos auxiliares para la escritura de código
    protected void out(String line) {
        specification.getPrintWriter().println(line);
    }

    //# Métodos específicos para cada función de código
    protected Object run(AST node, Object param) {
        return node.accept(specification.getVisitor(MaplCodeSpecification.CodeFunction.RUN), param);
    }

    protected Object execute(AST node, Object param) {
        return node.accept(specification.getVisitor(MaplCodeSpecification.CodeFunction.EXECUTE), param);
    }

    protected Object value(AST node, Object param) {
        return node.accept(specification.getVisitor(MaplCodeSpecification.CodeFunction.VALUE), param);
    }

    protected Object address(AST node, Object param) {
        return node.accept(specification.getVisitor(MaplCodeSpecification.CodeFunction.ADDRESS), param);
    }

    protected Object metadata(AST node, Object param) {
        return node.accept(specification.getVisitor(MaplCodeSpecification.CodeFunction.METADATA), param);
    }

    // ... omitted for brevity
}

Esta clase es el lugar donde, además del código común a todos los visitors ya generado, se podrá ubicar el código adicional común que surja en la implementación.

Clase MaplCodeSpecification.java

La clase MaplCodeSpecification, que en la versión monovisitor implementaba todas las funciones de código, aquí simplemente es la coordinadora de todos los visitors. Los crea en el constructor y, mediante su método process(AST), lanza el primero de ellos.

java
public class MaplCodeSpecification {

    enum CodeFunction { RUN, EXECUTE, VALUE, ADDRESS, METADATA }

    private Map<CodeFunction, Visitor> functions;

    public MaplCodeSpecification(String sourceFile, Writer writer) {

        // ...

        this.functions = new EnumMap<>(CodeFunction.class);

		functions.put(CodeFunction.RUN, new Run(this));
		functions.put(CodeFunction.EXECUTE, new Execute(this));
		functions.put(CodeFunction.VALUE, new Value(this));
		functions.put(CodeFunction.ADDRESS, new Address(this));
		functions.put(CodeFunction.METADATA, new Metadata(this));
    }

    //$ Entry point  ---------
    public void process(AST ast) {
        ast.accept(functions.get(CodeFunction.RUN), null);
    }

    // Auxiliary method for the visitors of the functions
    public Visitor getVisitor(CodeFunction function) {
        return functions.get(function);
    }

    ...
}

Cuando los distintos visitors necesiten acceder a otro, utilizarán esta clase para localizarlo a través del método getVisitor.

Una vez que se tiene la infraestructura, en los siguientes apartados se irán completando cada uno de estos métodos visit siguiendo la especificación de codigo.