0% encontró este documento útil (0 votos)
96 vistas13 páginas

Compiler (ANTLR)

Este documento introduce ANTLR y proporciona un ejemplo de cómo usarlo para construir un analizador sintáctico para una gramática de expresiones aritméticas. Explica que ANTLR puede generar código para el análisis léxico y sintáctico dado una especificación de gramática. Luego, describe los pasos para especificar la gramática de expresiones, incluyendo la definición de tokens léxicos como números y operadores, y reglas de producción recursivas para expresiones. Finalmente, explica cómo usar el código generado por ANTLR

Cargado por

Sheila
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
96 vistas13 páginas

Compiler (ANTLR)

Este documento introduce ANTLR y proporciona un ejemplo de cómo usarlo para construir un analizador sintáctico para una gramática de expresiones aritméticas. Explica que ANTLR puede generar código para el análisis léxico y sintáctico dado una especificación de gramática. Luego, describe los pasos para especificar la gramática de expresiones, incluyendo la definición de tokens léxicos como números y operadores, y reglas de producción recursivas para expresiones. Finalmente, explica cómo usar el código generado por ANTLR

Cargado por

Sheila
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
Está en la página 1/ 13

Introducción a ANTLR

Oscar Luis Vera Pérez


ÍNDICE
1. ¿Qué es ANTLR? ...................................................................................................... 3
2. ¿Por qué ANTLR? .................................................................................................... 3
3. El ejemplo .................................................................................................................. 4
4. ¿Cómo decirle a ANTLR lo que queremos?........................................................... 4
4.1. Opciones generales de ANTLR........................................................................ 4
4.2. Análisis lexicográfico en ANTLR .................................................................... 5
1. Los tokens de nuestro lenguaje........................................................................ 5
2. El Lexer.............................................................................................................. 5
3. Los tokens en ANTLR ...................................................................................... 5
4. Nuestro lexer...................................................................................................... 6
4.3. Análisis sintáctico en ANTLR.......................................................................... 7
1. Las reglas de producción en ANTLR.............................................................. 7
2. ¿Y la evaluación qué? ....................................................................................... 8
3. Nuestro evaluador............................................................................................. 9
5. ¿Cómo usar el código generado? ........................................................................... 10
5.1. ¿Cómo usar ANTLR?..................................................................................... 10
5.2. ¿Cómo interactuar con el código? ................................................................. 11
6. Conclusiones ............................................................................................................ 12
7. Ejercicios propuestos .............................................................................................. 12
1. ¿Qué es ANTLR?
Primero recordemos cuáles son las fases fundamentales del proceso de compilación:

Generación de
Lexicográfico
fuente
Código

Semántico
Sintáctico
Caracteres Tokens AST AST

Análisis

Análisis

Análisis

código

Las dos primeras etapas de este proceso, pueden resultar bastante engorrosas si se
realizan a “mano” y en realidad son bastante similares aún cuando estemos
construyendo herramientas de procesamiento de lenguajes diferentes. Por ello es que
desde hace algún tiempo comenzaron a aparecer herramientas capaces de, dada cierta
especificación, generar código fuente eficiente que se encargue de la lógica que rodea
a los análisis lexicográfico y sintáctico.

ANTLR, como su propio nombre nos indica (ANother Tool for Language
Recognition) es una de estas herramientas, que en particular maneja la creación de
mecanismos de reconocimiento para gramáticas LL(k), en realidad pred-LL(k), pero
eso es tema para otra oportunidad.

2. ¿Por qué ANTLR?


Esta herramienta posee características que pudieran ir a favor de su selección a la
hora de enfrentar un proyecto de construcción de una herramienta procesadora de
lenguajes, por ejemplo:
- A diferencia de otras, que solamente se encargan de una de las fases antes
mencionadas, ANTLR se encarga de las dos primeras fases y facilita en gran
medida el trabajo en el resto.
- La especificación de las gramáticas con que trabaja ANTLR es bastante
intuitiva y uniforme.
- El código que genera es. Dentro de lo que se puede esperar bastante legible.
- ANTLR genera código para C++, Java, Python y finalmente C#, razón de
peso por la que algunos la escogerán para su proyecto de Compilación en el
próximo semestre.
- ANTLR es independiente de la plataforma.
3. El ejemplo

