Está en la página 1de 11

República Bolivariana de Venezuela

Ministerio del Poder Popular para la Educación Universitaria


Universidad Jose Antonio Paez
Facultad de Ingeniería

Análisis Léxico

Docente:
Alumno:
Susan León Sebastian Salazar
28.465.047
Fernando Rodríguez 27.589.678
San diego, 29 de abril de 2023
Funciones del análisis léxico

Leer los caracteres de entrada y elaborar como salida una secuencia de


componentes léxicos que utiliza el analizador sintáctico para hacer el análisis.

El analizador léxico es la primera fase de un compilador.

Su principal función consiste en leer los caracteres de entrada y elaborar como


salida una secuencia de componentes léxicos que utiliza el analizador sintáctico
para hacer el análisis. Esta interacción, suele aplicarse convirtiendo al analizador
léxico en una subrutina o corrutina del analizador sintáctico. Recibida la orden
"obtén el siguiente componente léxico" del analizador sintáctico, el analizador
léxico lee los caracteres de entrada hasta que pueda identificar el siguiente
componente léxico.

Funciones secundarias

Como el analizador léxico es la parte del compilador que lee el texto fuente.
También puede realizar ciertas funciones secundarias en la interfaz del usuario,
como eliminar del programa fuente comentarios y espacios en blanco en forma de
caracteres de espacio en blanco, caracteres TAB y de línea nueva. Otra función es
relacionar os mensajes de error del compilador con el programa fuente. Por
ejemplo, el analizador léxico puede tener localizado el número de caracteres de
nueva línea detectados, de modo que se pueda asociar un número de línea con un
mensaje de error.

En algunos compiladores, el analizador léxico se encarga de hacer una copia del


programa fuente en el que están marcados los mensajes de error. Si el lenguaje
fuente es la base de algunas funciones de pre procesamiento de macros,
entonces esas funciones del preprocesador también se pueden aplicar al hacer el
análisis léxico.
Especificación de los componentes léxicos: expresiones regulares.

Las expresiones regulares son una notación importante para especificar patrones.
Cada patrón concuerda con una serie de cadenas, de modo que las expresiones
regulares servirán como nombres para conjuntos de cadenas.

Cadenas y lenguajes

El término alfabeto o clase de carácter denota cualquier conjunto finito de


símbolos. Ejemplos típicos de símbolos son las letras y los caracteres. El conjunto
{0, 1} es el alfabeto binario. Los códigos ASSCII y EBCDIC son dos ejemplos de
alfabetos de computador.

Una cadena sobre algún alfabeto es una secuencia finita de símbolos tomados de
ese alfabeto. En teoría del lenguaje, los términos frase y palabra a menudo se
utilizan como sinónimos del término "cadena". La longitud de una cadena s, que
suele escribirse lsl, es el número de apariciones de símbolos en s. Por ejemplo,
camino es una cadena de longitud seis. La cadena vacía, representada por Є, es
una cadena especial de longitud cero.

El término lenguaje se refiere a cualquier conjunto de cadenas de un alfabeto fijo.


Esta definición es muy amplia, y abarca lenguajes abstractos como Ǿ, el conjunto
vacío, o {Є} y el conjunto que sólo contiene la cadena vacía, así como al conjunto
de todos los programas de Pascal sintácticamente bien formados y el conjunto de
todas las oraciones en inglés gramaticalmente correctas, aunque los dos últimos
conjuntos son mucho más difíciles de especificar.

Hay varias operaciones importantes que se pueden aplicar a los lenguajes. Para el
análisis léxico, interesan principalmente la unión, la concatenación y la cerradura.
Reconocimiento de los componentes léxicos: autómatas finitos.

En esta sección, se estudia el reconocimiento de los componentes léxicos y se


utiliza como ejemplo el lenguaje generado por la siguiente gramática.

prop -> if expr then prop

| if expr then prop else prop

expr -> término oprel término

| término

término -> id

| núm

donde los terminales if, then, else, oprel, id y núm generan conjuntos de cadenas
dados por las siguientes definiciones regulares:

if -> if

then -> then

else -> else

oprel -> < | <= | = | <> | > | >=

id -> letra (letra | digito) *

