Skip to content

Versión Básica

Creación del Parser

Una solución sencilla para construir un parser con Antlr sería la siguiente:

java
grammar Grammar;

import Tokenizer;

@header {
	    import ast.*;
	    import ast.type.*;
	    import ast.statement.*;
	    import ast.expression.*;
}

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.text); }
	;

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.text, $right.ast); }
	| left=expression op=('+' | '-') right=expression	{ $ast = new Arithmetic($left.ast, $op.text, $right.ast); }
	| '(' expression ')'								{ $ast = $expression.ast; }
	| IDENT												{ $ast = new Variable($IDENT.text); }
	| INT_LITERAL										{ $ast = new IntLiteral($INT_LITERAL.text); }
	| FLOAT_LITERAL										{ $ast = new FloatLiteral($FLOAT_LITERAL.text); }
	;

TIP

Para la obtención del fichero anterior, se han seguido los criterios que se explican en los siguientes enlaces y cuya lectura se recomienda:

Como siempre, hay que ejecutar de nuevo Antlr para que genere la versión actualizada del analizador sintáctico.

bash
c:\mlang> antlr.bat

Ejecución

Antes de ejecutar el programa, se modifica input.txt para tener una entrada de prueba:

c
DATA
    float price;
    int width;

CODE
    width = 25 * (2 + 1);
    print width;

    price = 5.0;
    print price / 2.0;

Ahora ya se puede ejecutar el compilador y se obtiene la siguiente salida:

bash
Compiler started.

Compiler finished.

AstPrinter: Fichero 'AST.html' generado.

Aunque no pueda apreciarse observando sólo la ejecución del programa, esta versión del compilador es más avanzada que la del capítulo anterior. La diferencia es que ahora se ha creado un árbol en memoria que representa la estructura encontrada en intput.txt.

Pero como el árbol se crea en memoria y se pierde al acabar la ejecución, se debe añadir alguna forma de poder imprimir dicho árbol para su validación. Hay dos opciones:

  • Si los nodos se crearon de forma manual, se tendrán que implementar un recorrido del árbol que lo imprima en pantalla o en un fichero para su revisión.
  • Si se usó VGen, este ya ha generado una clase AstPrinter que realiza esta tarea. El árbol generado se puede examinar en el fichero AST.html que se ha generado al ejecutar el compilador.

El contenido de AST.html es el siguiente:

Program =
|  varDefinitions List<VarDefinition> =
|  .  0: VarDefinition = . . . . . . . . . . . . . . . [null null]   
|  .  |  type FloatType = . . . . . . . . . . . . . . .[null null]   
|  .  |  name = "price" string
|  .  1: VarDefinition = . . . . . . . . . . . . . . . [null null]   
|  .  |  type IntType = . . . . . . . . . . . . . . . .[null null]   
|  .  |  name = "width" string
|  statements List<Statement> =
|  .  0: Assignment = . . . . . . . . . . . . . . . . .[null null]   
|  .  |  left Variable = . . . . . . . . . . . . . . . [null null]   
|  .  |  .  name = "width" string
|  .  |  right Arithmetic = . . . . . . . . . . . . . .[null null]   
|  .  |  .  left IntLiteral = . . . . . . . . . . . . .[null null]   
|  .  |  .  |  intValue = 25 int
|  .  |  .  operator = "*" string
|  .  |  .  right Arithmetic = . . . . . . . . . . . . [null null]   
|  .  |  .  |  left IntLiteral = . . . . . . . . . . . [null null]   
|  .  |  .  |  .  intValue = 2 int
|  .  |  .  |  operator = "+" string
|  .  |  .  |  right IntLiteral = . . . . . . . . . . .[null null]   
|  .  |  .  |  .  intValue = 1 int
|  .  1: Print = . . . . . . . . . . . . . . . . . . . [null null]   
|  .  |  expression Variable = . . . . . . . . . . . . [null null]   
|  .  |  .  name = "width" string
|  .  2: Assignment = . . . . . . . . . . . . . . . . .[null null]   
|  .  |  left Variable = . . . . . . . . . . . . . . . [null null]   
|  .  |  .  name = "price" string
|  .  |  right FloatLiteral = . . . . . . . . . . . . .[null null]   
|  .  |  .  floatValue = 5.0 float
|  .  3: Print = . . . . . . . . . . . . . . . . . . . [null null]   
|  .  |  expression Arithmetic = . . . . . . . . . . . [null null]   
|  .  |  .  left Variable = . . . . . . . . . . . . . .[null null]   
|  .  |  .  |  name = "price" string
|  .  |  .  operator = "/" string
|  .  |  .  right FloatLiteral = . . . . . . . . . . . [null null]   
|  .  |  .  |  floatValue = 2.0 float

Como puede observarse, todos los nodos tienen a null sus posiciones de línea y columna. Esto, realmente, no es un requisito necesario para del proceso de traducción. Pero sí que es tremendamente útil, especialmente, para dar mejores mensajes de error al usuario del compilador (se podrá indicar en qué linea del fichero se encuentra cada error). Las siguientes versiones arreglarán este inconveniente.