Skip to content

Implementación de las Reglas

El comprobador de tipos se implementará como un visitor que recorrerá el árbol y comprobará que se cumplen todos los predicados. Bastará uno de ellos que no se cumpla para que el programa no sea válido.

Para implementar cada método visit de este recorrido, se tomará como guía la fila de la tabla de reglas del nodo que se está visitando. La implementación de cada métodos visit, normalmente, seguirá la siguiente estructura:

java
visit(Node parent) {

    for each child of parent {
        <set child inherited attributes>
        visit(child)
    }

    <check predicates - 2nd column>
    <set parent synthesized attributes - 3rd column>
}

La estructura anterior sólo es aplicable en la implementación de gramáticas L-atribuidas, condición que se cumple en nuestra gramática atribuida (de hecho, es S-atribuida, por lo que se podría omitir la parte de asignar los atributos heredados).

Una vez establecida la estructura de cada método visit, se puede proceder a implementar cada uno de ellos siguiendo la tabla de reglas de la gramática atribuida. De nuevo, al igual que ocurría en la etapa de identificación, para hacer esto, hay tres opciones:

  • Implementarlo de manera manual.
  • Utilizar la plantilla para crear visitors que VGen ha generado en /vgen.output/skeleton.visitor/ llamada SkeletonForNewVisitors.txt.
  • Definir la gramática atribuida en /specifications/AbstractGrammar.txt para que VGen genere un esqueleto específico para dicha gramática.

Dado que ya se definió una gramática atribuida llamada TypeChecking en el fichero de entrada de VGen, se optará de nuevo por la tercera opción.

java
CATEGORIES
...

NODES
program -> ...
...

// -----------------------
ATTRIBUTE GRAMMAR Identification
variable -> varDefinition;

// -----------------------
ATTRIBUTE GRAMMAR TypeChecking
expression -> type;
expression -> lvalue:boolean;

Al hacer lo anterior se obtuvo en /vgen.output/skeleton.attgrammars/ un fichero llamado TypeChecking.java. Se muestra a continuación parte de su contenido:

java
public class TypeChecking extends DefaultVisitor {

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

		// program.getVarDefinitions().forEach(varDefinition -> varDefinition.accept(this, param));
		// program.getStatements().forEach(statement -> statement.accept(this, param));
		super.visit(program, param);

		return null;
	}
    ...

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

		// arithmetic.getLeft().accept(this, param);
		// arithmetic.getRight().accept(this, param);
		super.visit(arithmetic, param);

		// TODO: Remember to initialize SYNTHESIZED attributes <-----
		// arithmetic.setType(?);
		// arithmetic.setLvalue(?);
		return null;
	}

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

		// TODO: Remember to initialize SYNTHESIZED attributes <-----
		// variable.setType(?);
		// variable.setLvalue(?);
		return null;
	}

	// class IntLiteral(int intValue)
	// phase TypeChecking { Type type, boolean lvalue }
	public Object visit(IntLiteral intLiteral, Object param) {

		// TODO: Remember to initialize SYNTHESIZED attributes <-----
		// intLiteral.setType(?);
		// intLiteral.setLvalue(?);
		return null;
	}
}

Nótese como en todos los visit anteriores que corresponden a nodos expression, VGen ha añadido comentarios recordando los métodos que hay que invocar para asignar dichos atributos.

La forma de proceder con el esqueleto anterior, de nuevo, es:

  • Copiar todos los métodos visit del esqueleto anterior sobre la clase /src/semantic/TypeChecking.java.
  • Borrar todo método visit de nodos que no tengan ninguna regla en su fila de la tabla de reglas.
  • En los métodos restantes, seguir el esquema de implementación de las gramáticas L-atribuidas visto anteriormente.

La clase TypeChecking, aplicando los pasos anteriores, quedaría así:

java
public class TypeChecking extends DefaultVisitor {

    private ErrorManager errorManager;

    public TypeChecking(ErrorManager errorManager) {
        this.errorManager = errorManager;
    }

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

        super.visit(assignment, param);

        predicate(sameType(assignment.getLeft(), assignment.getRight()),
            "The expression types don't match", assignment);

        predicate(assignment.getLeft().isLvalue(),
                "The expression on the left is not modifiable",
                assignment.getLeft());
        return null;
    }

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

        super.visit(arithmetic, param);

        predicate(sameType(arithmetic.getLeft(), arithmetic.getRight()),
            "The expression types don't match", arithmetic);

        arithmetic.setType(arithmetic.getLeft().getType());
        arithmetic.setLvalue(false);
        return null;
    }

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

        variable.setType(variable.getVarDefinition().getType());
        variable.setLvalue(true);
        return null;
    }

    // class IntLiteral(int intValue)
    // phase TypeChecking { Type type, boolean lvalue }
    public Object visit(IntLiteral intLiteral, Object param) {

        intLiteral.setType(new IntType());
        intLiteral.setLvalue(false);
        return null;
    }

    // class FloatLiteral(float floatValue)
    // phase TypeChecking { Type type, boolean lvalue }
    public Object visit(FloatLiteral floatLiteral, Object param) {

        floatLiteral.setType(new FloatType());
        floatLiteral.setLvalue(false);
        return null;
    }

    //# ----------------------------------------------------------
    //# Auxiliary methods (optional)

    private boolean sameType(Expression a, Expression b) {
        return (a.getType().getClass() == b.getType().getClass());
    }

    private void notifyError(String errorMessage, Position position) {
        errorManager.notify("Type Checking", errorMessage, position);
    }

    private void notifyError(String msg) {
        errorManager.notify("Type Checking", msg);
    }

    private boolean predicate(boolean condition, String errorMessage, Position position) {
        if (!condition) {
            notifyError(errorMessage, position);
            return false;
        }
        return true;
    }

    private boolean predicate(boolean condition, String errorMessage, AST node) {
        return predicate(condition, errorMessage, node.start());
    }
}