núm-> digito+ (. dígito+)? (E (+ | -)? dígito+.)?

donde letra y dígito se han definido anteriormente.


Para este fragmento de lenguaje, el analizador léxico reconocerá las palabras
clave if, then, else, al igual que los lexemas representados por oprel, id y núm.
Para simplificar las cosas, se supone que las palabras clave son reservadas; es
decir, no se pueden usar como identificadores, núm representa los números
enteros y reales sin signo de Pascal.

Además, se supone que los lexemas están separados por espacio en blanco,
formados por secuencias no nulas de espacios en blanco, caracteres TAB y
caracteres de nueva línea. El analizador léxico eliminará los espacios en blanco.
Esto lo hará comparando una cadena con la definición de la expresión regular eb
siguiente.

delim -> blanco | tab | lineanueva

eb -> delim+

Si se encuentra una concordancia para eb, el analizador léxico no devuelve un


componente léxico al analizador sintáctico, sino que se dispone a encontrar un
componente léxico a continuación del espacio en blanco y lo devuelve al
analizador sintáctico.

El objetivo es construir un analizador léxico que aislé el lexema para el siguiente


componente Léxico del buffer de entrada y que produzca como salida un par
formado por el componente léxico apropiado y el valor de atributo, utilizando la
tabla de traducción mostrada a continuación. Los valores de atributo para los
operadores relacionales están dados por las constantes simbólicas MEN, MEI,
IGU, MAY, MAI.
Implementación de un analizador léxico

Mediante bucles anidados

Se usa una variable para almacenar el estado actual y una estructura tipo “case”
doble anidada. El primer “case” comprueba el estado actual y el siguiente el
carácter en la entrada. Las transiciones se corresponden con asociar un nuevo
estado a la variable y avanzar en la entrada

Ch=next input char;

state = 1;

while (state == 1 o 2) do

case state

1: case ch

Letter: avanzar en la entrada; state=2;

otro caso: state = error u otro

fin_case;

2: case ch

letter, digit: avanzar en la entrada; state=2 (no necesario);

otro caso: state = 3;

fin_case;

fin_case;

fin_while;

if (state==3) then aceptar else error;


El código que se genera es largo y difícil de mantener en el caso de que se
introduzcan nuevos caracteres en el alfabeto de entrada o nuevos estados.

Mediante una tabla de transiciones

Input/state letter digit other Accepting


1 2 no
2 2 2 [3] no
3 yes

Se asume que los campos en blanco son errores. Los estados de aceptación se
marcan con una columna adicional. Los corchetes representan que no se tiene
que consumir un carácter en la entrada (no avanzar).

state = 1;

ch = next_input_character;

while (not Accept [state]) and (not error(state)) do

newstate = T [state, ch];

if Advance [state, ch] then ch=next_input_char;

state=newstate;

end while;

if Accept[state] then accept;


“Advance” y “Accept” son dos arrays booleanos indexados por el estado y por el
carácter en la entrada. El primero indica si tenemos que avanzar en la entrada. El
segundo si tenemos un estado de aceptación.

Aspectos prácticos en la implementación de un análisis léxico:

Principio de máxima longitud

Se da prioridad al componente léxico de máxima longitud.

Por ejemplo: <> se interpreta como el operador “distinto de”, en vez de “menor
que” y “mayor que”

Por ejemplo: “ende” se interpreta como el identificador “ende” y no como la palabra


reservada “end” y la letra e

Palabras reservadas versus identificadores

Un lenguaje de programación puede tener del orden de 50 palabras reservadas.


Para evitar tener un AFD demasiado grande las palabras reservadas se reconocen
como identificadores (una palabra reservada también es una letra seguida de más
letras) y se comprueba antes de decidir el tipo de “token” si se trata de una palabra
reservada o de un identificador consultando una tabla previamente inicializada con
las palabras reservadas. Este método es recomendable cuando el número de
palabras reservadas es grande.

Gestión de buffer de entrada

El proceso de lectura de los caracteres de la entrada y formar los componentes


