Sie sind auf Seite 1von 19

Anlisis sintctico con ANTLR

Antlr (ANother Tool for Language Recognition) genera analizadores sintcticos LL(k) con
predicados sintcticos y semnticos.
Estructura de un analizador sintctico
Los analizadores sintcticos o parsers reconocen estructuras sintcticas desde secuencias de
tokens. La estructura general de un analizador sintctico antlr es:
header { ... } //Cdigo que se situar en la cabecera del parser (opcional)
// Suele usarse para decidir la ubicacin del analizador tras ser
// compilado (pe. header {package X; })
class Anasint extends Parser;
options { ... } // opciones destinadas a particularizar el parser (opcional)
tokens {...} //definicin de tokens (opcional)
{ //clases internas necesarias para la implementacin del parser } (opcional)
// definicin de reglas lxicas
Ejemplo:
class Anasint extends Parser;
instrucciones: (expresion ";")* ;
expresion : exp_base (("+"|"-") exp_base)* ;
exp_base : NUMERO
| "(" expresion ")"
;
Uso del analizador sintctico
El procesamiento de un parser como el anterior produce una clase Anasint con mtodos que
representan las reglas gramaticales. El analisis sintctico se apoya sobre un anlisis lxico, es
decir, las categoras sintcticas son generadas desde un flujo de tokens producidos desde un
analizador lxico.
Ejemplo de uso del parser suponiendo un analizador lxico denominado Analex:
import java.io.*;
import antlr.collections.AST;

import antlr.ANTLRException;
public class expre {
public static void main(String args[]){
try{
FileInputStream fis=
new FileInputStream("entrada.txt");
Analex analex = new Analex(fis);
Anasint anasint = new Anasint(analex);
anasint.instrucciones();
}catch(ANTLRException ae){
System.err.println(ae.getMessage());
}
catch (FileNotFoundException fnfe){
System.err.println("No se encontr el fichero");
}
}
}
Construccin de Reconocedores por Separado
Antlr ofrece la posibilidad de desarrollar lexers y parsers por separado.
Por defecto, todo parser exporta su vocabulario (conjunto de tokens usados en la definicin del
parser). Antlr implementa esta idea mediante dos ficheros, uno de texto y otro Java
implementando una interfaz.
Supongamos un fichero Anasint.g conteniendo el siguiente texto:
class Anasint extends Parser;
instrucciones : (expresion ";")* ;
expresion : exp_mult (("+"|"-") exp_mult)* ;
exp_mult : exp_base (("*"|"/") exp_base)* ;
exp_base : NUMERO
| "(" expresion ")"
;
Al compilarlo se producen tres ficheros:

Anasint.java (el parser java),


AnasintTokenTypes.java (la interfaz con la definicin del conjunto de tokens utilizados en
el parser) y

AnasintTokenTypes.txt (el fichero de texto con la definicin del conjunto de tokens


utilizados en el parser).

AnasintTokenTypes.java contiene:
public interface AnasintTokenTypes {
int EOF = 1;
int NULL_TREE_LOOKAHEAD = 3;
// ";" = 4
// "+" = 5
// "-" = 6
// "*" = 7
// "/" = 8
int NUMERO = 9;
// "(" = 10
// ")" = 11
}
AnasintTokenTypes.txt contiene:
";"=4
"+"=5
"-"=6
"*"=7
"/"=8
NUMERO=9
"("=10
")"=11
Estos ficheros son necesarios para conectar el parser con el lexer. La forma de hacerlo es incluir
en el lexer una importacin del vocabulario correspondiente:
class Analex extends Lexer;
options{
importVocab = Anasint; // Importacin del conjunto de tokens
}
BLANCO : (' '|'\t'|"\r\n") {$setType(Token.SKIP);};
NUMERO : ('0'..'9')+('.'('0'..'9')+)?;
OPERADOR : '+'|'-'|'*'|'/';
PARENTESIS : '('|')';
SEPARADOR : ';';
Predicados sintcticos: Antlr permite construir parsers LL(k) (con k arbitrario) haciendo uso de
predicados sintcticos. Se trata de construcciones de la forma ( lookahead ) => regla gramatical

El siguiente ejemplo es un tpico de gramtica no-LL(1):


instruccion : asignacion
| llamada
...
;
asignacion : IDENT ":=" expr ";"
;
llamada : IDENT "(" expr ")" ";"
;
La forma de resolverlo con predicados sintcticos es:
instruccion : (IDENT ":=") => asignacion
| (IDENT "(") => llamada
...
;
Gramticas Atribuidas en Antlr
El reconocimiento descendente permite asociar a cada smbolo no terminales de una gramtica
argumentos de entrada, resultados de salida y variables locales. La idea que subyace detrs de
implementar reglas sintcticas como procedimientos en un lenguaje de programacin.
Las variables locales son simuladas mediante declaraciones entre llaves en la parte izquierda de
la regla:
expr_1 returns [int res=0;] {int e1,e2;} :
e1=expr_2 {res=e1;} (("+" e2=expr_2 {res=res+e2;})
| ( "-" e2=expr_2 {res=res-e2;}))*
;
En este ejemplo, se definen dos variables locales a la regla {int e1,e2;} para almacenar los
atributos sintetizados asociados a las ocurrencias de los smbolos no terminales expr_2
(e1=expr_2 {res=e1;} y e2=expr_2 {res=res+e2;})
Los atributos sintetizados se simulan mediante resultados devueltos por los smbolos no
terminales. La forma de hacerlo es mediante la definicin de clusulas returns en la parte
izquierda de la regla:
expr_1 returns [int res=0;] {int e1,e2;} :
e1=expr_2 {res=e1;} (("+" e2=expr_2 {res=res+e2;})
| ( "-" e2=expr_2 {res=res-e2;}))*
;
En este ejemplo, el reconocimiento del smbolo no terminal expr_1 tiene asociado la
construccin de un valor entero res.

Los atributos heredados se simulan mediante argumentos asociados a los smbolos no


terminales. La forma de hacerlo es mediante la definicin de argumentos entre corchetes en la
parte izquierda de la regla:
expr_1 [java.util.HashMap h] returns [int res=0;] {int e1,e2;} :
e1=expr_2[h] {res=e1;} (("+" e2=expr_2[h] {res=res+e2;})
| ("-" e2=expr_2[h] {res=res-e2;}))*
;
En este ejemplo, el smbolo no terminal expr_1 tiene asociado el argumento h de tipo
java.util.HashMap .
Es importante destacar que el uso de objetos como atributos heredados, tambin permite la
modificacin de sus respectivos estados convirtindose en atributos heredado y sintetizados de
forma simultnea.
El siguiente ejemplo muestra una gramtica con atributos heredados ( h ) y sintetizados ( res ):
class Anasint extends Parser;
options{
k=2;
}
prog { java.util.HashMap h = new java.util.HashMap();} :
( declaracion [h] )* ;
declaracion [java.util.HashMap h] { int e;}:
e=expr_1[h] ";" {System.out.println("Expresion => "+e);}
| i:IDENT ":=" e=expr_1[h] ";" { h.put(i.getText(),new Integer(e));
System.out.println("Asignacion ("+i.getText()+ ")=> "+e);}
;
expr_1 [java.util.HashMap h] returns [int res=0;] {int e1,e2;} :
e1=expr_2[h] {res=e1;} (("+" e2=expr_2[h] {res=res+e2;})
|("-" e2=expr_2[h] {res=res-e2;}))*
;
expr_2 [java.util.HashMap h] returns [int res=0;] {int e1,e2;} :
e1=expr_3[h] {res=e1;} (("*" e2=expr_3[h] {res=res*e2;})
| ("/" e2=expr_3[h] {res=res/e2;}))*
;
expr_3 [java.util.HashMap h] returns [int res=0;] {int e;} :
i:IDENT {Object aux=h.get(i.getText());
if (aux!=null)
res=((Integer)aux).intValue();
}
| n:NUMERO {res=new Integer(n.getText()).intValue();}
| "(" e=expr_1[h] ")" {res=e;}

;
Opciones
Muchos aspectos en el comportamiento de Antlr puede programarse mediante opciones. Las
opciones principales en relacin con el anlisis sintctico son:
k: nmero de tokens lookahead.
Ejemplo:
class Anasint extends Parser;
options { k=2; }
...
importVocab: permite importar un vocabulario de tokens.
Ejemplo:
class Anasint extends Parser;
options{
importVocab = Analex; // Importacin del conjunto de tokens
}
buildAST: indica si el analizador es genera o no representaciones intermedias en forma de
boles de sintaxis abstracta (ASA)
Ejemplo:
class Anasint extends Parser;
options{
buildAST = true; //construccin automtica de AST
}
instrucciones : (expresion ";")*;
expresion : exp_mult (("+"|"-") exp_mult)*;
exp_mult : exp_base (("*"|"/") exp_base)*;
exp_base : NUMERO
| "(" expresion ")"
;

Anlisis Sintctico en Antlr. Ejemplos


En esta seccin, mostramos en 5 ejemplos los recursos de Antlr para realizar anlisis sintctico.
Ejemplo 1: Ejemplo de analizador sintctico.
Analizador lxico-sintctico Antlr (Anasint.g) :
class Anasint extends Parser;
instrucciones:
(expresion ";")* {System.out.println("instrucciones reconocidas");}
;
expresion : exp_base (("+"|"-") exp_base)* ;
exp_base : NUMERO
| "(" expresion ")"
;
class Analex extends Lexer;
BLANCO : (' '|'\t'|"\r\n") {$setType(Token.SKIP);};
NUMERO : ('0'..'9')+('.'('0'..'9')+)?;
OPERADOR : '+'|'-'|'*'|'/';
PARENTESIS : '('|')';
SEPARADOR : ';';
Programa que utiliza el analizador anterior (Prog.java):
import java.io.*;
import antlr.collections.AST;
import antlr.ANTLRException;
public class Prog {
public static void main(String args[]){
try{
FileInputStream fis=
new FileInputStream("entrada.txt");
Analex analex = new Analex(fis);
Anasint anasint = new Anasint(analex);
anasint.instrucciones();
}catch(ANTLRException ae){
System.err.println(ae.getMessage());
}

catch (FileNotFoundException fnfe){


System.err.println("No se encontr el fichero");
}
}
}
Flujo de entrada (entrada.txt):
1+2;
(3-1)+7+5;
33;

Resultado ejecucin programa Prog sobre entrada.txt:

instrucciones reconocidas

Ejemplo 2: Ejemplo de analizadores construidos por separado.


Analizador sintctico Antlr (Anasint.g) :
class Anasint extends Parser;
instrucciones :
(expresion ";")* {System.out.println("instrucciones reconocidas");}
;
expresion : exp_mult (("+"|"-") exp_mult)* ;
exp_mult : exp_base (("*"|"/") exp_base)* ;
exp_base : NUMERO
| "(" expresion ")"
;
Analizador lxico Antlr (Analex.g) :
class Analex extends Lexer;
options{
importVocab = Anasint; // Importacin del conjunto de tokens
}
BLANCO : (' '|'\t'|"\r\n") {$setType(Token.SKIP);};
NUMERO : ('0'..'9')+('.'('0'..'9')+)?;
OPERADOR : '+'|'-'|'*'|'/';
PARENTESIS : '('|')';
SEPARADOR : ';';
Programa que utiliza el analizador anterior (Prog.java):
import java.io.*;
import antlr.collections.AST;
import antlr.ANTLRException;
public class Prog {
public static void main(String args[]){
try{
FileInputStream fis=
new FileInputStream("entrada.txt");
Analex analex = new Analex(fis);
Anasint anasint = new Anasint(analex);
anasint.instrucciones();

}catch(ANTLRException ae){
System.err.println(ae.getMessage());
}
catch (FileNotFoundException fnfe){
System.err.println("No se encontr el fichero");
}
}
}
Flujo de entrada (entrada.txt):
1+2;
(3-1)+7+5;
33;

Resultado ejecucin programa Prog sobre entrada.txt:

instrucciones reconocidas

Ejemplo 3: Ejemplo de uso de predicados sintcticos para resolver indeterminismo.


Analizador sintctico Antlr (Anasint.g) con indeterminsmo en la regla instruccion :
class Anasint extends Parser;
prog :
(instruccion)*
{System.out.println("programa reconocido");}
;
instruccion : asignacion
| llamada
;
asignacion : IDENT ":=" expresion ";"
;
llamada : IDENT "(" expresion ")" ";"
;
expresion : exp_mult (("+"|"-") exp_mult)* ;
exp_mult : exp_base (("*"|"/") exp_base)* ;
exp_base : NUMERO
| IDENT
| "(" expresion ")"
;
Analizador lxico Antlr (Analex.g) :
class Analex extends Lexer;
options{
importVocab = Anasint; // Importacin del conjunto de tokens
}
BLANCO : (' '|'\t'|"\r\n") {$setType(Token.SKIP);};
protected DIGITO : ('0'..'9');
protected LETRA : ('a'..'z')|('A'..'Z');
OPERADOR : '+'|'-'|'*'|'/';
PARENTESIS : '('|')';
SEPARADOR : ';';
ASIG : ":=";

NUMERO : (DIGITO)+('.'(DIGITO)+)?;
IDENT : LETRA (LETRA | DIGITO)* ;

Programa que utiliza el analizador anterior (Prog.java):


import java.io.*;
import antlr.collections.AST;
import antlr.ANTLRException;
public class Prog {
public static void main(String args[]){
try{
FileInputStream fis=
new FileInputStream("entrada.txt");
Analex analex = new Analex(fis);
Anasint anasint = new Anasint(analex);
anasint.prog();
}catch(ANTLRException ae){
System.err.println(ae.getMessage());
}
catch (FileNotFoundException fnfe){
System.err.println("No se encontr el fichero");
}
}
}
Flujo de entrada (entrada.txt):
a:=3+5;
f(a);

Resultado ejecucin programa Prog sobre entrada.txt:


line 1:13: expecting ":=", found '('
line 1:15: expecting ":=", found ')'
programa reconocido

Analizador sintctico Antlr (Anasint.g) sin indeterminismo:


class Anasint extends Parser;
prog :
(instruccion)*
{System.out.println("programa reconocido");}
;
instruccion : (IDENT ":=") => asignacion
| (IDENT "(") => llamada
;
asignacion : IDENT ":=" expresion ";"
;
llamada : IDENT "(" expresion ")" ";"
;
expresion : exp_mult (("+"|"-") exp_mult)* ;
exp_mult : exp_base (("*"|"/") exp_base)* ;
exp_base : NUMERO
| IDENT
| "(" expresion ")"
;
Flujo de entrada (entrada.txt):
a:=3+5;
f(a);

Resultado ejecucin programa Prog sobre entrada.txt:


programa reconocido

Ejemplo 4: Ejemplo de atribucin


Analizador sintctico Antlr (Anasint.g):
class Anasint extends Parser;
options{
k=2;
}
prog {java.util.HashMap h = new java.util.HashMap();} :
( declaracion [h] )* ;
declaracion [java.util.HashMap h] {int e;}:
e=expr_1[h] ";" {System.out.println("Expresion => "+e);}
| i:IDENT ":=" e=expr_1[h] ";" { h.put(i.getText(),new Integer(e));
System.out.println("Asignacion ("+i.getText()+ ")=> "+e);}
;
expr_1 [java.util.HashMap h] returns [int res=0;] {int e1,e2;} :
e1=expr_2[h] {res=e1;} (("+" e2=expr_2[h] {res=res+e2;})
|("-" e2=expr_2[h] {res=res-e2;}))*
;
expr_2 [java.util.HashMap h] returns [int res=0;] {int e1,e2;} :
e1=expr_3[h] {res=e1;} (("*" e2=expr_3[h] {res=res*e2;})
| ("/" e2=expr_3[h] {res=res/e2;}))*
;
expr_3 [java.util.HashMap h] returns [int res=0;] {int e;} :
i:IDENT {Object aux=h.get(i.getText());
if (aux!=null)
res=((Integer)aux).intValue();
}
| n:NUMERO {res=new Integer(n.getText()).intValue();}
| "(" e=expr_1[h] ")" {res=e;}
;
Analizador lxico Antlr (Analex.g) :
class Analex extends Lexer;
options{
importVocab = Anasint; // Importacin del conjunto de tokens
}
BLANCO : (' '|'\t'|"\r\n") {$setType(Token.SKIP);};
protected DIGITO : ('0'..'9');
protected LETRA : ('a'..'z')|('A'..'Z');
OPERADOR : '+'|'-'|'*'|'/';

PARENTESIS : '('|')';
SEPARADOR : ';';
ASIG : ":=";
NUMERO : (DIGITO)+('.'(DIGITO)+)?;
IDENT : LETRA (LETRA | DIGITO)* ;

Programa que utiliza el analizador anterior (Prog.java):


import java.io.*;
import antlr.collections.AST;
import antlr.ANTLRException;
public class Prog {
public static void main(String args[]){
try{
FileInputStream fis=
new FileInputStream("entrada.txt");
Analex analex = new Analex(fis);
Anasint anasint = new Anasint(analex);
anasint.prog();
}catch(ANTLRException ae){
System.err.println(ae.getMessage());
}
catch (FileNotFoundException fnfe){
System.err.println("No se encontr el fichero");
}
}
}
Flujo de entrada (entrada.txt):
3+5;
a := 3+5;
b := a +2;
b:=b+1;
1;

Resultado ejecucin programa Prog sobre entrada.txt:


Expresion => 8
Asignacion (a)=> 8
Asignacion (b)=> 10
Asignacion (b)=> 11
Expresion => 1

Ejemplo 4: Ejemplo de Generacin Automtica de rboles de Sintaxis Abstracta


Analizador sintctico Antlr (Anasint.g):
class Anasint extends Parser;
options{
k=2;
buildAST = true;
}
prog {java.util.HashMap h = new java.util.HashMap();} :
( declaracion [h] )* ;
declaracion [java.util.HashMap h] {int e;}:
e=expr_1[h] ";" {System.out.println("Expresion => "+e);}
| i:IDENT ":=" e=expr_1[h] ";" { h.put(i.getText(),new Integer(e));
System.out.println("Asignacion ("+i.getText()+ ")=> "+e);}
;
expr_1 [java.util.HashMap h] returns [int res=0;] {int e1,e2;} :
e1=expr_2[h] {res=e1;} (("+" e2=expr_2[h] {res=res+e2;})
|("-" e2=expr_2[h] {res=res-e2;}))*
;
expr_2 [java.util.HashMap h] returns [int res=0;] {int e1,e2;} :
e1=expr_3[h] {res=e1;} (("*" e2=expr_3[h] {res=res*e2;})
| ("/" e2=expr_3[h] {res=res/e2;}))*
;
expr_3 [java.util.HashMap h] returns [int res=0;] {int e;} :
i:IDENT {Object aux=h.get(i.getText());
if (aux!=null)
res=((Integer)aux).intValue();
}
| n:NUMERO {res=new Integer(n.getText()).intValue();}
| "(" e=expr_1[h] ")" {res=e;}
;
Analizador lxico Antlr (Analex.g) :
class Analex extends Lexer;
options{
importVocab = Anasint; // Importacin del conjunto de tokens
}
BLANCO : (' '|'\t'|"\r\n") {$setType(Token.SKIP);};
protected DIGITO : ('0'..'9');
protected LETRA : ('a'..'z')|('A'..'Z');

OPERADOR : '+'|'-'|'*'|'/';
PARENTESIS : '('|')';
SEPARADOR : ';';
ASIG : ":=";
NUMERO : (DIGITO)+('.'(DIGITO)+)?;
IDENT : LETRA (LETRA | DIGITO)* ;

Programa que utiliza el analizador anterior (Prog.java):


import java.io.*;
import antlr.collections.AST;
import antlr.*;
public class Prog {
public static void main(String args[]){
try{
FileInputStream fis=
new FileInputStream("entrada.txt");
Analex analex = new Analex(fis);
Anasint anasint = new Anasint(analex);
anasint.prog();
CommonAST a = (CommonAST)anasint.getAST();
System.out.println("Resultado ASA: "+a.toStringList());
}catch(ANTLRException ae){
System.err.println(ae.getMessage());
}
catch (FileNotFoundException fnfe){
System.err.println("No se encontr el fichero");
}
}
}
Flujo de entrada (entrada.txt):
3+5;
a := 3+5;
b := a +2;
b:=b+1;
1;

Resultado ejecucin programa Prog sobre entrada.txt:


Expresion => 8
Asignacion (a)=> 8
Asignacion (b)=> 10
Asignacion (b)=> 11
Expresion => 1
Resultado ASA: 3 + 5 ; a := 3 + 5 ; b := a + 2 ; b := b + 1 ; 1 ;

Das könnte Ihnen auch gefallen