Skip to content

Versión Regular

Creación del Parser

Esta segunda solución aprovechará el código opcional que genera automáticamente VGen, el cual añade automáticamente la información de línea y columna a los nodos.

Para ello, hay que cambiar la forma en la que se pasan algunos argumentos a los constructores de los nodos. Cuando se vaya a pasar un terminal, en vez de pasar $TOKEN.text (sólo el lexema), se pasará el propio $TOKEN (el token entero). De esta forma, el nodo tendrá la información de la posición en el fichero.

Por ejemplo, donde antes aparecía varDefinition como:

java
varDefinition ...
	: type IDENT ';' { $ast = new VarDefinition($type.ast, $IDENT.text); }

Ahora se haría de la siguiente manera:

java
varDefinition ...
	: type IDENT ';' { $ast = new VarDefinition($type.ast, $IDENT); }

Haciendo este cambio en todas las reglas necesarias, se obtendría la nueva versión de la gramática en la que las líneas resaltadas destacan dónde se ha eliminado el .text.

java
program returns[Program ast]
	: 'DATA' varDefinitions 'CODE' statements EOF { $ast = new Program($varDefinitions.list, $statements.list); }
	;

varDefinitions returns[List<VarDefinition> list = new ArrayList<VarDefinition>()]
	: (varDefinition { $list.add($varDefinition.ast); })*
	;

statements returns[List<Statement> list = new ArrayList<Statement>()]
	: (statement { $list.add($statement.ast); })*
	;

varDefinition returns[VarDefinition ast]
	: type IDENT ';' { $ast = new VarDefinition($type.ast, $IDENT); }
	;

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

statement returns[Statement ast]
	: 'print' expression ';'					{ $ast = new Print($expression.ast); }
	| left=expression '=' right=expression ';'	{ $ast = new Assignment($left.ast, $right.ast); }
	;

expression returns[Expression ast]
	: left=expression op=('*' | '/') right=expression	{ $ast = new Arithmetic($left.ast, $op, $right.ast); }
	| left=expression op=('+' | '-') right=expression	{ $ast = new Arithmetic($left.ast, $op, $right.ast); }
	| '(' expression ')'								{ $ast = $expression.ast; }
	| IDENT												{ $ast = new Variable($IDENT); }
	| INT_LITERAL										{ $ast = new IntLiteral($INT_LITERAL); }
	| FLOAT_LITERAL										{ $ast = new FloatLiteral($FLOAT_LITERAL); }
	;

Esto no solo hace que los nodos terminales tengan la posición del fichero, sino que VGen hace que los nodos intermedios también obtengan sus posiciones a partir de sus hijos, quedando así todos los nodos con esta información.

Ejecución

Si ahora se vuelve a usar antlr.bat y se ejecuta el programa, se obtiene la nueva versión del árbol con las posiciones del fichero:

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> =
|  .  0: Assignment = . . . . . . . . . .[6:5 6:24]    width = 25 * (2 + 1);
|  .  |  left Variable = . . . . . . . . [6:5 6:10]    width = 25 * (2 + 1);
|  .  |  .  name = "width" string
|  .  |  right Arithmetic = . . . . . . .[6:13 6:24]   width = 25 * (2 + 1);
|  .  |  .  left IntLiteral = . . . . . .[6:13 6:15]   width = 25 * (2 + 1);
|  .  |  .  |  intValue = 25 int
|  .  |  .  operator = "*" string
|  .  |  .  right Arithmetic = . . . . . [6:19 6:24]   width = 25 * (2 + 1);
|  .  |  .  |  left IntLiteral = . . . . [6:19 6:20]   width = 25 * (2 + 1);
|  .  |  .  |  .  intValue = 2 int
|  .  |  .  |  operator = "+" string
|  .  |  .  |  right IntLiteral = . . . .[6:23 6:24]   width = 25 * (2 + 1);
|  .  |  .  |  .  intValue = 1 int
|  .  1: Print = . . . . . . . . . . . . [7:11 7:16]   print width;
|  .  |  expression Variable = . . . . . [7:11 7:16]   print width;
|  .  |  .  name = "width" string
|  .  2: Assignment = . . . . . . . . . .[9:5 9:16]    price = 5.0;
|  .  |  left Variable = . . . . . . . . [9:5 9:10]    price = 5.0;
|  .  |  .  name = "price" string
|  .  |  right FloatLiteral = . . . . . .[9:13 9:16]   price = 5.0;
|  .  |  .  floatValue = 5.0 float
|  .  3: Print = . . . . . . . . . . . . [10:11 10:22] print price / 2.0;
|  .  |  expression Arithmetic = . . . . [10:11 10:22] print price / 2.0;
|  .  |  .  left Variable = . . . . . . .[10:11 10:16] print price / 2.0;
|  .  |  .  |  name = "price" string
|  .  |  .  operator = "/" string
|  .  |  .  right FloatLiteral = . . . . [10:19 10:22] print price / 2.0;
|  .  |  .  |  floatValue = 2.0 float

En la columna central, entre corchetes, puede verse la posición inicial y final de cada nodo. Y, en cada posición, se ve la línea y columna separadas por dos puntos. A la derecha de las posiciones se muestra además, resaltado en verde, el texto contenido entre dichas posiciones del fichero.

Debido a que se consigue más funcionalidad y, además, escribiendo menos, se recomienda esta versión en vez de la versión básica.