léxicos es lo más costoso en tiempo en el proceso de traducción. Es importante
implementarlo eficientemente. Se utiliza un buffer dividido en dos mitades de
tamaño N caracteres, donde N es un bloque de disco (1024, 4096). Se leen N
caracteres de la entrada en cada mitad del buffer con una única orden de lectura.
Se mantienen dos apuntadores. Uno marca el inicio del lexema y el otro el
carácter actual que se mueve hasta encontrar una subcadena que corresponde
con un patrón. Una vez reconocido un componente léxico ambos apuntadores se
colocan en la misma posición y justo detrás del lexema reconocido.

Errores léxicos y su tratamiento.

Los errores léxicos constituyen la categoría de errores más numerosos y


distractores en el aprendizaje de una segunda lengua, pues refieren al incorrecto
uso en la producción tanto oral como escrita de un ítem léxico del idioma que se
está aprendiendo. De ese modo, su existencia conlleva a que el mensaje que se
pretende entregar en la situación comunicativa se vea afectado parcial o
totalmente, generando en muchos casos la completa incomprensión del mismo.

Tratamiento de los errores léxicos

Un traductor debe adoptar alguna estrategia para detectar, informar y recuperarse


para seguir analizando hasta el final.

Las respuestas ante el error pueden ser:

 Inaceptables: Provocadas por fallos del traductor, entrada en lazos


infinitos, producir resultados erróneos, y detectar sólo el primer error y
detenerse.
 Aceptables: Evitarla avalancha de errores (mala recuperación) y, aunque
más complejo, informar y reparar el error de forma automática. La conducta
de un Analizador de Léxico es el de un Autómata finito o “scanner”.
 Detección del error: El analizador de Léxico detecta un error cuando no
existe transición desde el estado que se encuentra con el símbolo de la
entrada. El símbolo en la entrada no es el esperado.

Los errores léxicos se detectan cuando el analizador léxico intenta reconocer


componentes léxicos y la cadena de caracteres de la entrada no encaja con
ningún patrón. Son situaciones en las que usa un carácter invalido (@, $,",>,), que
no pertenece al vocabulario del lenguaje de programación, al escribir mal un
identificador, palabra reservada u operador.

Errores léxicos típicos son:

1. Nombres ilegales de identificadores: un nombre contiene caracteres


inválidos.
2. Números incorrectos: Un numero contiene caracteres inválidos o no está
formado correctamente, por ejemplo 3,14 en vez de 3.14 o 0.3.14.
3. Errores de ortografía en palabras reservadas: caracteres omitidos,
adicionales o cambiados de sitio, por ejemplo, la palabra while en vez de
hwile.
4. Fin de archivo: se detecta un fin de archivo a la mitad de un componente
léxico.

Los errores léxicos se deben a descuidos del programador. En general, la


recuperación de errores léxicos es sencilla y siempre se traduce en la generación
de un error de sintaxis que será detectado más tarde por el analizador sintáctico
cuando el analizador léxico devuelve un componente léxico que el analizador
sintáctico no espera en esa posición.

Los métodos de recuperación de errores léxicos se basan bien en saltarse


caracteres en la entrada hasta que un patrón se ha podido reconocer; o bien usar
otros métodos más sofisticados que incluyen la inserción, borrado, sustitución de
un carácter en la entrada o intercambio de dos caracteres consecutivos

Generadores automáticos de analizadores léxicos: Lex:


Lex genera código fuente en C, a partir de una serie de especificaciones escritas
en lenguaje Lex. El código C generado contiene una función llamada yylex (), que
localiza cadenas en la entrada (lexemas) que se ajusten a uno de los patrones
léxicos especificados en el código fuente Lex, realizando entonces las acciones
asociadas a dicho patrón. yylex () puede llevar a cabo cualquier tipo de acciones
ante un determinado patrón y, en particular, puede comportarse como un
analizador léxico.

Funcionamiento:

Lex no es un analizador sino un generador de analizadores. Esto permite incluir de


manera cómoda un analizador a medida en cualquier programa:

Programa fuente en Compilador de Lex


Lex (.1) lex.yy.c
(lex, flex)

Compilador de C
lex.yy.c a.out
gcc -1
gcc -1f1

Fichero de entrada a.out Acciones

Link del video: https://www.youtube.com/watch?


v=5GlBeUtCzm8

También podría gustarte