Para ver los elementos más importantes en el uso de ANTLR, iremos construyendo
un pequeño evaluador de expresiones a partir de la gramática correspondiente:

E →T + E |T
T → F *T | F
F → n | (E)
donde n representa a todos los enteros no negativos.

En particular asumiremos que a nuestro evaluador se le dará como entrada un archivo


donde, en cada línea se pondrá una cadena que debe representar una expresión
generada por la gramática anterior y en caso de estar correcta imprimimos su
resultado en consola.

4. ¿Cómo decirle a ANTLR lo que queremos?

ANTLR trabaja a partir de un archivo de texto que toma como entrada y que contiene
la información referente a nuestro lenguaje, los elementos lexicográficos, su
gramática además de un conjunto de opciones y especificaciones.
El archivo, es un fichero de texto, editado lo mismo en notepad, notepad++ o Visual
Studio, en nuestro caso le llamaremos expressions.g.

4.1. Opciones generales de ANTLR

En el inicio de nuestro archivo podemos incluir dos secciones, completamente


opcionales:

header
{
//Aquí ponemos el código que se incluirá al inicio de los
fuentes generados
//Fundamentalmente se usa para la inclusión de paquetes o
librerías del lenguaje
//en el que se va a generar el código
//Por ejemplo:
import os //Python
using System.Reflection; //C#
//Para nuestro ejemplo no será necesaria

Luego de esta, podemos incluir otra sección para especificar las opciones
generales de nuestro archivo, en particular el lenguaje que se ha de generar.

options
{
language = “CSharp”; //También “Java” que es el valor por
defecto, “Cpp”, “Python”
//Para nuestro ejemplo usaremos C#
namespace = “ANTLR_Example”
}

Existen muchas otras opciones que puede n encontrarse en en la documentación


de la herramienta, aparte de language, una de las más socorridas es k que
representa el lookahead que usaremos, aunque ANTLR, nos permite variarlo en
distintos momentos de nuestro código.

4.2. Análisis lexicográfico en ANTLR

Para el análisis lexicográfico, la herramienta genera una clase que contiene la


implementación necesaria, pero antes de ver cómo se “escribe” debemos ver los
elementos léxicos de nuestro lenguaje.

1. Los tokens de nuestro lenguaje

Recordemos que identificamos como tokens, los símbolos terminales


presentes en la gramática, luego, para nuestro ejemplo tendríamos los
siguientes:
- +
- *
- (
- )
- n, que representa a todos los enteros no negativos.

2. El Lexer

El analizador lexicográfico, en el argot de la herramienta es llamado lexer y


su especificación sería de la siguiente forma:

class <Identificador del lexer> extends Lexer;

A continuación se puede poner un bloque de opciones cuya estructura es


similar a la del bloque de opciones generales, aunque no permite
necesariamente las mismas; y le siguen las especificaciones de los distintos
tipos de tokens que manejaremos.

3. Los tokens en ANTLR

A cada token o tipo de tokens se le referencia con un identificador, siempre


en MAYÚSCULAS y su “contenido” viene dado por una expresión regular,
la forma general de declararlo sería:
<identificador>: <expresión regular> ;

Veamos algunos ejemplos:


- El paréntesis abierto perteneciente a nuestra gramática se pudiera
resolver como
LP: ‘(‘; //este sería el caso más simple
- Si quisiéramos un token cuyo valor o lexema fuese el carácter ‘a’ o
el ‘b’ sería
T: ’a’ | ’b’;
- Un elemento opcional en el lexema
T: ’a’(‘b’)? ;
- Un elemento que se repita una o más veces
T: (’a’)+ ;
- Un elemento que se repita cero o más veces
T: ’a’(’b’)* ;

Declaremos ahora nuestros enteros no negativos:


N: (’0’|’1’|’2’|’3’|’4’|’5’|’6’|’7’|’8’|’9’)+;

Pero lo podemos hacer mejor que eso, ANTLR incluye una construcción
cuya semántica indica tomar un
elemento dentro de un conjunto consecutivo de caracteres, nuestros enteros
quedarían:
N: (’0’..’9’)+ ;

Dentro de la expresión regular podemos referenciar otros tokens:


protected DIGIT: ’0’..’9’ ; //La palabra clave
protected indica que el token
//solo es visible para el
lexer

N: (DIGIT)+;

Recordemos que un buen analizador lexicográfico, se hace cargo de


“limpiar” los espacios en blanco, tabulaciones y cambios de línea, aunque
para nuestro ejemplo los cambios de línea no nos interesan. ¿Cómo hacemos
esto?

WS: ( ' ' | '\t')


{ _ttype = Token.SKIP; } //De esta forma le decimos
que limpie estos elementos
;
Debemos hacer notar que la herramienta define un conjunto de tokens
especiales, muy útiles como EOF que indica el fin del fichero.

4. Nuestro lexer

Finalmente el analizador para nuestro ejemplo quedaría:

class ExpressionLexer extends Lexer;


LP : ’(’ ;
RP : ’)’ ;
PLUS: ’+’ ;
MULT: ’*’ ;
protected
DIGIT: ’0’..’9’;
N : (DIGIT)+ ;
WS: ( ' ' | '\t' | '\r')
{ _ttype = Token.SKIP; }
;

4.3. Análisis sintáctico en ANTLR

ANTLR encapsula el código del análisis sintáctico en una clase denominada


Parser. Declarar un Parser personalizado, o sea, que se atenga a nuestra
gramática es bastante similar a la forma en que declaramos el lexer.

class <Identificador del parser> extends Parser;

Luego podemos poner o no un bloque de opciones y posteriormente las


declaraciones de cada una de las reglas de producción de la gramática.

1. Las reglas de producción en ANTLR

Una regla de producción se especifica con el siguiente formato:


<identificador de la regla>: <cuerpo de la regla>;

Tomemos la primera regla de nuestra gramática, nos quedaría:


expression: term PLUS expresión ;

como podemos apreciar, y ya se ha dicho, los tokens se referencian en


mayúsculas.

Si hacemos lo mismo con la segunda regla, obtenemos:


expression: term ;
pero ANTLR nos diría que hay una definición múltiple de la regla, pero
una de las facilidades que brinda esta herramienta consiste en permitirnos
el uso de los operadores que se emplean para formar la expresiones
regulares en el cuerpo de las reglas y si miramos el par de reglas anterior
como una sola, nos percatamos de que expression es un elemento opcional
dentro del cuerpo, con lo que podemos hacer:
expression: term (PLUS expression)? ;
o incluso:
expression: term (PLUS term)* ;
si vemos la expresión como un término y la suma sucesiva de cero o más
términos.
2. ¿Y la evaluación qué?

En el código que genera la herramienta, cada una de las reglas q ueda


definida como un método de la clase que corresponde al parser, no es de
extrañarse entonces que ANTLR nos permita ponerle a cada regla valor
de retorno, parámetros e insertarle código a distintos niveles.

Para adicionar argumentos haríamos algo como:


rule[int arg] : ... ;
Para ponerle valor de retorno a una regla:
rule returns[int result = 0] : ... ;
los dos juntos:
rule [int arg] returns [int result = 0]: ... ;

La adición de código puede hacerse en cualquier parte del cuerpo de la


regla y entre antes de los dos puntos que dan inicio al cuerpo. El código se
pone entre llaves y por supuesto ANTLR no hace ningún análisis del
mismo.

Ahora nuestra regla para la expresión puede quedar como:

expression returns[int result = 0]


:
result = term
(
{
int e = 0;
}
e = expression
{
result += e ;
}
)?

;
O bien pudiera quedar:
expression returns[int result = 0]
:
result = term
(
{
int t = 0;
}
t = term
{
result += t ;
}
)*

;
Nótese la forma de tomar el valor de retorno de una regla y de cómo las
variables e y t son solamente visibles desde el scope o ámbito de las
subreglas correspondientes.

Faltaría por ver cómo tomar el valor de un token, miremos la última regla
de nuestra gramática, escrita para nuestro evaluador en la sintaxis de
ANTLR:

factor returns[int result = 0]


:
n:N { result = int.Parse(n.getText()); }
| LP result = expresión RP
;

La variable n representa una instancia de IToken la cual expone un


conjunto de métodos, entre ellos getText que nos da el lexema del token.

3. Nuestro evaluador

Finalmente nuestro evaluador completo sería:

class ExpressionParser extends Parser;

expression[int a] returns[int result = 0]


:
result = term
(
{int aux = 0;}
PLUS aux = term
{result += aux;}
)*
;
term returns [int result = 0]
:
result = factor
(
{int aux = 0;}
MULT aux = factor
{ result *= aux;}
)*
;

factor returns [int result = 0;]


:
n:N
{result = int.Parse(n.getText());}
| LP result = expression RP
;
5. ¿Cómo usar el código generado?
Primero creamos un proyecto de aplicación de consola en Visual Studio al que
llamaremos ANTLR_Example al que adicionaremos nuestro expression.g. Con
este archivo abierto, haremos uso de ANTLR.

5.1. ¿Cómo usar ANTLR?

Para hacerlo, adicionaremos la aplicación de ANTLR como herramienta externa a


Visual Studio, vamos al menú Tools>>External Tools y nos aparece un diálogo
como este:

Presionamos adicionar, ponemos el título que queramos, en Command ponemos


la ruta del archivo de la aplicación, en Arguments ponemos $(ItemPath) y así la
aplicación tomo como parámetro el documento que tengamos abierto, en Inicial
Directory ponemos $(ItemDir) y así los archivos que genere la aplicación nos
quedan en la misma carpeta del documento abierto y no podemos olvidar marcar
la opción Use Output window para visualizar la salida estándar de la aplicación en
el panel Output de Visual Studio. Al aceptar, aparece un menú Tool>>External
Tools>>ANTLR
5.2. ¿Cómo interactuar con el código?

Primero agregamos el ensamblado antlr.runtime.dll como referencia a nuestro


proyecto. Después de usar la aplicación en el archivo expression.g aparecen en la
carpeta del proyecto, cuatro archivos nuevos:
- ExpressionLexer.cs
- ExpressionParser.cs
- ExpressionLexerTokenTypes.cs
- ExpressionLexerTokenTypes.txt

Adicionamos a nuestro proyecto los tres primeros y en el Main escribimos el


siguiente código:

if(args.Length>0)
{
using (StreamReader reader = new StreamReader(args[0]))
{
while(!reader.EndOfStream)
{
string line = reader.ReadLine();
StringReader lineReader = new StringReader(line);
ExpressionLexer lexer = new ExpressionLexer(lineReader);
ExpressionParser parser = new ExpressionParser(lexer);
Console.WriteLine(parser.expression());

}
}
Console.WriteLine("File ended");
Console.ReadLine();

Este código toma el archivo cuya ruta se le pasa como argumento y lo comienza a
leer línea por línea. Para cada línea crea un StringReader pues la clase Lexer
necesita un TextReader para y luego creamos nuestro Parser que necesita un
Lexer. Por último, imprimimos por la consola el resultado de llamar al método
expression que nos debe dar el resultado de evaluar la expresión escrita en la línea
correspondiente.

Para el archivo que contiene:

1+2
3*4
2*4+5
La salida es:

6. Conclusiones
ANTLR es hoy una de las herramientas generadoras de lexers y parsers más
populares, sin embargo, no es perfecta. Con ella nos limitamos a las gramáticas
LL(k), aunque tiene varias construcciones que nos hacen ir un poco más allá de
esta clase de lenguaje. No posee una interfaz agradable aunque hemos visto lo
fácil que es usarla desde Visual Studio y el código que genera abusa del
mecanismo de excepciones; pero no por esto deja de ser una muy buena
herramienta.

El presente documento no ha sido más que una brevísima y sencillísima


introducción a los principales aspectos que envuelven a la herramienta. Quedan
por ver muchas opciones verdaderamente útiles así como la construcción y
verificación de los árboles de sintaxis abstractas, pero solo con la práctica es que
se puede alcanzar un buen dominio de ANTLR.

7. Ejercicios propuestos

1. Amplíe la gramática para evaluar expresiones con división y resta, así como
para enteros negativos.
2. Amplíe la gramática para “parsear” un archivo, como el archivo de entrada del
ejemplo, es decir un archivo donde en cada línea tenga una expresión. Su
evaluador debe imprimir por la consola el resultado de cada línea.
3. Haga un analizador sintáctico que elimine los paréntesis superfluos en una
expresión, reescriba la expresión con la menor cantidad de paréntesis posibles
sin que cambie la semántica y por ende su resultado.

También podría gustarte