miércoles, 2 de febrero de 2011

ANTLR

¿Qué es y cómo funciona ANTLR?
ANTLR es un programa está escrito en java, por lo que se necesita alguna máquina virtual de java para poder ejecutarlo. Es software libre, lo que quiere decir que al descargarlo de la página oficial (http://www.antlr.org) obtendremos tanto los ficheros compilados *.class como el código fuente en forma de ficheros *.java.
ANTLR es un generador de analizadores. Mucha gente llama a estas herramientas compiladores de compiladores 15 , dado que ayudar a implementar compiladores es su uso más popular. Sin embargo tienen otros usos. ANTLR, por ejemplo, podría servir para implementar el intérprete de un fichero de configuración.
ANTLR es capaz de generar un analizador léxico, sintáctico o semántico en varios lenguajes (java, C++ y C# en su versión 2.7.2) a partir de unos ficheros escritos en un lenguaje propio.
Dicho lenguaje es básicamente una serie de reglas EBNF y un conjunto de construcciones auxiliares.


ANTLR genera analizadores pred-LL(k), y él mismo utiliza un analizador pred-LL(k) para leer los ficheros en los que están escritas las reglas EBNF. ANTLR admite acciones en sus reglas, además de otras prestaciones como paso de parámetros, devolución de valores o herencia de gramáticas.
Los analizadores en ANTLR
Ya mencionamos que ANTLR genera sus analizadores a partir de unos ficheros de entrada en los que se especifica el funcionamiento de dichos analizadores mediante el uso de reglas EBNF.
Para poder comprender los analizadores necesitaremos conocer la sintaxis de dichos ficheros de entrada.
Especificación de gramáticas con ANTLR
Los ficheros con los que trabaja ANTLR tienen la terminación *.g, y en adelante los llamaremos ficheros de especificación de gramáticas o, directamente, ficheros de gramáticas.
Un fichero de gramática contiene la definición de uno o varios analizadores. Cada uno de estos analizadores se traducirá a código nativo (java, C++ o C#, dependiendo de ciertas opciones) en forma de clases. Es decir, por cada analizador descrito en el fichero de gramáticas se generará una clase.
Todo fichero de gramática tiene la siguiente estructura:
header{
/* opciones de cabecera */
}
options
{
/* opciones generales a todo el fichero */
}
// A continuación la definición de el(los) analizadore(s).
Cabecera: Esta zona es opcional (puede aparecer o no). Delimitada por las partículas “header {” y “}”, en esta zona incluimos elementos en código nativo (java, C++ o C#) que deben preceder a la definición de las diferentes clases de los analizadores. Esta sección se utiliza para incluir otros ficheros (import e #include), definir el paquete al que pertenecerá la clase del analizador (package) etc.
• Opciones generales del fichero: Esta zona es opcional. Permite controlar algunos parámetros de ANTLR mediante “opciones”. Las opciones se representan como asignaciones:
nombreOpcion=valor;. Se utilizan mucho en ANTLR. La opción más importante de esta zona es la que permite elegir el lenguaje nativo en el que se generarán los analizadores (java,C++,C#). Su valor por defecto es “java”. Dado que vamos a generar reconocedores en java, no necesitaremos esta zona. En el manual de ANTLR aparecen todas las opciones que se pueden incluir en esta zona.
Tras las opciones generales del fichero vienen las definiciones de analizadores. Es muy común que en un mismo fichero se especifiquen varios analizadores (en la mayoría de los ejemplos que acompañan a ANTLR se utiliza esta técnica). Sin embargo también es posible definir cada analizador en un fichero, sobre todo cuando se trata de analizadores extensos.
Dado que nuestro lenguaje en este capítulo será muy sencillo, definiremos los tres analizadores en el mismo fichero.
En ANTLR, cada analizador tiene la siguiente estructura:
class nombreAnalizador extends tipoAnalizador; // definición del analizador
options {
/* Zona de opciones del analizador*/
}
tokens {
/* Zona de definición de tokens */
}
{
/* Zona de código nativo */
}
/* Zona de reglas */
Definición del analizador: En la primera línea definimos el nombre del analizador (nombreAnalizador) y su tipo (tipoAnalizador). El tipo puede será Lexer para analizadores léxicos, Parser para analizadores sintácticos y TreeParser para analizadores semánticos.
Zona de opciones: Esta zona es opcional, aunque casi siempre interesa utilizarla. En esta zona se definen propiedades muy importantes del analizador: se define el lookahead (k), si se va a generar un AST o no, en el caso de Parsers y TreeParsers, la importación/exportación de vocabulario, la activación/desactivación del tratamiento automático de errores etc
La zona de código nativo
No hay que olvidar que para ANTLR cualquier analizador es una instancia de una clase. En ocasiones es muy útil declarar métodos y atributos para dicha clase.
Para añadir métodos y variables a una clase de un analizador basta con escribirlos, entre la zona de opciones y la zona de reglas, entre llaves.
Flujo de caracteres
Para ANTLR el flujo caracteres es, simplemente “cualquier subclase de java.io.InputStream”.
Lo más normal es utilizar un flujo de caracteres provinentes de un fichero (con un FileInputStream) pero pueden utilizarse otras fuentes, como una cadena (StringBufferStream) o una página web (URL.openStream).
El InputStream que proporciona los caracteres al analizador léxico se le pasa como parámetro en su constructor.



Uno de los inconvenientes de utilizar las implementaciones de ASTs proporcionadas por ANTLR es que la visión que se tiene del árbol es muy reducida; cada nodo solamente tiene constancia de su primer hijo y de su “hermano”. Es decir, se trabaja con un árbol como el de la izquierda en la siguiente figura:

No hay comentarios:

Publicar un comentario