Skip to content

Apéndice. Posiciones del Fichero

Si se observa el fichero AST.html, incluso en las dos últimas versiones del parser en las que el AST incluye las posiciones del fichero, puede observarse que algunos nodos no tienen dicha información y en su lugar aparece null:

Program =
|  varDefinitions List<VarDefinition> =
|  .  0: VarDefinition = . . . . . . . . [2:11 2:16]   float price;
|  .  |  type FloatType = . . . . . . . .[null null]   
|  .  |  name = "price" string
|  .  1: VarDefinition = . . . . . . . . [3:9 3:14]    int width;
|  .  |  type IntType = . . . . . . . . .[null null]   
|  .  |  name = "width" string
|  statements List<Statement> = ...

Esto se debe a la forma en la que se obtiene las posiciones de cada nodo con VGen. Básicamente, el proceso consiste en:

  • Los nodos terminales obtienen su posición de su token.
  • Los nodos intermedios obtienen su posición de sus hijos. La posición inicial se obtiene a partir de la de su primer hijo y la posición final a partir de la de su último hijo (si algún hijo no tiene la posición buscada se pasa al siguiente).

Y así, de abajo hacia arriba, todos los nodos obtienen de manera automática y sin ningún código adicional toda la gestión de posiciones.

El proceso anteriormente descrito es el que se hace en el método updatePositions que VGen generó en cada constructor:

java
//	arithmetic: expression -> left:expression operator:string right:expression
public class Arithmetic extends AbstractExpression  {

    ...

	public Arithmetic(Expression left, String operator, Expression right) {

		this.left = left;
		this.operator = operator;
		this.right = right;

		updatePositions(left, operator, right);
	}

}

Por tanto, las dos situaciones en las que un nodo quedará sin información de posición son:

  • Que el nodo no tenga hijos (y por tanto no pueda extraer de ellos posiciones).
  • Que ninguno de los hijos tenga información de posición.

Estas dos situaciones (especialmente la segunda) son muy poco frecuentes.

En el caso de los tipos, se presenta la primera de las dos situaciones anteriores: no tienen ningún hijo (y, por tanto, no se pasa ningún hijo en su constructor). Este es el motivo por el que no tienen información de posición.

java
...
type returns[Type ast]
	: 'int'		{ $ast = new IntType(); }
	| 'float'	{ $ast = new FloatType(); }
	;

Esto no supone un problema en la práctica. No es necesario que todos los nodos tengan información de posición; basta con que lo tengan aquellos que se van a utilizar para dar mensajes de error o generar código. Y, en este caso, estos nodos no se van a utilizar para ninguna de las dos funciones. Por tanto, sería correcto dejarlo como está.

De todas formas, si se quisiera información más precisa, VGen ha generado en los nodos un método updatePositions que permite pasarle uno o más símbolos (independientemente de que sean hijos suyos o no) para que extraiga de ellos sus posiciones.

La forma más sencilla es aprovechar el token que ha servido para reconocer el tipo (int o float) y, aunque no se vaya a añadir al árbol, extraer sus posiciones mediante el método updatePositions para copiarlas en el nodo indicado:

java
...
type returns[Type ast]
	: token='int'	{ $ast = new IntType(); $ast.updatePositions($token); }
	| token='float'	{ $ast = new FloatType(); $ast.updatePositions($token); }
	;

Otra forma de hacerlo, sin tener que usar etiquetas, es utilizar $ctx.start. El atributo start lo guarda ANTLR en el contexto de cada regla apuntando siempre al primer token de dicha regla. En este caso, serán los tokens int y float (por lo que hace lo mismo que la solución anterior).

java
...
type returns[Type ast]
	: 'int'		{ $ast = new IntType(); $ast.updatePositions($ctx.start); }
	| 'float'	{ $ast = new FloatType(); $ast.updatePositions($ctx.start); }
	;

Si después de volver a pasar Grammar.g4 por ANTLR se ejecuta el código resultante, se obtiene este otro árbol:

Program =
|  varDefinitions List<VarDefinition> =
|  .  0: VarDefinition = . . . . . . . . . . [2:5 2:16]    float price;
|  .  |  type FloatType = . . . . . . . . . .[2:5 2:10]    float price;
|  .  |  name = "price" string
|  .  1: VarDefinition = . . . . . . . . . . [3:5 3:14]    int width;
|  .  |  type IntType = . . . . . . . . . . .[3:5 3:8]     int width;
|  .  |  name = "width" string
|  statements List<Statement> =

Nótese cómo no solo los tipos tienen ahora su posición inicial y final, sino que las posiciones de sus nodos padre, los dos VarDefinition, también han mejorado respecto al árbol anterior, ya que ahora pueden utilizar la información de su hijo tipo (obsérvese el texto que aparece subrayado después de las posiciones de las VarDefinition y compárese con el texto de la versión anterior).