Skip to content

Implementación de las Reglas

Para finalizar la etapa de identificación, queda por implementar el recorrido del árbol siguiendo como guía la tabla de reglas de su gramática atribuida.

Para hacer esto, hay tres opciones:

  • Implementar de manera manual un visitor de la forma habitual: hacer una nueva clase que derive de Visitor e implemente todos sus métodos visit con el código de las reglas intercalado con el código de recorrido de los hijos.
  • 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 reglas.
  • Si se define una gramática atribuida en /specifications/AbstractGrammar.txt, VGen genera un esqueleto específico para dicha gramática con código más completo que el esqueleto anterior.

Dado que se definió una gramática atribuida llamada Identification en el fichero de entrada de VGen, se puede optar por la tercera opción, ya que aunque el resultado de hacerlo a mano sería el mismo, aquí se va aprovechar VGen para obtener gran parte del código rutinario ya implementado.

java
CATEGORIES
...

NODES
program -> varDefinition* statement*;

...

ATTRIBUTE GRAMMAR Identification
variable -> varDefinition;

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

java
public class Identification extends DefaultVisitor {

	// class Program(List<VarDefinition> varDefinitions, List<Statement> statements)
	@Override
	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 VarDefinition(Type type, String name)
	@Override
	public Object visit(VarDefinition varDefinition, Object param) {

		// varDefinition.getType().accept(this, param);
		super.visit(varDefinition, param);

		return null;
	}

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

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

		return null;
	}

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

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

}

La forma de proceder con el esqueleto de una gramática atribuida es:

  • Se copian todos los métodos visit del esqueleto sobre la clase que vaya a implementar la gramática atribuida. En nuestro caso, se copiarían sobre /src/semantic/Identification.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, añadir el código de las reglas de la gramática atribuida. Nótese que en los nodos que tienen atributos, VGen inserta comentarios recordando que hay que inicializar sus atributos de esta etapa — ver visit(Variable) en el código anterior.

La clase Identification.java, aplicando los pasos anteriores, quedaría así:

java
public class Identification extends DefaultVisitor {

    private ErrorManager errorManager;
    private Map<String, VarDefinition> variables = new HashMap<>();

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

    // # ----------------------------------------------------------
    // class VarDefinition(Type type, String name)
    public Object visit(VarDefinition varDefinition, Object param) {
        var definition = variables.get(varDefinition.getName());
        if (definition != null)
            notifyError("Variable already defined: " + varDefinition.getName(), varDefinition);
        else
            variables.put(varDefinition.getName(), varDefinition);
        return null;
    }

    // class Variable(String name)
    // phase Identification { VarDefinition varDefinition }
    public Object visit(Variable variable, Object param) {

        var definition = variables.get(variable.getName());
        if (definition == null)
            notifyError("Undefined variable: " + variable.getName(), variable);
        else
            variable.setVarDefinition(definition);
        return null;
    }

    // # --------------------------------------------------------
    private void notifyError(String msg, AST node) {
        errorManager.notify("Identification", msg, node.start());
    }
}