Tutorial ANTLR y Python II. Creando una gramatica sencilla

Viene de https://objektblog.wordpress.com/2009/03/29/tutorial-antlr-y-python-i-introduccion-e-instalacion/

Tutorial ANTLR y Python:

Parte I: Introducción e Instalación.

Parte II: Creacion de una gramática sencilla.

Parte III: Creacion e implementación de un AST.

Antes de empezar, el codigo que se usara en los ejemplos esta publicado en el sig. URL:

http://rapidshare.com/files/215758595/ejemplos.tar.bz2

Ahh las gramaticas, tan odiadas por muchos. Y no es para asombrarse, ya que tantos problemas que dan, más aún si la herramienta no es nada amigable (como la tan odiada combinacion JLex/CUP), ANTLR es lo suficientemente amigable como para que desarrollemos gramáticas de la manera menos dolorosa posible, teniendo como valor agregado el ANTLRWorks, un IDE que de verdad permite aumentar nuestra eficiencia a la hora de hacer gramáticas para ANTLR. Esta parte se enfocará en generar una gramática simple con la cual se aprenderá de manera básica la estructura y sintaxis de ANTLR.

Pero… Y donde esta el analizador Léxico???

Como dije antes ANTLR simplifica las cosas, con ello, unifica tanto el analizador léxico como el sintáctico, por lo que para ambas tareas usaremos la misma herramienta, evitando los dolores de cabeza al intentar integrar ambos mundos.

Empezando…

Abrimos el ANTLRWorks que instalamos previamente en nuestro DIRECTORIO DEL IDE. y desde consola ejecutamos


$ cd [DIRECTORIO DEL IDE]
$ java -jar antlrworks-1.2.3.jar &

Abriremos de esta manera el entorno de trabajo de ANTLR, desde donde podemos hacer clic en el menu File -> New… para crear una nueva gramatica

ANTLRWorks.

ANTLRWorks.

Luego de esto, ya podemos empezar a escribir… Empezaremos definiendo lo primero, el nombre que queremos para nuestra gramatica, para ello escribimos els siguiente texto en el editor:
grammar ejemplo;

Luego de esto se puede definir diversas opciones para el ANTLR en la directiva Options que ofrece, en este caso especificamos que se generara codigo para python.
options{
language=Python;
}

Y luego viene la gramática…

Especificacion de la gramatica:

Ya especificado el lenguaje, podemos empezar a hacer ya nuestra gramatica. Nuestro objetivo es crear un lenguaje que pueda leer expresiones aritméticas simples de suma, resta y multiplicación. Debo aclarar que el método de análisis de ANTLR es LL(k) descendente, por lo que debemos previamente hacer las transformaciones necesarias a nuestra gramática (quitar recursividad por la izquierda, factorizar, etc).A continuación la gramática que se utilizará …

grammar ejemplo;
options{
language=Python;
}
program : sentencias
;
sentencias
: (expresion NEWLINE )+
| NEWLINE
;
expresion
: suma
;
suma
: mult (OPSUM mult)*
;
mult
: nume (OPMUL nume)*
;
nume : ENTERO
| '(' expresion ')'
;
ENTERO : ('0'..'9')+ ; // Lee enteros postivos
NEWLINE: '\r'? '\n' ; // lee cambios de linea
OPSUM : '+' | '-'; // operadores aditivos
OPMUL : '/' | '*'; // operadores multiplicativos

Debemos tomar en cuenta que LOS SIMBOLOS TERMINALES VAN EN MAYUSCULAS, asi tambien la gramatica tiene que estar factorizada y sin recursividad por la izquierda. la recursividad se puede quitar desde el mismo ANTLRWorks que tiene la opcion “Remove All Left Recursion” en el menú “Refactor”(forma sencilla), o también, escribiéndolos como Gramáticas Regulares es decir es decir transformar la forma recursiva por la derecha (forma avanzada)…

e : ep
;

ep : OP ep
| null
;

equivalente a a la expresion regular en la gramatica.

e : ep (OP ep)*
;

…como se hizo en el ejemplo, pues ANTLR permite la escritura de expresiones regulares desde la misma gramatica, además de hacerla más legible, se vuelve más intuitiva la forma de gramatica regular.

