Sei sulla pagina 1di 12

Departamento de Lenguajes y Sistemas Informticos

Procesadores de Lenguajes
1

TEMA 2: ANLISIS LXICO-SINTCTICO


OBJETIVO

Dar respuesta a las siguientes preguntas:
Qu es el anlisis lxico-sintctico de un lenguaje?
Cmo se disea y construye un analizador lxico-sintctico?

LENGUAJE DE EJEMPLO

Para presentar este tema utilizaremos un lenguaje de programacin hipottico llamado LF.
Definicin informal : Se trata de un lenguaje de programacin secuencial con variables enteras y
dos tipos de instrucciones: (a) definicin de variables con expresin entera (DEF) y (b)
evaluacin de variables (EVAL). No se acepta el uso de variables sin declarar. La declaracin de
variables asocia valor 0 por defecto. Cuando se define una variable se le asocia una expresin
entera sin evaluar. Esta expresin se evala justo al ejecutar la instruccin EVAL sobre dicha
variable. Toda variable definida sobre s misma se le asocia un valor indefinido.
Se muestra un programa tpico para precisar la sintaxis de LF.
VARIABLES x, y, z, a, b;
INSTRUCCIONES
a DEF -1;
b DEF (a+1);
EVAL b;
a DEF 2;
EVAL b;
b DEF b + 1;
z DEF 2*y;
EVAL z;
EVAL b;
Departamento de Lenguajes y Sistemas Informticos
Procesadores de Lenguajes
2

Para precisar la semntica de LF, se aporta la ejecucin del programa de ejemplo:
b = 0 (primera instruccin EVAL)
b = 3 (segunda instruccin EVAL)
z = 0 (tercera instruccin EVAL)
b = indefinido (cuarta instruccin EVAL)

QU ES EL ANLISIS LXICO-SINTCTICO DE UN LENGUAJE?

El anlisis lxico-sintctico tiene por objeto reconocer la forma de las sentencias de un lenguaje.
Reconocer la forma de una sentencia implica reconocer sus lexemas y estructuras sintcticas. El
resultado del anlisis lxico-sintctico puede ser un error de reconocimiento o una versin de la
sentencia reconocida en forma de rbol de sintaxis abstracta (asa).




Para reconocer los lexemas de un lenguaje usaremos expresiones regulares y para reconocer
estructuras sintcticas usaremos gramticas independientes de contexto (gramtica en
adelante).
Una gramtica es un conjunto de reglas. Cada regla es de la forma genrica:
cabeza : cuerpo1 | cuerpo2 | ... | cuerpoN siendo N>=1

La cabeza de la regla es un smbolo llamado no terminal que representa una estructura
sintctica. El cuerpo de la regla est compuesto por smbolos terminales (lexemas) y no
terminales. La composicin de estos smbolos se consigue haciendo uso de alternativas,
iteraciones y yuxtaposiciones.
A continuacin se muestra la gramtica del lenguaje LF.

Ejemplo 1. Gramtica de LF.
programa : variables instrucciones
;
variables : VARIABLES idents PyC
;
idents : VAR COMA idents
| VAR
|
;
Filtro
1.1:
Analizador
lxico
Filtro
1.2:
Analizador
sintctico
Sentencia
(texto) lexemas
s
Sentencia
(asa)
Departamento de Lenguajes y Sistemas Informticos
Procesadores de Lenguajes
3

instrucciones : INSTRUCCIONES (definicion|evaluacion)*
;
definicion : VAR DEF expr PyC
;
evaluacion : EVAL idents PyC
;
expr : expr1 MAS expr
| expr1 MENOS expr
| expr1
;
expr1 : expr2 POR expr1
| expr2
;
expr2 : NUMERO
| MENOS expr
| VAR
| PA expr PC
;

Dada una gramtica G, el anlisis sintctico se puede realizar aplicando reglas de G de
izquierda a derecha (anlisis descendente). La aplicacin de una regla se denomina derivacin.
En el anlisis sintctico descendente, el objetivo es encontrar una derivacin que reconozca la
sentencia. Los smbolos no terminales son smbolos generativos porque producen derivaciones.
Los lexemas son smbolos no generativos porque no producen derivaciones.
A continuacin se muestran derivaciones producidas desde la gramtica mostrada en el Ejemplo
1 para el programa de ejemplo.

Ejemplo 2. Derivacin.
programa -> variables instrucciones -> VARIABLES idents PyC
instrucciones -> VARIABLES VAR COMA idents PyC instrucciones ->


Si no es posible construir una derivacin que genere la sentencia completa como secuencia de
lexemas entonces dicha sentencia no pertenece al lenguaje especificado en la gramtica.
Una gramtica es determinista si toda sentencia perteneciente al lenguaje especificado por
dicha gramtica tiene una nica derivacin. La gramtica propuesta para el lenguaje LF (Ejemplo
1) no es determinista. El no determinismo se debe a la existencia de prefijos comunes para
distintas alternativas en una misma regla. Por ejemplo, la regla idents : VAR COMA idents
| VAR | ; tiene el prefijo comn VAR en sus 2 primeras alternativas. Los prefijos comunes
pueden producirse sobre smbolos no terminales. Por ejemplo sobre los smbolos expr1 y
expr2 en las reglas correspondientes a la definicin de expresin.

Una forma de resolver el problema del prefijo comn es usar predicados sintcticos. Los
predicados sintcticos son condiciones aadidas a las alternativas para reconocer de manera
tentativa un determinado prefijo de lexemas. Si es as, se selecciona la alternativa
correspondiente. Si no, se selecciona la siguiente alternativa en la regla (y as sucesivamente).
La siguiente gramtica resuelve el problema de la gramtica original (Ejemplo 1) usando prefijos
comunes.
Departamento de Lenguajes y Sistemas Informticos
Procesadores de Lenguajes
4


Ejemplo 3. Gramtica determinista.

programa : variables instrucciones
;
variables !: VARIABLES idents PyC
;
idents : (VAR COMA) => VAR COMA idents //regla con predicado sint.
| VAR
|
;
instrucciones : INSTRUCCIONES (definicion|evaluacion)*
;
definicion : VAR DEF expr PyC
;
evaluacion : EVAL idents PyC
;
expr : (expr1 MAS) => expr1 MAS expr // regla con predicado sint.
| (expr1 MENOS) => expr1 MENOS expr
| expr1
;
expr1 : (expr2 POR) => expr2 POR expr1 // regla con predicado sint.
| expr2
;
expr2 : NUMERO
| MENOS expr
| VAR
| PA expr PC
;

Los lexemas o tokens son los smbolos terminales que aparecen en la gramtica y representan
las distintas categoras de palabras y smbolos de puntuacin que podemos encontrar en una
sentencia del lenguaje. Los lexemas de un lenguaje se definen con reglas de la forma:
LEXEMA : expresion regular
donde expresion regular puede ser:

(1) carcter (ej. '('),
(2) concatenacin de caracteres (ej. "INSTRUCCIONES") ,
(3) rango de caracteres (ej. '0'..'9'),
(4) conjunto negativo de caracteres (ej. ~('0'|'1')),
(5) opcin (ej. (('a'..'z')?),
(6) alternativa (ej. 'a'..'z'|'A'..'Z') y
(7) cierre (ej. (LETRA)+).





Las reglas que definen los lexemas del lenguaje LF se muestran a continuacin.
Departamento de Lenguajes y Sistemas Informticos
Procesadores de Lenguajes
5

Ejemplo 4. Lexemas de LF.
// Lexemas auxiliares (se usan en la definicin de otros lexemas)

protected NL: "\r\n"; //concatenacin
protected DIGITO : '0'..'9'; //rango
protected LETRA : 'a'..'z'|'A'..'Z'; //alternativa de rangos

// Lexemas no auxiliares
;
BTF: (' '|'\t'|NL); //alternativa
NUMERO : (DIGITO)+; // cierre
VAR : (LETRA)+; //cierre
VARIABLES: "VARIABLES"; //concatenacin
INSTRUCCIONES: "INSTRUCCIONES"; //concatenacin
EVAL: "EVAL"; //concatenacin
DEF: "DEF"; //concatenacin
PA : '(' ;//carcter
PC : ')'; //carcter
PyC : ';'; //carcter
COMA : ','; //carcter
MAS : '+' ;//carcter
MENOS : '-'; //carcter
POR : '*' ;//carcter


Un rbol de sintaxis abstracta (asa) es un rbol que refleja la estructura lxico-sintctica de
una sentencia. Los nodos del asa son lexemas. Se distinguen 2 tipos de lexemas en el asa:
lexemas producidos por el analizador lxico y lexemas producidos por el analizador sintctico.
Estos ltimos sirven para identificar estructuras sintcticas.
Para construir un asa tenemos que anotar la gramtica. El siguiente ejemplo muestra la
gramtica del Ejemplo 1 con anotaciones para construir rboles de sintaxis abstracta.
Ejemplo 5. Gramtica que construye rboles de sintaxis abstracta.

programa : variables instrucciones EOF!
{#programa = #(#[PROGRAMA, "PROGRAMA"], ##);}
;
variables !: a:VARIABLES b:idents PyC {#variables = #(#a,#b);}
;
idents : (VAR COMA) => VAR COMA! idents
| VAR
|
;
instrucciones : INSTRUCCIONES^ (definicion|evaluacion)*
;
definicion : VAR DEF^ expr PyC!
;
evaluacion : EVAL^ idents PyC!
;
expr : (expr1 MAS) => expr1 MAS^ expr
| (expr1 MENOS) => expr1 MENOS^ expr
| expr1
;
expr1 : (expr2 POR) => expr2 POR^ expr1
Departamento de Lenguajes y Sistemas Informticos
Procesadores de Lenguajes
6

| expr2
;
expr2 : NUMERO
| MENOS^ expr
| VAR
| PA! expr PC!
;

El asa es una expresin #(nodo, subrbol1 , , subarbolk) donde nodo es un asa elemental
formado por un nico lexema y subrbol1, , subarbolk son los asas hijos de nodo.
El asa elemental o nodo se puede expresar de diferentes maneras:
1. #[LEXEMA, "txt"] donde LEXEMA es el identificador del lexema y "txt" es el
texto asociado al lexema. Por ejemplo, #[PROGRAMA, "PROGRAMA"]
2. #[LEXEMA] donde LEXEMA es el identificador del lexema.
3. LEXEMA donde LEXEMA es el identificador del lexema.
Cada smbolo no terminal de la gramtica puede sintetizar un asa o una secuencia de asas. Por
ejemplo, el smbolo idents sintetiza una secuencia de asas y el smbolo programa sintetiza un
asa. Cada lexema de la gramtica, por el contrario, slo puede sintetizar un asa con un nico
nodo.

Podemos referenciar los asas sintetizados con etiquetas. Por ejemplo, la regla variables !:
a:VARIABLES b:idents PyC; define dos etiquetas: la etiqueta a para referirse al asa
#[VARIABLES] y la etiqueta b para referirse al asa #idents.

Para referirnos a todos los asas sintetizados por el cuerpo de una regla se tiene la expresin ##.
La programacin de asas es una actividad importante en el desarrollo de un procesador.
ANTLR construye asas de forma automtica segn el orden de derivacin. Por ejemplo, la regla
definicion : VAR DEF expr PyC; construira un asa #definicion con nodo raz VAR y
con 3 asas hijos (de izquierda a derecha: #[DEF], #expr y #[PyC]). Podemos cambiar la forma
de sintetizar el asa haciendo uso de los operadores ^ y !. El operador ^ slo se puede usar como
sufijo de un lexema y sirve para enraizar. El operador ! se puede usar como sufijo de cualquier
smbolo y sirve para evitar la construccin de asas. Por ejemplo, la regla definicion : VAR
DEF^ expr PyC!; construye un asa llamado #definicion donde DEF es la raz , VAR es el
primer asa hijo (con un nico nodo) y expr es el segundo asa hijo. PyC es un lexema que no
forma parte del asa. Cuando este operador ! se usa como sufijo en la cabeza de la regla, la regla
no construye ningn asa de forma automtica.

ANTLR proporciona una biblioteca para programar asas llamada antlr. Esta biblioteca incluye
una interfaz AST para el tipo rbol sintaxis abstracta y la clase CommonAST para implementarlo.
El nodo del asa tiene 2 campos principales: el lexema y el texto asociado el lexema. Podemos
consultar estos campos con las operaciones: getType() y getText().Podemos modificar
estos campos con las operaciones: setType(int) y setText(String).
Las operaciones getFirstChild() y getNextSibling() servirn para visitar los nodos del
asa y las operaciones setFirstChild(AST) and setNextSibling(AST) para construir el
asa.
Departamento de Lenguajes y Sistemas Informticos
Procesadores de Lenguajes
7

ANTLR tambin proporciona una clase ASTFactory que permite hacer una copia de un asa
dupTree(AST) y una copia de una secuencia de asas dupList(AST).


CMO SE DISEA Y CONSTRUYE UN ANALIZADOR LXICO-
SINTCTICO?

Recomendamos los siguientes pasos para el diseo de un analizador lxico-sintctico.
Dado un lenguaje,
(1) Disear una gramtica determinista.
(2) Disear los lexemas definidos en la gramtica.
(3) Implementar una primera versin del analizador lxico-sintctico en ANTLR.
(4) Testar dicha versin.
(5) Anotar la gramtica para construir rboles de sintaxis abstracta.
(6) Implementar la segunda versin del analizador lxico-sintctico en ANTLR.
(7) Testar el analizador con distintas clases de sentencias del lenguaje.

El diseo de una gramtica se basa en un conjunto de recomendaciones:
La estructura sintctica del lenguaje debe interpretarse de forma jerrquica. La cabeza de la
regla pone nombre a la estructura sintctica y el cuerpo define dicha estructura.

Las estructuras alternativas del lenguaje pueden expresarse con cuerpos alternativos. Por
ejemplo, definicion|evaluacion.

Las estructuras repetitivas del lenguaje pueden expresarse con reglas iterativas o recursivas. Por
ejemplo, INSTRUCCIONES (definicion|evaluacion)* y expr : expr1 MAS expr
| expr1 MENOS expr | expr1.

Las piezas elementales de una estructura se formalizan con lexemas. Por ejemplo,
INSTRUCCIONES.

Los cuerpos de las reglas deben ser simples de interpretar. De esta manera, resultar ms
inteligible la estructura sintctica del lenguaje.

Una vez diseada las reglas de la gramtica, nos planteamos si permite realizar anlisis
descendente deterministas. Si no es as, hay que transformar la gramtica haciendo uso de
predicados sintcticos.

Una vez diseada la gramtica se tiene constancia de los lexemas del lenguaje. El diseo de
los lexemas se basa en un conjunto de recomendaciones:
Departamento de Lenguajes y Sistemas Informticos
Procesadores de Lenguajes
8

Los lexemas con formas alternativas de caracteres pueden expresarse con expresiones
regulares alternativas. Por ejemplo, BTF: (' '|'\t'|NL);.

Los lexemas con formas iterativas de caracteres pueden expresarse con expresiones regulares
de tipo cierre. Por ejemplo, NUMERO : (DIGITO)+;.

Los lexemas con partes opcionales pueden expresarse con expresiones regulares de tipo
opcin.

Los lexemas con forma de rango de caracteres pueden expresarse con expresiones regulares de
tipo rango. Por ejemplo, LETRA : 'a'..'z'|'A'..'Z';

Los lexemas con forma de secuencia de caracteres pueden expresarse con expresiones
regulares de tipo concatenacin. Por ejemplo, INSTRUCCIONES: "INSTRUCCIONES";

Los lexemas con 1 carcter pueden expresarse con expresiones regulares de tipo carcter.. Por
ejemplo, MAS : '+' ;.

El uso de lexemas auxiliares facilita la legibilidad de las expresiones regulares. Por ejemplo, VAR
: (LETRA)+; siendo LETRA : 'a'..'z'|'A'..'Z'; un lexema auxiliar.


En este punto, se propone la implementacin de la primera versin del analizador lxico-
sintctico en ANTLR.
La gramtica mostrada en Ejemplo 5 constituir el ncleo principal del parser ANTLR.
Ejemplo 6. Parser Antlr para el lenguaje LF.
class Anasint extends Parser;

programa : variables instrucciones EOF
;
variables : VARIABLES idents PyC
;
idents : (VAR COMA) => VAR COMA idents
| VAR
|
;
instrucciones : INSTRUCCIONES (definicion|evaluacion)*
;
definicion : VAR DEF expr PyC
;
evaluacion : EVAL idents PyC
;
expr : (expr1 MAS) => expr1 MAS expr
| (expr1 MENOS) => expr1 MENOS expr
| expr1
;
expr1 : (expr2 POR) => expr2 POR expr1
| expr2
;
expr2 : NUMERO
| MENOS expr
| VAR
Departamento de Lenguajes y Sistemas Informticos
Procesadores de Lenguajes
9

| PA expr PC
;
De forma similar, los lexemas mostrados en Ejemplo 4 constituir el ncleo principal de la
implementacin del lexer ANTLR.
Ejemplo 7. Lexer Antlr para el lenguaje LF.
class Analex extends Lexer;

options{
importVocab = Anasint;
}
tokens{
VARIABLES="VARIABLES";
INSTRUCCIONES="INSTRUCCIONES";
EVAL="EVAL";
DEF="DEF";
}
protected NL: "\r\n" {newline();} ;
protected DIGITO : '0'..'9';
protected LETRA : 'a'..'z'|'A'..'Z';
BTF: (' '|'\t'|NL) {$setType(Token.SKIP);} ;
NUMERO : (DIGITO)+ ;
VAR : (LETRA)+ ;
PA : '(' ;
PC : ')' ;
PyC : ';' ;
COMA : ',' ;
MAS : '+' ;
MENOS : '-' ;
POR : '*' ;

Construida la primera versin del analizador lxico-sintctico, se procede a testarlo. El siguiente
programa Java servir para realizar el testing. La idea es configurar en Eclipse un entorno de
ejecucin con programas LF de pruebas pasados como argumento. Los programas LF de
pruebas deben ser de 2 tipos: programas correctos y programas incorrectos. El analizador debe
aceptar los programas correctos y elevar excepciones de reconocimiento para los incorrectos.
Ejemplo 8. Programa para testar el analizador lxico-sintctico.
import java.io.FileInputStream;
import java.io.IOException;
import java.io.FileNotFoundException;
import antlr.RecognitionException;
import antlr.TokenStreamException;

public class Principal {

public static void main(String[] args) {
try{
FileInputStream f = new FileInputStream(args[0]);
Analex analex = new Analex(f);
Anasint anasint = new Anasint(analex);
// Anlisis lxico-sintctico
anasint.programa();
f.close();
Departamento de Lenguajes y Sistemas Informticos
Procesadores de Lenguajes
10

}catch(FileNotFoundException e)
{System.out.println("Error in file");}
catch(TokenStreamException e)
{System.out.println("Error in lexical analysis");}
catch(RecognitionException e)
{System.out.println("Error in parser analysis");}
catch(IOException e)
{System.out.println("Error in file");}

}
}



Si lo que se quiere testar es slo el analizador lxico entonces el programa de prueba para
hacerlo se muestra en el siguiente ejemplo.

Ejemplo 9. Programa para testar el analizador lxico.
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import antlr.Token;
import antlr.TokenStreamException;

public class Principal2 {

public static void main(String[] args) {
try{

FileInputStream f = new FileInputStream(args[0]);
Analex analex = new Analex(f);
Token t = analex.nextToken();
while (t.getType()!= Token.EOF_TYPE){
System.out.print("lexema:"+t.getType()+
"("+t.getText()+")"+", ");
t = analex.nextToken();
}
}catch(FileNotFoundException e)
{System.out.println("Error in file");}
catch(TokenStreamException e)
{System.out.println("Error in lexical analysis");}

}
}


Una vez testada la primera versin del analizador, se anota la gramtica para que sea capaz
de construir rboles de sintaxis abstracta. Para anotar la gramtica, primero hay que tener
claro cul es la estructura del rbol. El siguiente ejemplo muestra la estructura de asa requerida.



Departamento de Lenguajes y Sistemas Informticos
Procesadores de Lenguajes
11





Ejemplo 10. rbol de sintaxis abstracta.


La gramtica mostrada en el Ejemplo 5 construye el rbol mostrado en el Ejemplo 10.

Ejemplo 11. Parser Antlr para el lenguaje LF con la capacidad de construir ASAs.
class Anasint extends Parser;
options{
buildAST = true; // activar la sntesis de asas durante
// el anlisis.
}
tokens{
PROGRAMA;
}
programa : variables instrucciones EOF!
{#programa = #(#[PROGRAMA, "PROGRAMA"], ##);}
;
variables !: a:VARIABLES b:idents PyC {#variables = #(#a,#b);}
;
idents : (VAR COMA) => VAR COMA! idents
| VAR
Departamento de Lenguajes y Sistemas Informticos
Procesadores de Lenguajes
12

|
;
instrucciones : INSTRUCCIONES^ (definicion|evaluacion)*
;
definicion : VAR DEF^ expr PyC!
;
evaluacion : EVAL^ idents PyC!
;
expr : (expr1 MAS) => expr1 MAS^ expr
| (expr1 MENOS) => expr1 MENOS^ expr
| expr1
;
expr1 : (expr2 POR) => expr2 POR^ expr1
| expr2
;
expr2 : NUMERO
| MENOS^ expr
| VAR
| PA! expr PC!
;

Construida la segunda versin del analizador lxico-sintctico, se procede a testarlo. El
siguiente programa Java servir para realizar el testing.

Ejemplo 12. Programa para testar el analizador lxico-sintctico.
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import antlr.RecognitionException;
import antlr.TokenStreamException;
import antlr.collections.AST;
import antlr.debug.misc.ASTFrame;

public class Principal {

public static void main(String[] args) {
try{

FileInputStream f = new FileInputStream(args[0]);
Analex analex = new Analex(f);
Anasint anasint = new Anasint(analex);
// Anlisis lxico-sintctico
anasint.programa();
AST a = anasint.getAST();
ASTFrame af = new ASTFrame(args[0],a);
af.setVisible(true); // mostrar el asa por pantalla
}catch(FileNotFoundException e)
{System.out.println("Error in file");}
catch(RecognitionException e)
{System.out.println("Error in parser analysis");}
catch(TokenStreamException e)
{System.out.println("Error in lexical analysis");}
}
}

Potrebbero piacerti anche