Skip to content

Implementación Monovisitor

Estructura

En la implementación de una especificación de código con un sólo visitor, habrá métodos visit que tendrán que implementar más de una plantilla de código. Por tanto, al invocar dicho método, habrá que indicar cuál de ellas debe ejecutarse, es decir, qué función de código se le quiere aplicar al nodo.

Por ejemplo, al símbolo variable se le puede aplicar tanto la función de código value como la función de código address.

value⟦expression ...

value⟦variable → name:string⟧ =
     address⟦variable⟧
     LOAD<variable.type>

address⟦expression⟧ ...

address⟦variable → name:string⟧ =
     PUSHA {variable.varDefinition.address}

Por ello, se define el siguiente enum:

java
enum ExpressionFunctions { ADDRESS, VALUE }

Con ello, se utiliza el segundo parámetro del método accept para indicar qué función se quiere aplicar a la variable. El método visit(Variable) implementa ambas plantillas y, en función de su segundo parámetro, decide cual ejecutar.

java
public Object visit(Variable variable, Object param) {

    if (((ExpressionFunctions) param) == ExpressionFunctions.VALUE) {
        variable.accept(this, ExpressionFunctions.ADDRESS);
        out("load", variable.getType());

    } else {
        out("pusha " + variable.getVarDefinition().getAddress());
    }
    return null;
}

Implementación

Para hacer el visitor, aunque se puede empezar con un visitor vacío, se recomienda utilizar la plantilla para crear visitors que VGen ha generado en /vgen.output/skeleton.visitor/ llamada SkeletonForNewVisitors.txt. Este fichero ya incluye la implementación de todos los métodos visit con el código de recorrido de los hijos, por lo que solo habría que añadir el código de las plantillas.

El código final de la especificación con la implementación monovisitor es el siguiente:

java
enum ExpressionFunctions { ADDRESS, VALUE }
enum ProgramFunctions { RUN, METADATA }

public class MaplCodeSpecification extends DefaultVisitor {

    private String sourceFile;
    private PrintWriter writer;
    private Map<String, String> instruccion = new HashMap<>();

    public MaplCodeSpecification(String sourceFile, Writer writer) {
        this.sourceFile = sourceFile;
        this.writer = new PrintWriter(writer);

        instruccion.put("+", "add");
        instruccion.put("-", "sub");
        instruccion.put("*", "mul");
        instruccion.put("/", "div");
    }

    public void process(AST ast) {
        ast.accept(this, ProgramFunctions.RUN);
    }

    // class Program(List<VarDefinition> varDefinitions, List<Statement> statements)
    @Override
    public Object visit(Program program, Object param) {

        if (((ProgramFunctions) param) == ProgramFunctions.RUN) {
            program.accept(this, ProgramFunctions.METADATA);
            program.getStatements().forEach(statement -> statement.accept(this, param));
            out("halt");

        } else {
            assert (param == ProgramFunctions.METADATA);

            out("#source \"" + sourceFile + "\"");
            program.getVarDefinitions().forEach(varDefinition -> varDefinition.accept(this, param));
        }

        return null;
    }

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

        line(print);
        print.getExpression().accept(this, ExpressionFunctions.VALUE);
        out("out", print.getExpression().getType());

        return null;
    }

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

        line(assignment);
        assignment.getLeft().accept(this, ExpressionFunctions.ADDRESS);
        assignment.getRight().accept(this, ExpressionFunctions.VALUE);
        out("store", assignment.getLeft().getType());

        return null;
    }

    // class Arithmetic(Expression left, String operator, Expression right)
    // phase TypeChecking { Type type, boolean lvalue }
    @Override
    public Object visit(Arithmetic arithmetic, Object param) {

        assert (param == ExpressionFunctions.VALUE);
        arithmetic.getLeft().accept(this, ExpressionFunctions.VALUE);
        arithmetic.getRight().accept(this, ExpressionFunctions.VALUE);
        out(instruccion.get(arithmetic.getOperator()), arithmetic.getType());

        return null;
    }

    // class Variable(String name)
    // phase Identification { VarDefinition varDefinition }
    // phase TypeChecking { Type type, boolean lvalue }
    @Override
    public Object visit(Variable variable, Object param) {

        if (((ExpressionFunctions) param) == ExpressionFunctions.VALUE) {
            variable.accept(this, ExpressionFunctions.ADDRESS);
            out("load", variable.getType());

        } else {
            assert (param == ExpressionFunctions.ADDRESS);

            out("pusha " + variable.getVarDefinition().getAddress());
        }

        return null;
    }

    // class IntLiteral(int intValue)
    // phase TypeChecking { Type type, boolean lvalue }
    @Override
    public Object visit(IntLiteral intLiteral, Object param) {
        assert (param == ExpressionFunctions.VALUE);

        out("push " + intLiteral.getIntValue());

        return null;
    }

    // class FloatLiteral(float floatValue)
    // phase TypeChecking { Type type, boolean lvalue }
    @Override
    public Object visit(FloatLiteral floatLiteral, Object param) {
        assert (param == ExpressionFunctions.VALUE);

        out("pushf " + floatLiteral.getFloatValue());

        return null;
    }

    // class VarDefinition(Type type, String name)
    // phase MemoryAllocation { int address }
    @Override
    public Object visit(VarDefinition varDefinition, Object param) {
        out("#GLOBAL " + varDefinition.getName() + ":" + maplType(varDefinition.getType()));

        return null;
    }

    // # ----------------------------------------------------------
    // Métodos auxiliares recomendados (opcionales) -------------

    // Imprime una línea en el fichero de salida
    private void out(String instruction) {
        writer.println(instruction);
    }

    private void out(String instruction, Type type) {
        out(instruction + suffixFor(type));
    }

    private String suffixFor(Type type) {
        if (type instanceof IntType)
            return "i";
        if (type instanceof FloatType)
            return "f";

        // Sealed classes + pattern matching would avoid this situation. Those features were not
        // finished when this code was implemented
        throw new IllegalArgumentException("Unknown Type: " + type);
    }

    private String maplType(Type type) {
        if (type instanceof IntType)
            return "int";
        if (type instanceof FloatType)
            return "float";

        // Sealed classes + pattern matching would avoid this situation. Those features were not
        // finished when this code was implemented
        throw new IllegalArgumentException("Unknown Type: " + type);
    }

    private void line(AST node) {
        if (node.end() != null)
            out("\n#line " + node.end().getLine());
    }
}

Como puede observarse, se han implementado en esta clase los métodos out(String, Type) y maplType(Type).

  • El primero se utiliza para imprimir la variante de cada instrucción en función del tipo (apoyándose en el método suffixFor(Type)).
  • La segunda es la función auxiliar que se definión en la especificación de código.

Siguiendo el criterio del patrón visitor, se ha intentando evitar que los visitors añadan métodos propios a los nodos. Tanto el método out(String, type) como maplType(Type) son específico para la generación de MAPL y, en un futuro, podría cambiarse a otra arquitectura destino. En ese caso, no se querría que quedaran en el AST métodos de la generación de MAPL.