Pero bueno, en si lo único que hace esta gramática es revisar si la entrada va bien escrita… todavia faltan las acciones.

Agregando acciones semánticas en la gramatica:

Gracias a que ANTLR usa método descendente, se puede utilizar Atributos Heredados. Trabajaremos en base a atributos heredados, pues es como normalmente se trabaja en ANTLR. Tambien hay que tomar en cuenta que debido a que se estará generando código para Python, obviamente las acciones deberán ir en ese lenguaje.

Agregando acciones, la gramática nos quedaría de la siguiente manera:


grammar ejemplo;
options{
language=Python;
}
program : sentencias
;//fin program
sentencias
: (expresion NEWLINE {print $expresion.valor} )+
| NEWLINE
;//fin sentencias
expresion returns [valor]
: mult { $expresion.valor = $mult.valor }// suma
;//fin expresion
suma returns [valor]
: e1=mult {$valor = $e1.valor }
(OPSUM e2=mult
{ if ($OPSUM.getText() == '+') :
$valor = $valor + $e2.valor
if ($OPSUM.getText() == '-') :
$valor = $valor - $e2.valor
} //cerramos atributo

)*
;//fin suma
mult returns [valor] // nume ( OPMUL nume)*
: e1=nume {$valor = $e1.valor }
( OPMUL e2=nume
{ if ($OPMUL.getText() == '*') :
$valor = $valor * $e2.valor
if ($OPMUL.getText() == '/') :
if ($e2.valor 0) :
$valor = $valor / $e2.valor
else:
print "Division entre 0\n"
} //cerramos atributo
)* //cerramos expresion
;//fin mult
nume returns [valor] : ENTERO { $valor = int($ENTERO.getText()) }
| '(' expresion ')' { $valor = $expresion.valor }
;//fin nume
ENTERO : ('0'..'9')+ ; // Lee enteros postivos
NEWLINE: '\r'? '\n' ; // lee cambios de linea
OPSUM : '+' | '-'; // operadores aditivos
OPMUL : '/' | '*'; // operadores multiplicativos

Revisando la gramática tenemos cosas nuevas:

* suma returns [valor] nos indica que el dato regresado por la regla expresión devolverá un atributo llamado valor. ésto es también para las demás reglas que tengan la sentencia returns.

* e1=mult {$valor = $e1.valor } , Al mismo tiempo, también podemos colocar “alias” a los nombres de los terminales y no terminales que estemos utilizando en la producción, en este caso el e1=mult indica que el símbolo mult, estará identificado dentro de las acciones como e1 para la accion que tenga más próxima, en este caso es {$valor = $e1.valor } , En esta acción también podemos ver que $valor nos representa el atributo del símbolo no terminal que identifica la produccción al cual queremos “pasar” la información. en este caso estaremos pasando el valor de e1, al atributo valor del símbolo suma .

(OPSUM e2=mult
{ if ($OPSUM.getText() == '+') :
$valor = $valor + $e2.valor
if ($OPSUM.getText() == '-') :
$valor = $valor - $e2.valor
} //cerramos atributo

)*

Lo mismo que el anterior, le asignamos e2 al simbolo mult , Y como pueden darse cuenta ANTLR permite más de un segmento de codigo dentro de una regla gramatical, gracias a esto podemos evaluar cada repeticion que se encuentra en la sentencia mult (OPSUM mult)* , quedando como mult {} (OPSUM mult {})* en donde codigo2 se ejecutara cada vez que se lea OPSUM mult, pues esta dentro del operador de repeticion *.

La producción mult es similar a la suma, con excepcion de que agrega una validación adicional dentro del código para divisiones entre cero. Debemos tener las siguientes consideraciones cuando creamos codigo en python.

Podemos ver tambien que accedemos al valor del terminal OPSUM, con su atributo getText(), el cual devuelve el lexema leído actualmente por la regla OPSUM. con lo cual nos evitamos agregarles acciones semánticas a todos los terminales (ENTERO, NEWLINE, OPSUM, OPMUL).

