RESUMEN UNIDAD 3
4.4.- Integración de JFlex (analizadores léxico y sintáctico)
CARACTERÍSTICAS DE JFLEX
Las características sobre salientes de JFlex son las siguientes:
Soporte completo con caracteres Unicode
Permite generar analizadores léxicos rápidamente.
Tiene una sintaxis cómo da de manipular y fácil de interpretar.
Es independiente de la plataforma debido a que esta diseñado
para ser integrado con Java.
Permite la integración con CUP (Analizador sintáctico
ESTRUCTURA UN ARCHIVO DE JFLEX
Un archivo JFex esta dividido en 3 secciones:
Opciones y declaraciones
Código de usuario
Reglas lexicográfica
OPCIONES Y DECLARACIONES
La primera parte del archivo es el bloque donde se importaran los paquete que se van a utilizar para nuestro analizador, es decir, si en nuestro programa utilizaremos componentes del paquete útil debemos importar aquí dicho paquete:
import java.util.*;
Luego sigue un par de signos de porcentaje ( %) para indicar que empezará la definición del bloque de configuración del analizador.
El bloque de configuración se define por el conjunto de parámetros que Unicode, es una gran tabla, que en la actualidad asigna un código a cada uno de los más de cincuenta mil símbolos, los cuales abarcan todos los alfabetos europeos, ideogramas chinos, japoneses, coreanos, muchas otras formas de escritura, y más de un millar de símbolos especiales.
Código de usuario
En el siguiente fragmento de código podremos incluir código Java el cual podemos utilizar en el analizador, cabe notar que el código que aquí se escriba será incluido sin ninguna alteración al resultado final del analizador, dicho fragmento ira enmarcado entre las etiquetas %{ al inicio del código y %} al final del mismo.
%{
public static void escribir(String cadena)
{
System.out.println(cadena);
}
%
}
Reglas lexicográficas
El siguiente fragmento formará parte esencial dentro del funcionamiento del analizador, en este se definirán el conjunto de expresiones regulares que se utilizarán durante el proceso de análisis, a continuación se presentan unos ejemplos de este tipo de declaraciones:
FinDeLinea = \r | \n | \r\n
Variable = [:jletter:][:jletterdigit:]*
Si = “Si”
Entero = 0 | [1-9][0-9]*
Codificación de caracteres
%7bit Permite usar entradas de 7 bits, es decir entre 0-127.
Si una entrada es mayor que 127 se generará un error en tiempo de ejecución y se lanzará una excepción de tipo:
ArrayIndexOutofBoundsException.
4.5.- Implementación de analizador léxico sintáctico
Analizador Sintáctico(Parser)
Sintaxis: El orden correcto de las palabras
Ej. Programa Fuente: 34:= *x - 640;
Analex: NUM ASIGN POR ID MENOS NUM PTOCOMA
Parser: "Error Sintáctico"
Haciendo usos de la estructura de análisis de un diagrama de Conway verificamos que todos los caminos desde el inicio de nuestro código, hasta el final; representan formas senténciales válidas.
main ( ) {
get_token ( );
do {
expresión ( );
while (token != PUNTOYCOMA) {!Error en expresión¡
get_token ( );
};
get_token( );
}while (token != EOF);
};
En este caso se considera al ‘;’ como un token de seguridad, lo que permite hacer una recuperación de errores mediante el método panic mode.
El programa flex por medio de su proceso de compilación reconoce los errores sintácticos dentro de la estructura de análisis. Estos errores corresponden a que el analizador sintáctico pide al lexicográfico tokens a través de get_token ( );, y que el lexicográfico deja el token actual en la variable global token.
Es decir que antes de entrar a una función, en token debemos tener el token de lookahead, que esa función necesita consultar.
4.1.- IDENTIFICADORES DE USUARIO
4.2.- TABLA DE SÍMBOLOS
Una tabla de símbolos es una estructura de datos usada en el proceso de traducción de un lenguaje de programación (por un compilador o un intérprete), dónde cada símbolo en el código fuente de un programa está asociado con información tal como la ubicación, el tipo de datos y el ámbito de cada variable, constante o procedimiento.
Estructura
Cada entrada de la tabla de símbolos corresponde a al declaración de un nombre. El formato de las entradas no tiene que ser uniforme porque la información de un nombre depende del uso de dicho nombre. Cada entrada en principio puede considerarse:
( Nombre, descriptor )LEXEMA ATRIBUTOS
Interfaz de la tabla de símbolos
Como ya se ha comentado, la interfaz de la tabla de símbolos debe quedar clara desde el principio de manera que cualquier modificación en la implementación de la tabla de símbolos no tenga repercusión en las fases del compilador ya desarrolladas. Las operaciones básicas que debe poseer son:
crear(): crea una tabla vacía.
insertar(símbolo): añade a la tabla el símbolo dado.
buscar(nombre): devuelve el símbolo cuyo nombre coincide con el parámetro. Si el símbolo no existe devuelve null.
imprimir(): a efectos informativos,
visualiza por la salida estándar la lista de variables almacenadas en la tabla de símbolos junto con sus valores asociados.
Operaciones con la Tabla de símbolos.
En general en la Tabla de símbolos (TS a partir de ahora) se realizan dos operaciones: la inserción y la búsqueda.
En C la operación de inserción se realiza cuando se procesa una declaración.
Hay dos posibilidades: que la TS esté ordenada (o sea, nombres de variables por orden alfabético) o que no esté ordenada.
La estructura inicial de la tabla de símbolos suele constar de dos partes:
PARTE FIJA
formada por las palabras clave del lenguaje (suelen ser de uso reservado y del orden de unas decenas de palabras en un lenguaje programación típico)
PARTE VARIABLE
definida por el programador: con el significado de los identificadores utilizados en cada frase (programa).
4.3 TABLA DE SIMBOLOS EJEMPLOS
- crear : Crea una tabla vacía.
- insertar: Parte de una tabla de símbolo y de un nodo, lo que hace es añadir ese nodo a la cabeza de la tabla.
- buscar: Busca el nodo que contiene el nombre que le paso por parámetro.
- imprimir: Devuelve una lista con los valores que tiene los identificadores de usuario, es decir recorre la tabla de símbolos. Este procedimiento no es necesario pero se añade por claridad, y a efectos de resumen y depuración
El programa lex :
Si se encuentra un número lo convierte a int y devuelve el token NUMERO.
Si se encuentra un identificador de usuario:
Primero lo busca en la tabla de símbolos, Si lo encuentra, entonces devuelve su valor Si no lo encuentra, lo inserta
El programa Yacc:
%union {
int numero;
simbolo * ptr_simbolo;
}
Esto es un registro con parte variante, en el que todo es parte variante. Si ponemos
%union ya no hay que poner YYSTYPE.
Cosas interesantes de la gramática: prog :
prog asig
...
| (J forma parte de un prog).
;
asig : ID ASIG expr
| ID ASIG asig
Como podemos observar esta regla es recursiva a la derecha. Se permiten cosas como a := b := c := 3 * 4
En cuanto a la regla expr produce ambigüedad, ésta se soluciona introduciendo las instrucciones
% left ‘+’
% left ‘*’
El atributo del identificador de usuario ‘ID’, es un puntero a un símbolo.