Notas:

  • En las acciones, el codigo python, las sangrias deberan estar hechas por ESPACIOS, no por TABS.
  • Cuando se vaya a usar el operador % de python en las acciones, se debera anteceder con una backslash “\” de esta manera a % b se convertiria en a \% b

Ya lista la gramática, generamos código de la siguiente manera:

Seleccionamos la opcion “generate code” del menu “Generate”, y si todo sale bien no dara ningun error, de otra manera ANTLRWorks avisara cualquier falta que se este cometiendo. El código será generado en un nuevo directorio llamado “output” dentro de nuestro DIRECTORIO DEL IDE.

Pero bueno… y como conecto lo generado con un script python?

Creamos el siguiente script que servirá como punto de entrada, para ello, estando en nuestro DIRECTORIO DEL IDE, nos metemos al directorio output generado por ANTLR al generar código, y abrimos el editor nano con

$ cd output && nano ejemplo.py

y escribimos lo siguiente

import sys
import antlr3 # importamos librerias de antlr3
from ejemploLexer import ejemploLexer # importamos nuestro lexer generado por ANTLRWorks
from ejemploParser import ejemploParser # Importamos nuestro parser generado por ANTLRWorks

inputf = file(“input.txt”) # leemos archivo de entrada
char_stream = antlr3.ANTLRInputStream(inputf) #asignamos archivo de entrada a ANTLR

lexer = ejemploLexer(char_stream) # Asignamos strean devuelto por ANTLR al lexer
tokens = antlr3.CommonTokenStream(lexer) # Creamos stream de dokens con el lexer
parser = ejemploParser(tokens) # Creamos nuevo parser y le pasamos el lexer de parametro
parser.program() # Ejecutamos la produccion program del parser.
inputf.close() # Cerramos archivo ya que todo se haya leido

Nos salimos con CTRL+X y guardamos el script cuando nos lo pregunten.

Luego creamos el archivo de entrada

$ nano input.txt

y escribimos en él

1*1
2*2
3*3
2*2/3
2*2/2

Nos salimos con CTRL+X y guardamos el archivo de entrada cuando nos lo pregunten.

Corriendo el código:

Lo ejecutamos con el siguiente comando

python ejemplo.py

y en la pantalla veremos los resultados.

Los ejemplos pueden ser bajados directamente del siguiente URL:

http://rapidshare.com/files/215758595/ejemplos.tar.bz2

Siguiente –>

8 comentarios

  1. […] ) Tutorial ANTLR y Pyt… en Tutorial ANTLR y Python I. In…joselin en Iron Maiden en Guatemala!!!!…Mis […]

  2. gracias por la ayuda, pero fijate que al ejecutar el ejemplo que tenes me muesta el siguiente error.

    Traceback (most recent call last):
    File “ejemplo.py”, line 2, in
    import antlr3 #
    ImportError: No module named antlr3

  3. @Wlix Revisa si existe y si tiene archivos los directorios

    /usr/lib/python2.5/site-packages/antlr_python_runtime-3.1.2-py2.5.egg/antlr3

    sino estan deberias de volver a ejecutar el instalador de los runtimes de python.

    Si no te jala tambien podes probar instalar antlr3 desde el apt

    $ sudo apt-get install antlr3

  4. […] objekt::comments( ) Erik Giron en Mi TOP 10 del 2007 para el Hea…sebastian en Mi TOP 10 del 2007 para el Hea…Erik Giron en Un nuevo enfoque para ejecutar…Pepe Barrascout Orti… en Un nuevo enfoque para ejecutar…erikgiron en Tutorial ANTLR y Python II. C… […]

  5. […] ANTLR_EJ Comments RSS feed LikeBe the first to like this post. […]

  6. Saludos muy chevere aunque como soy tan novato y estoy dando una materia de esto en la universidad si me puedes dar info de como prender esto te agradeceria voy tirado en la materia, por cierto me sale este error cuando ejecuto python ejemplo.py

    Traceback (most recent call last):
    File “ejemplo.py”, line 4, in
    from ejemploParser import ejemploParser
    File “/home/geisaugusto/antlr/output/ejemploParser.py”, line 247
    valor = valor + e2

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: