Está en la página 1de 53

Lenguajes y Autómatas 1.

Unidades:

Unidad 1: Introducción a la Teoría de Lenguajes y


Autómatas.
Unidad 2: Expresiones Regulares.
Unidad 3: Autómatas Finitos.
Unidad 4: Máquinas de Turing.
Unidad 5: Análisis léxico.
Unidad 6: Análisis Sintáctico.
Unidad 1: Introducción a la Teoría de Lenguajes y
Autómatas.
Introducción:

El objetivo de esta unidad es identificar los conceptos teóricos y necesarios sobre


la materia teoría de Lenguajes y autómatas 1.

Unidad 1 - Introducción a la Teoría de Lenguajes Formales.

1.1 Alfabeto

Un alfabeto es un conjunto finito no vacío cuyos elementos se llaman símbolos.


Denotamos un alfabeto arbitrario con la letra Σ.

Símbolos:

Es una entidad abstracta que no se puede definir, ya que se dejaría como una
axioma. Igual que se define un punto en la geometría. La cual normalmente los
símbolos son letras (a, b, c,…. z), dígitos (0,1,…9, caracteres (+, -, *, /,>,< …..). los
símbolos pueden estar formados por varias letras o caracteres.

Alfabeto:

El alfabeto o abecedario es un conjunto de letras, con un determinado orden.


Podríamos precisamente decir que el alfabeto es un conjunto de letras (caracteres
o grafemas) de un sistema de escritura, cada una representa aproximadamente un
fonema (consonante o vocal).

1.2 Cadenas.

Una cadena o palabra sobre un alfabeto Σ. admitimos la existencia de una única


cadena que no tiene símbolos, la cual se denomina cadena vacía y se denota con
λ. la cadena vacía desempeña, en la teoría de lenguajes formales, un papel similar
al que desempeña el conjunto vacío Ø en la teoría de conjuntos.

Longitud de cadena.

La longitud de cadena es el número de símbolos que contiene. La notación


empleada es la que es la que se indica en el ejemplo:

Utilizamos las cadenas de los ejemplos:

I abcb I = 4,

I a + 2*b I = 5
I 000111 I = 6

I if a > b then a = b; I = 9

Cadena Vacía.

Una cadena vacía es la única cadena de caracteres de tamaño cero. Y la podemos


denotar usualmente con letras λ o Є (Griegas).

Concatenación de cadenas.

La concatenación de dos cadenas u y v, escrita uv, es "pegar" las dos cadenas para
formar una nueva.

Ejemplo:

Sea u = ab

v = ca

w = bb. Entonces

uv = abca

uw = cabb

(uv) w = abcabb

u(vw) = abcabb

El resultado de la concatenación de u, v y w es independiente del orden en que las


operaciones son ejecutadas. Matemáticamente esta propiedad es conocida como
asociatividad.
Universo del discurso.

Es un conjunto de todas las cadenas donde podemos formar con símbolos del
alfabeto V le denominamos universo del discurso de V y la representamos de la
siguiente manera W (V). Es evidente que W(V) es un conjunto infinito y que la
cadena vacía pertenece a W(V).

Ejemplo:

Un afabeto con una sola letra V = { a }, podemos decir que el universo del discurso
es: W(V) = { λ, a, aa, aaa, aaaa,....} y asi contiene una cadenas infinitas.

1.1 Lenguajes.

Es un conjunto de cadenas, de todas las seleccionadas de un Σ*. Donde Σ


determinado el alfabeto se denomina lenguaje. Si Σ es un alfabeto y L Σ*, entonces
L es un lenguaje de Σ. Observe que un lenguaje de Σ no necesita incluir cadenas
con todos los símbolos de Σ, ya que una vez que hemos esta que L es un lenguaje
de Σ, también sabemos que es un lenguaje de cualquier alfabeto que sea un súper
conjunto de Σ.

La elección del término "lenguaje" puede parecer extraña. Sin embargo, los
lenguajes habituales pueden interpretarse como conjuntos de cadenas. Un ejemplo
seria el inglés, donde la colección de las palabras correctas inglesas es un conjunto
de cadenas del alfabeto que consta de todas las letras. Otro ejemplo es el lenguaje
C.

1.2 Tipos de lenguajes.

Lenguaje natural (castellano)

Nosotros estamos relacionados con el concepto tradicional de gramática que, de


esta forma intuitiva, podemos considerar un conjunto de reglas el cual nos indican
que es correcto y que no lo es del, lenguaje natural. Con este fin podemos acércanos
a la definición más clara y formal de la lengua castellana.

Lenguaje artificial.

En este lenguaje aplicamos el mismo método en el cual definimos un fragmento del


lenguaje de programación. Donde pretendemos describir las instrucciones el cual
nos permite asignar un valor a una expresión ó a una variable en un lenguaje C.

Lenguaje regular.

Llamamos así a los lenguajes porque sus palabras contienen "regularidades" o


repeticiones de los mismos componentes, por ejemplo en este lenguaje L1 = { ab,
abab, ababab, abababab,...} Este ejemplo podemos apreciar las palabras de L1 son
solo repeticiones de "ab" donde se repiten varias veces. Su regularidad consiste en
las palabras que contienen "ab" varias veces.

1.5 Herramientas computadoras ligadas con lenguajes.

Traductor.

Un traductor es un programa que tiene como entrada un texto escrito en un lenguaje


(lenguaje fuente) y como salida produce un texto escrito en un lenguaje (lenguaje
objeto) que preserva el significado de origen.

Ejemplos de traductores son los ensambladores y los compiladores.

Compilador.

El compilador es un programa informático que traduce un programa escrito en


lenguaje de programación y lo pasa a lenguaje de programación, podemos decir
que este programa nos permite traducir un código fuente de un programa en
lenguaje de nivel alto, y lo pasmos a otro nivel inferior (lenguaje maquina).

Ensambladores.

El ensamblador es el programa en que se realiza la tracción de un programa escrito


en ensamblador y lo pasa a lenguaje máquina. Directa o no directa la traducción en
que las instrucciones no son más que instrucciones que ejecuta la computadora.

Interpretes.

Los intérpretes son los que realizan normalmente dos operaciones:

 Traducen el código fuente a un formato interno.


 Ejecuta o interpretan el programa traducido al formato interno.

Donde la primera pertenece al interprete el cual llama a veces al compilador, así se


genera el código interno, pero no es el lenguaje de máquina, ni lenguaje de
símbolos, ni mucho menos un lenguaje de nivel alto.

Conclusión

De esta manera se vio los conceptos teóricos de alfabeto, cadena, símbolo, longitud
de cadena, etc. Así como cada uno de sus temas y subtemas.
Unidad 2 - Expresiones Regulares.

2.1. Definición formal de una ER.


Es un equivalente algebraico para un autómata.
 Utilizado en muchos lugares como un lenguaje
para describir patrones en texto que son
sencillos pero muy útiles.
 Pueden definir exactamente los mismos
lenguajes que los autómatas pueden describir:
Lenguajes regulares.
 Ofrecen algo que los autómatas no: Manera
declarativa de expresar las cadenas que
queremos aceptar.
Dado un alfabeto Dado un alfabeto Σ, una , expresión
regular sobre expresión regular sobre Σ se define de
forma recursiva:
 ER primitivas: Φ, λ, {a | a ЄЄЄ Σ Є}
 Si α y β son ER, entonces son también ER: α +
β (unión), α β (concatenación), α* (cierre), (α).
 No existen otras reglas para la construcción de
ER sobre Σ.
Ejemplos de usos.
 Comandos de búsqueda, e.g., grep de UNIX.
 sistema de formato de texto: Usan notación de
tipo expresión regular para describir patrones.
 Convierte la expresión regular a un DFA o un
NFA y simula el autómata en el archivo de
búsqueda.
 Generadores de analizadores - Léxicos. Como
Lex o Flex.
 Los analizadores léxicos son parte de un
compilador. Dividen el programa fuente en
unidades lógicas (tokens) divide el programa
fuente en unidades.
 Produce un DFA que reconoce el token.
Las expresiones regulares denotan lenguajes.
Por ejemplo, la expresión regular: 01* + 10* denota todas
las cadenas que son o un 0 seguido de cualquier
cantidad 1's o un 1 seguida de cualquier cantidad de 0's.
Operaciones de los lenguajes:
 Unión: Si L y M son dos lenguajes, su unión se
denota por L U M.
 Concatenación: La concatenación es: LM o L.M.
 Cerradura (o cerradura de Kleene): Si L es un
lenguaje su cerradura se denota por L *.
Si E es una expresión regular, entonces L(E) denota el
lenguaje que define E. Las expresiones se construyen
de la manera siguiente:
 Las contantes y son expresiones regulares que
representan a los lenguajes L (Q) = {Q} y L (Φ)
L = Φ respectivamente.
 Si a es un símbolo, entonces es una expresión
regular que representan al lenguaje: L (a) = {a}.
2.2. Operaciones
Unión o Alternativa: Consideremos dos lenguajes
diferentes definidos sobre el mismo alfabeto L1 ⊂ W(∑)
y L2 ⊂ W(∑). Se denomina unión de ambos lenguajes
al lenguaje formado por las palabras de ambos
lenguajes:

 L1 U L2={ x | x ∈ L1 ó x ∈ L2}
Concatenación: Consideremos dos lenguajes
definidos sobre el mismo alfabeto, L1 y L2. La
concatenación o producto de estos lenguajes es el
lenguaje L1 L2= { xy / x ∈ L1 y x ∈ L2} Las
palabras de este lenguaje estarán formadas al
concatenar cada una palabra del primero de los
lenguajes con otra del segundo.
La concatenación de lenguajes con el lenguaje vació
es ΦL = L Φ = Φ
Potencia de un lenguaje: Se define la potencia i-ésima de
un lenguaje a la operación de concatenarlo consigo
mismo i veces.
Li= LLL ....L
|------------|
i
Clausura positiva de un lenguaje: Se define la clausura
positiva de un lenguaje L:

L + = U L i
i=1
Lenguaje obtenido uniendo el lenguaje con todas sus
potencias posibles excepto Lº. Si L no contiene la
palabra vacía, la clausura positiva tampoco
Cierre o Clausura de un lenguaje: Se define el cierre o
clausura de un lenguaje L como :

L* = U Li
i=0
Lenguaje obtenido uniendo el lenguaje con todas sus
potencias posibles, incluso Lº. Todas las clausuras
contienen la palabra vacía.
Existen tres operaciones básicas que se pueden realizar
sobre las ER:
 Selección de alternativas : Se indica con el
operador |(barra vertical). Si r y s son ER,
entonces r | s es una ER que define a cualquier
cadena que concuerde con una r o una s,
también se dice que r | s , es la unión de los
lenguajes de r y s y lo podemos definir: L( r | s )
= L( r ) U L( s ). Esta operación se puede
extender a más de dos ER.
 Concatenación: Se indica con la yuxtaposición
de las ER. Si r y s son ER, entonces rs es una
ER que define a cualquier cadena que
concuerde con la concatenación de r y s , esta
operación la podemos definir: L(rs) =
L(r)L(s).Esta operación se puede extender a
más de dos ER.
Repetición o Cerradura: También se conoce con el
nombre de cerradura de Kleene. Se indica con el
operador *. Si r es una ER, entonces r* es una ER que
define a las cadenas de caracteres representadas por
la concatenación repetida de r en n veces, o sea que lo
podemos definir como: L(r*) = L(r)*o también lo
podemos definir como la unión infinita de conjuntos r :r*
n = r 0 r 1 r 2...r n.
2.3 Aplicaciones en problemas reales.
Una de las principales aplicaciones de los hermanos Deitel,
son las expresiones regulares que facilitan la
construcción de un compilador. A menudo se utiliza una
expresión regular larga y compleja para validar la
sintaxis de un programa. Si el código del programa no
concuerda con la expresión regular, el compilador sabe
que hay un error de sintaxis dentro del código.
Generalmente convierten la expresión regular a un
autómata finito no determinista y después construyen el
autómata finito determinista.
Otra aplicación del mismo libro es en los editores de texto.
También encontramos a las expresiones regulares en la
biología molecular. También hay esfuerzos importantes
para tratar de representar cadenas como generadas por
expresiones regulares o por lenguajes regulares.
Unidad 3: Autómatas Finitos.

Minimizar:
Los no finales son A, B, C Y D
Finales: E

Grupo Grupo
Grupo 1
1 1
A, B, C A, C A (representa A y C)
Grupo Grupo
Grupo 2
2 2
D B B
Grupo Grupo
Grupo 3
3 3
E D D
Grupo
Grupo 4
4
E E

a b
A B A
B B D
D B E
E B A

Autómatas Finitos

El término máquina evoca algo hecho en metal,


usualmente ruidoso y grasoso, que ejecuta tareas
repetitivas, que requieren de mucha fuerza o velocidad
o precisión. Ejemplos de estas máquinas son las
embotelladoras automáticas de refrescos. Su diseño
requiere de conocimientos en mecánica, resistencia de
materiales y hasta dinámica de fluidos. Al diseñar tal
máquina, el plano en que se le dibuja hace abstracción
de algunos detalles presentes en la máquina real, tales
como el color con que se pinta, o las imperfecciones
en la soldadura.

El término máquina evoca algo hecho en metal,


usualmente ruidoso y grasoso, que ejecuta tareas
repetitivas, que requieren de mucha fuerza o velocidad
o precisión. Ejemplos de éstas máquinas son las
embotelladoras automáticas de refrescos. Su diseño
requiere de conocimientos en mecánica, resistencia de
materiales y hasta dinámica de fluidos.

Autómatas Finitos (2)

Al diseñar tal máquina, el plano en que se le dibuja


hace abstracción de algunos detalles presentes en la
máquina real, tales como el color con que se pinta, o
las imperfecciones en la soldadura.

Autómatas Finitos (3)

El plano de diseño mecánico de una máquina es una


abstracción de ésta, que e útil para representar su
forma física. Sin embargo, hay otro enfoque con que
se puede modelar la máquina embotelladora: cómo
funciona, en el sentido de saber qué secuencia de
operaciones ejecuta. Así, la parte que introduce el
líquido para por un ciclo repetitivo en que primero
introduce un tubo en la botella, luego descarga el
líquido y finalmente sale el tubo para permitir la
colocación de la cápsula (corcho lata).

Autómatas Finitos (4)

El orden en que se efectúa este ciclo es crucial, pues


si se descarga el líquido antes de haber introducido el
tubo en la botella, el resultado no será satisfecho. Las
máquinas que estudiamos son abstracciones
matemáticas que capturan solamente el aspecto
referente a las secuencias de eventos que ocurren, sin
tomar en cuenta ni la forma de la máquina ni sus
dimensiones, ni tampoco si efectúa movimientos rectos
o curvos, etc.

Autómatas Finitos Deterministas (1)

Ahora es el momento de representar el concepto


formal de autómata finito, con el fin de precisar
algunos de los argumentos y descripciones informales
que se han visto anteriormente, comenzaremos con el
formalismo de un autómata finito determinista, que es
aquel que sólo puede estar en un único estado
después de leer cualquier secuencia de entradas. El
término «determinista» hace referencia al hecho de
que para cada entrada sólo existe uno y sólo un
estado al que el autómata puede hacer la transición a
partir de su estado actual.

Autómatas Finitos Deterministas (2)

El término «Autómata Finito» hace referencia a la


variedad determinista, aunque normalmente
utilizaremos el término «determinista», o la abreviatura
AFD, con el fin de recordar el tipo de autómata del que
estamos hablando.

Un Autómata Finito Determinista consta de:


 Un conjunto finito de estados, a menudo
designado como Q.
 Un conjunto finito de símbolos de entrada, a
menudo designado como ∑ (sigma).
 Una función de transición que toma como
argumentos un estado y un símbolo de entrada y
devuelve un estado. La función de transición se
designa habitualmente como δ o Δ (delta).
 Un estado inicial, uno de los estados de Q.
 Un conjunto de estados finales o de aceptación F.
El conjunto F es un subconjunto de Q.
A menudo haremos referencia a un autómata finito
determinista mediante su acrónimo: AFD. La
representación más sucinta de un AFD consiste en un
listado de los cinco componentes anteriores.
Normalmente, en las demostraciones, definiremos un
AFD utilizando la notación de «quíntupla» siguiente:
A= ( Q, ∑ , Δ ,q0, F ).
Unidad 4: Máquinas de Turing.
4.1 Definición formal MT
La Máquina de Turing (MT) fue introducida por Alan M.
Turing en 1936, y puede considerarse como un
modelo abstracto que formaliza la idea Intuitiva de
algoritmo.

(MT) Es un modelo computacional que realiza una


lectura/escritura de manera automática sobre una
entrada llamada cinta, generando una salida en esta
misma. Este modelo está conformado por un alfabeto
de entrada y uno de salida, un símbolo especial
llamado blanco (normalmente b, Δ o 0), un conjunto
de estados finitos y un conjunto de transiciones entre
dichos estados.

Su funcionamiento se basa en una función de


transición, que recibe un estado inicial y una cadena
de caracteres (la cinta, la cual es finita por la
izquierda) pertenecientes al alfabeto de entrada.
Luego va leyendo una celda de la cinta , borrando el
símbolo , escribir el nuevo símbolo perteneciente al
alfabeto de salida y finalmente avanza a la izquierda
o a la derecha (solo una celda a la vez), repitiendo
esto según se indique en la función de transición,
para finalmente detenerse en un estado final o de
aceptación, representando así la salida.

Esta constituida por los siguiente elementos:

MT = ( E, A, B, e0, F, f)

E = Conjunto de estados, no vacío.


A = Conjunto de símbolos de entrada.
B = Conjunto de símbolos auxiliares.
e0 = Estado inicial.
F = Conjunto de estados finales.
f = Función de control, definida:

donde: f: ( E - F ) x ( A È B ) Þ E x ( A È B) x (
I, O, D )

I = movimiento del cabezal a la izquierda.


O = movimiento nulo.
D = movimiento a la derecha.

La máquina de Turing consta de un cabezal


lector/escritor y una cinta infinita en la que el cabezal
lee el contenido, borra el contenido anterior y escribe
un nuevo valor. Las operaciones que se pueden
realizar en esta máquina se limitan a:

avanzar el cabezal lector/escritor para la derecha.


avanzar el cabezal lector/escritor para la izquierda.

4.2 Construcción modular de una MT.

El objetivo de la creación modular de una maquina de


Turing es poder desarrollar máquinas complejas a
partir de bloques elementales, a partir de maquinas
más pequeñas, mediante diagramas de transiciones.
La construcción de máquinas de Turing se lleva a
cabo mediante los diagramas de transición y
combinarlos de manera parecida a lo que se realiza
en la formación de la unión y concatenación de los
autómatas finitos.

Pasos para la construcción de una máquina de Turing:


 Elimine las características de inicio de los
estados iniciales de las maquinas, excepto la de
aquel donde iniciara la maquina compuesta.
 limine las características de detención de los
estados de parada de todas la maquinas e
introduzca un nuevo estado de parada que nos
se encuentre en ninguno de los diagramas que
se combinan.
 Para cada uno de los antiguos estados de
parada p y cada x en y.

Una máquina de Turing es un autómata que se mueve


sobre una secuencia lineal de datos. En cada
instante la máquina puede leer un solo dato de la
secuencia (generalmente un carácter) y realiza
ciertas acciones en base a una tabla que tiene en
cuenta su "estado" actual (interno) y el último dato
leído.

Entre las acciones está la posibilidad de escribir nuevos


datos en la secuencia; recorrer la secuencia en
ambos sentidos y cambiar de "estado" dentro de un
conjunto finito de estados posibles.

4.3 Lenguajes aceptados por la MT.

Una máquina de Turing se puede comportar como un


aceptador de un lenguaje. Si colocamos una cadena
w en la cinta, situamos la cabeza de lectura/escritura
sobre el símbolo del extremo izquierdo de la cadena
w y ponemos en marcha la máquina a partir de su
estado inicial. Entonces w es aceptada si, después
de una secuencia de movimientos, la máquina de
Turing llega a un estado final y para. Por tanto w es
aceptada. Si qw * w1pw2 para algún estado final p
y unas cadenas w1 y w2.

Entonces, se obtiene la siguiente definición:

Sea M = (Q, S , G, q0=q1, B, F, d) una máquina de


Turing. Entonces el lenguaje aceptado por M es: L(M)
= {wÎ S*½q1w * w1pw2 para pÎF y wiÎG*}.

Los lenguajes formales que son aceptados por una


máquina de Turing son exactamente aquellos que
pueden ser generados por una gramática formal. El
cálculo Lambda es una forma de definir funciones.
Las funciones que pueden se computadas con el
cálculo Lambda son exactamente aquellas que
pueden ser computadas con una máquina de Turing.
Estos tres formalismos, las máquinas de Turing, los
lenguajes formales y el cálculo Lambda son
formalismos muy disímiles y fueron desarrollados por
diferentes personas. Sin embargo, ellos son todos
equivalentes y tienen el mismo poder de expresión.
Generalmente se toma esta notable coincidencia
como evidencia de que la tesis de Church-Turing es
cierta, que la afirmación de que la noción intuitiva de
algoritmo o procedimiento efectivo de cómputo
corresponde a la noción de cómputo en una máquina
de Turing.

Gramáticas estructuradas por frases:


 Parte izquierda de las reglas: combinació
n de símbolos terminales y no terminales,
con al menos un no terminal.
 Parte derecha de las reglas: combinación de
símbolos terminales y no terminales de cualquier
longitud (incluso 0).
 Las máquinas de Turing aceptan lenguajes
estructurados por frases.
La M.T. como generadora de lenguajes.

L={an b2n an, con n mayor o igual a 0}

Entrada:

Cinta1: ...BBB...
Cinta2: ...BBB...
Salida:

Cinta1: ...0000...
Cinta2: ...λ$abba$aabbbbaa$...

Proceso:

El proceso de la maquina es sencillo, consiste en


generar 0's en la primera cinta y su correspondiente
lenguaje en la segunda cinta. Este proceso será
cíclico y sin fin, ya que estamos tratando con un
generador.

Para ello utilizamos multicinta porque nos facilita de


manera considerable el trabajo.

Ejemplifiques el funcionamiento de una Máquina de


Turing.

Supongamos que tenemos Σ={a,b} y q_f y que


representamos los enteros positivos mediante
cadenas solo de “as”. Así el entero “n” estaría
representado por a^n.

Se puede construir la MT que calcule la función f(n,m) =


n+m, implementando la transformación
a^n ba^m en a^(n+m) b

Solución:

Se recorren desde la izquierda todas las as hasta


encontrar una “b”, esta se reemplaza por una “a”,
cambiando de estado, en este mismo estado se
recorren todas las “as” a la derecha y cuando se llega
a un blanco se reemplaza por el mismo blanco se
deja la cabecera a la izquierda y se reemplaza la “a”
por un blanco para restarle la que adiciono y se
mueve hacia la derecha y se cambia al estado final
“q3”.

M=(Q,Σ,Γ,q_0,q_3,B,δ)

Donde la función se define así:


Unidad 5: Análisis léxico.

5.1 Funciones del analizador léxico.


Un analizador léxico aísla el analizador sintáctico de la
representación de lexemas de los componentes
léxicos.

El analizador léxico opera bajo petición del analizador


sintáctico devolviendo un componente léxico conforme
el analizador sintáctico lo va necesitando para avanzar
en la gramática.Los componentes léxicos son los
símbolos terminales de la gramática.
Suele implementarse como una subrutina del
analizador sintáctico. Cuando recibe la orden obtén el
siguiente componente léxico, el analizador léxico lee
los caracteres de entrada hasta identificar el siguiente
componente léxico.

Definiciones.

Tokens:
 Símbolos terminales de una gramática
o Identificadores, palabras reservadas,
operadores,...
o Varios signos pueden forman el mismo
token
Atributos:

Información adicional que tiene el token, de utilidad


para el análisis sintáctico y semántico.

Componentes léxicos (tokens):

unidad mínima de información que significa algo a la


hora de compilar; concepto de palabra; las fases de un
lenguaje constan de cadenas de componentes léxicos.

Lexema:

Una secuencia de caracteres de entrada que


comprenden un solo componente léxico se llama
lexema; cadena de caracteres que extrae el
componente abstracto del componente léxico.

Patrón:

Descripción de la forma que han de tomarlos lexemas


para ajustarse a un componente léxico.

Ejemplo:

Otras Funciones:
 Manejo del fichero de entrada del programa
fuente: abrirlo, leer sus caracteres, cerrarlo y
gestionar posibles errores de lectura.
 Eliminar comentarios, espacios en blanco,
tabuladores y saltos de línea (caracteres no
validos para formar un token).
 Inclusión de ficheros: # include...
 La expansión de macros y funciones in line: #
define...
 Contabilizar el número de líneas y columnas para
emitir mensajes de error.
 Reconocimiento y ejecución de las directivas de
compilación (por ejemplo, para depurar u
optimizar el código fuente).
Aspectos del Análisis Léxico.

Diseño más sencillo:


 Los símbolos que trata el scanner se describe con
una gramática más simple que la del parser,
gramática regular
Mejora la eficiencia:
 Gran parte del tiempo de compilación se consume
en la lectura y exploración de caracteres
Mejora la portabilidad:
 Se pueden tener varias versiones del scanner una
para distintos códigos (EBCDID, ASCII, ...), con el
mismo parser
Descarga el análisis sintáctico:
Ejemplo; no puedo distinguir en FORTRAN hasta
después del 1
o DO 5 I=1.25
o DO 5 I=1,25
5.2 Componentes léxicos, patrones y lexemas.
Componente léxico (token).

Son las unidades lógicas que genera el analizador


léxico. Formar caracteres en tokens es muy parecido a
formar palabras en un lenguaje natural

Es el conjunto de cadenas de entrada que produce


como salida el mismo componente
léxico. Cada token es una secuencia de caracteres que
representa una unidad de información en el programa
fuente. Los componentes léxicos más comunes son los
siguientes: palabras clave o reservadas:

 operadores aritméticos
 operadores
 relacionales
 operadores lógicos
 operador de asignación
 identificadores
 constantes
 cadenas
 literales
 signos de puntuación
 librerías

Lexema:

Representan cadenas de caracteres en el programa


fuente que se pueden tratar juntos como una unidad
léxica. Un lexema es una secuencia de caracteres en el
programa fuente con la que concuerda el patrón para un
componente léxico.

Patrón:

Regla que describe el conjunto de lexemas que pueden


representar a un determinado componente léxico en los
programas fuente. En otras palabras, es la descripción
del componente léxico mediante una regla.

Atributos de los componentes léxicos:

El analizador léxico recoge información sobre los


componentes léxicos en sus atributos asociados. Los
componentes léxicos influyen en las decisiones del
análisis sintáctico y los atributos en la traducción de los
componentes léxicos:
 Apuntador a la entrada de la Tabla de símbolos
donde se guarda la información sobre el
componente léxico.
 El lexema para un identificador
 El número de línea en que se encontró por primera
vez.
5.3 Creación de Tabla de tokens.
Tabla: conjunto de pares clave-valor, llamados
elementos de la tabla. La tabla de símbolos es una
componente necesaria de un compilador. Al declarar un
identificador (normalmente una sola vez), éste es
insertado en la tabla. Cada vez que se utilice el
identificador se realizará una búsqueda en la tabla para
obtener la información asociada (el valor).
 Búsqueda: dada la clave de un elemento, encontrar
su valor.
 Inserción: Dado un par clave-valor, añadir un
elemento nuevo a la tabla.
 Cambio de valor: Buscar el elemento y cambiar su
valor.
 Borrado: Eliminar un elemento de la tabla.
 Longitud de búsqueda (o tiempo de acceso)
De una clave: Li = número de comparaciones con
elementos de la tabla para encontrar esa clave.
Máxima: LM = número máximo de comparaciones para
encontrar cualquier clave. Media (esperada): Lm =
número medio de comparaciones para encontrar un
valor. Si la frecuencia de todas las claves es la misma:
Lm = (S Li)/N
Si la frecuencia de todas las claves no es la misma:
Lm = S pi.Li
Grado de ocupación:
s = n/N donde n=número de elementos en la tabla y
N=capacidad máxima de la tabla.

Función de búsqueda: B : K→E asocia a cada clave k


un elemento B(k).

Valor asociado a una clave k: v(B(k)). Puede ser


múltiple, en cuyo caso normalmente se convierte en un
puntero. Si está en la tabla puede almacenarse
consecutivamente o en subtablas paralelas. Tablas de
símbolos (identificadores) La clave es el identificador. El
valor está formado por:

Atributos del identificador. Puntero a la posición de


memoria asignada. La clave puede sustituirse por un
puntero. Los identificadores pueden estar
empaquetados. La longitud del identificador puede
especificarse en la tabla o delante del nombre, o ser
implícita.

Tablas consecutivas: Todos los elementos ocupan


posiciones de memoria adyacentes. Tablas ligadas:
cada elemento apunta al siguiente. Tablas doblemente
ligadas: cada elemento apunta al siguiente y al anterior.
Tablas no ordenadas Inserción: en el primer lugar
vacío.

5.4 Errores léxicos

El análisis léxico constituye la primera fase, aquí se lee


el programa fuente de izquierda a derecha y se agrupa
en componentes léxicos (tokens), que son secuencias
de caracteres que tienen un significado. Además, todos
los espacios en blanco, líneas en blanco, comentarios y
demás información innecesaria se elimina del programa
fuente. También se comprueba que los símbolos del
lenguaje (palabras clave, operadores,...) se han escrito
correctamente.

Como la tarea que realiza el analizador léxico es un


caso especial de coincidencia de patrones, se necesitan
los métodos de especificación y reconocimiento de
patrones, y estos métodos son principalmente las
expresiones regulares y los autómatas finitos. Sin
embargo, un analizador léxico también es la parte del
traductor que maneja la entrada del código fuente, y
puesto que esta entrada a menudo involucra un
importante gasto de tiempo, el analizador léxico debe
funcionar de manera tan eficiente como sea posible.

Son pocos los errores simplemente en el nivel léxico ya


que tiene una visión muy restringida de un programa
fuente. El analizador léxico debe devolver el
componente léxico de un identificador y dejar a otra fase
se ocupe de los errores.
Suponga que una situación en la cual el analizador
léxico no puede continuar porque ninguno de los
patrones concuerda con un prefijo de la entrada. Tal vez
la estrategia de recuperación más sencilla sea
recuperación “EN MODO PANICO” (este método de
recuperación es donde se borra caracteres sucesivos
de la entrada hasta que el analizador léxico pueda
encontrar un componente léxico bien formado). ¡¡Los
programas no siempre son correctos!!

El compilador tiene que:


1. Reportar clara y exactamente la presencia de
errores
2. Recuperarse de cada error lo suficientemente
rápido para poder detectar errores subsiguientes:
Tratar de evitar mensajes falsos de error
Un error que produce un token erróneo
Errores léxicos posibles

Un token o componente léxico es una cadena de


caracteres que tiene un significado coherente en cierto
lenguaje de programación. Ejemplos de tokens, podrían
ser palabras clave (if, while, int), identificadores,
números, signos, o un operador de varios caracteres.
Son los elementos más básicos sobre los cuales se
desarrolla toda traducción de un programa, surgen en la
primera fase, llamada análisis léxico.

5.5 Generadores de analizadores Léxicos


Se pueden usar varias técnicas para acelerar el
algoritmo y ahorrar espacio
Las etiquetas pueden guardarse mediante dispersión
para producir un sistema de firmas que acelere sus
búsquedas
Como las tablas de transiciones son muy escasas, se
pueden guardar en una lista corta que se consulte cada
vez que se necesite hacer una transición a partir de un
estado

Estas listas no suelen tener más de tres o cuatro


elementos, así que su búsqueda será razonablemente
rápida

5.6 Aplicaciones (Caso de estudio)


Unidad 6: Análisis Sintáctico.
6.1 GLC.
Gramáticas Libres de Contexto (GLC), o de tipo 2: las
reglas son de la forma X → α, donde X es una variable
y α es una cadena que puede contener variables y
constantes. Estas gramáticas producen los lenguajes
Libres de Contexto (abreviado “LLC”)
 Capturan la noción de constituyente sintáctico y la
noción de orden.
 Herramienta formal que puede ser vista tanto
desde un punto de vista generador como
estructurador.
 Propiedades computacionales interesantes: se
puede reconocer en tiempo polinómico.
Una Gramática Libre de Contexto es una tupla con 4
parámetros:
 G = (V, T, P, S)
 V – conjunto de símbolos variables
 T – conjunto de símbolos terminales
 S Є V, símbolo inicial
 P – conjunto de reglas de producción: A → α, con
α sucesión de símbolos de V U T, eventualmente
vacía (α = ε)
Una GLC es un dispositivo generador.
Definimos el lenguaje LG generado por una gramática
G del siguiente modo: G = { w / S →* w } , siendo ⇒*
una “especie” de clausura transitiva de → y w una tira
de terminales
6.2 Árbol de derivación.
Es una representación gráfica (en forma de árbol
invertido) de un proceso de derivación en una
gramática. Se define el árbol de derivación como sigue:
 la raíz del árbol será el símbolo inicial de la
gramática
 los nodo interiores del árbol están etiquetados por
los símbolos no terminales
 las hojas están etiquetadas por símbolos
terminales
 si un nodo interior etiquetado por A, posee como
hijos los nodos etiquetados por X1,X2, …Xn ,
entonces A→ X1,X2, …Xn es una producción de la
gramática, en donde Xi , representa símbolo
terminal o no terminal.
Sea la siguiente gramática:
G=( Σ={a, b}, N={S,A,B},S P ) P: S→aABAa , A→ε |aA ,
B→ε|bB la construcción de un árbol de derivación en el
proceso de la generación de la palabra aa es el
siguiente:
Propiedades de un árbol de derivación.

Sea G = (N, T, S, P) una gramática libre de contexto,


sea A Є N una variable. Diremos que un árbol TA= (N,
E) etiquetado es un árbol de derivación asociado a G si
verifica las propiedades siguientes:
 La raíz del árbol es un símbolo no terminal
 Cada hoja corresponde a un símbolo terminal o λ.
 Cada nodo interior corresponde a un símbolo no
terminal.
Para cada cadena del lenguaje generado por una
gramática es posible construir (al menos) un árbol de
derivación, en el cual cada hoja tiene como rótulo uno
de los símbolos de la cadena.
Árbol de derivación.

Ejemplo:

Sea G=(N, T, S, P) una GLC con P: S→ ab|aSb

La derivación de la cadena aaabbb será: S → aSb →


aaSbb → aaabbb y el árbol de derivación:

6.3 Formas normales de Chomsky.

Una gramática formal está en Forma normal de


Chomsky si todas sus reglas de producción son de
alguna de las siguientes formas:

A → BC o
A → a o

donde A, B y C son símbolos no terminales (o variables)


y α es un símbolo terminal.

Todo lenguaje independiente del contexto que no posee


a la cadena vacía, es expresable por medio de una
gramática en forma normal de Chomsky (GFNCH) y
recíprocamente. Además, dada una gramática
independiente del contexto, es posible
algorítmicamente producir una GFNCH equivalente, es
decir, que genera el mismo lenguaje.

Sea G = (∑ N, ∑T, P, $) una gramática con P ⊂ ∑N X


(∑N U ∑T)* y X Є ∑N un símbolo no-terminal (o una
variable). Podemos clasificar tales símbolos X en tres
clases:
Variables accesibles:
 Si existe una derivación desde el símbolo inicial
que contiene X, es decir, existe $ → * α Xβ donde
α, β Є∑*
Variables generativas:
 Si existe una derivación desde el la variable que
produce una sentencia , es decir, existe X →* ω
donde ω Є *T.
Variables útiles:
 Si existe una derivación desde el símbolo inicial
usando que produce una sentencia ω, es decir,
existe $ →* α X β →*ω donde α, β Є ∑* y ω Є ∑*T.
6.4 Diagramas de sintaxis

Los diagramas sintácticos, de sintaxis o diagramas del


ferrocarril son una forma de representar una gramática
libre de contexto. Representan una alternativa gráfica
para la Forma de Backus-Naur (BNF, por sus siglas en
inglés) o la Forma Extendida de Backus-Naur (EBNF,
por sus siglas en ingles).

Los diagramas de ferrocarril son más comprensibles


para la mayoría de la gente. Alguna parte de la
popularidad del formato de intercambio de datos JSON
se debe a su representación en los diagramas de
ferrocarril.

Un segundo método alternativo para desplegar las


producciones de ciertas gramáticas de tipo 2 es el
diagrama de sintaxis. Ésta es una imagen de las
producciones que permite al usuario ver las
sustituciones en forma dinámica, es decir, verlas como
un movimiento a través del diagrama. En la figura 10.5
se ilustrará los diagramas que resultan de la traducción
de conjuntos de producciones típicos, que son, por lo
general, todas las producciones que aparecen en el
lado derecho de algún enunciado BNF.
6.5 Eliminación de la ambigüedad

Una GLC es ambigua si existe una cadena w Є L(G) que


tiene más de una derivación por la izquierda o más de
una derivación por la derecha o si tiene dos o más
arboles de derivación .

En casi de y que toda cadena w Є L (G) tenga un único


árbol de derivación no es ambigua.

Ejemplo: La gramática S → aS| Sa | a es ambigua


porque aa tiene dos derivaciones por la izquierda S Þ
aS Þ aa S Þ Sa Þ aa.

Tipos de Ambigüedad

Dentro del estudio de gramáticas existen dos tipos


fundamentales de ambigüedad, los cuales son:

Ambigüedad Inherente:
Las gramáticas que presentan este tipo de ambigüedad
no pueden utilizarse para lenguajes de programación,
ya que por más transformaciones que se realicen sobre
ellas, nunca se podrá eliminar completamente la
ambigüedad que presentan:

Un lenguaje L es inherentemente ambiguo si todas sus


gramáticas; si existe cuando menos una gramática no
ambigua para L, L no es ambiguo.
 El lenguaje de las expresiones no es Ambiguo
 Las expresiones regulares no son ambiguas
Ejemplo de un lenguaje inherentemente ambiguo:

La gramática es ambigua: hay cadenas con más de una


derivación más izquierda:

6.6 Generación de matriz predictiva (cálculo first y


follow)

FIRST: Sea G := (V; ∑; Q0; P) una gramática libre de


contexto. Para cada forma sentencial α Є (V U ∑)* y
para cada k Є N definiremos la función.

En otras palabras, el operador F IRST k asocia a cada


forma sentencial los primeros k símbolos de cualquier
forma terminal alcanzable desde α mediante
derivaciones “masa la izquierda".

FOLLOW: Con las mismas notaciones anteriores, para


cada forma sentencial α Є (V U ∑)* definiremos la
función FOLLOWG GK (α) del modo siguiente.

De nuevo nos ocuparemos solamente de FOLLOW: =


FOLLOW1. Obsérvese que FOLLOW k (α) ⊂ ∑* y que
para cada x Є FOLLOW (α), Ixl ≤ k. Obsérvese que para
cada variable A Є V, FOLLOW(A) son todos los
símbolos terminales que pueden aparecer a la derecha
de A en alguna forma sentencial de la gramática.

6.7 Tipos de analizadores sintácticos

Analizador Descendente:

Se construye el árbol de análisis sintético partiendo del


símbolo inicial y aplicando las producciones mediante
derivaciones por la izquierda, el símbolo a expandir es
el que esta mas a la izquierda.

Analizador Ascendente:

Se construye el árbol de análisis sintético partiendo de


la frase a reconocer y aplicando las producciones
mediante reducciones hasta llegar al símbolo inicial de
la gramática.
Ejemplo:

G= ({+,*, ID, (,)}, {E, T, P},E, P)P={E:=E+T | T; T:=T*P |


P; P:= ID | (E) }FraseID + ( ID * ID )

Ejemplo:

G= ({+,*, ID, (,)}, {E, T, P},E, P)P={E:=E+T | T; T:=T*P |


P; P:= ID | (E) }FraseID + ( ID * ID )
6.8 Manejo de errores.

Un compilador es un sistema que en la mayoría de los


casos tiene que manejar una entrada incorrecta. Sobre
todo en las primeras etapas de la creación de un
programa, es probable que el compilador se utiliza para
efectuar las características que debería proporcionar un
buen sistema de edición dirigido por la sintaxis, es decir,
para determinar si las variables han sido declaradas
antes de usarla, o si faltan corchetes o algo así.

Por lo tanto, el manejo de errores es parte importante


de un compilador y el escritor del compilador siempre
debe tener esto presente durante su diseño.

Hay que señalar que los posibles errores ya deben estar


considerados al diseñar un lenguaje de programación.
Por ejemplo, considerar si cada proposición del
lenguaje de programación comienza con una palabra
clave diferente (excepto la proposición de asignación,
por supuesto). Sin embargo, es indispensable lo
siguiente:

El compilador debe ser capaz de detectar errores en la


entrada;
 El compilador debe recuperarse de los errores sin
perder demasiada información;
 Y sobre todo, el compilador debe producir un
mensaje de error que permita al programador
encontrar y corregir fácilmente los elementos
(sintácticamente) incorrectos de su programa.
Errores Sintácticos.

Muchos errores de naturaleza sintáctica Recuperación:


Al producirse un error el compilador debe ser capaz de
informar del error y seguir compilando. (Ideal).

El manejo de errores de sintaxis es el más complicado


desde el punto de vista de la creación de compiladores.
Nos interesa que cuando el compilador encuentre un
error, se recupere y siga buscando errores. Por lo tanto
el manejador de errores de un analizador sintáctico
debe tener como objetivos:
 Indicar los errores de forma clara y precisa. Aclarar
el tipo de error y su localización.
 Recuperarse del error, para poder seguir
examinando la entrada.
 No ralentizar significativamente la compilación.
Un buen compilador debe hacerse siempre teniendo
también en mente los errores que se pueden producir;
con ello se consigue:
 Simplificar la estructura del compilador.
 Mejorar la respuesta ante los errores.
Errores semánticos.
Un lenguaje con comprobación fuerte de tipos es capaz
de garantizar que los programas se pueden ejecutar sin
errores de tipo, por lo que los errores de tipo se
detectarán siempre en tiempo de compilación.
Como mínimo, ante un error, un comprobador de tipos
debe informar de la naturaleza y posición del error y
recuperarse para continuar con la comprobación del
resto del programa a analizar.
Veamos algunas de las operaciones a tener en cuenta
en una comprobación de tipos:
 Conversión de tipos: A veces es necesario
transformar el tipo de una expresión para utilizar
correctamente un operador o para pasar de forma
adecuada un parámetro a una función.
 Coerción: Es una conversión de tipos que realiza
de forma implícita el propio compilador. Si es el
programador el que realiza la conversión se tratará
entonces de una conversión explícita.
 Sobrecarga de operadores: La sobrecarga se
resuelve determinando el tipo de cada una de las
expresiones intervinientes en la sobrecarga.
 Funciones polimórficas: Son aquellas que trabajan
con argumentos cuyo tipo puede cambiaren
distintas llamadas a la función.
6.9 Generadores de analizadores sintácticos

ANTLR:
(ANother Tool for Language Recognition; en español
"otra herramienta para reconocimiento de lenguajes")
es una herramienta creada principalmente por Terence
Parr, que opera sobre lenguajes, proporcionando un
marco para construir reconocedores (parsers),
intérpretes, compiladores y traductores de lenguajes a
partir de las descripciones gramaticales de los mismos
(conteniendo acciones semánticas a realizarse en
varios lenguajes de programación).
GNU bison:
Es un programa generador de analizadores sintácticos
de propósito general perteneciente al proyecto GNU
disponible para prácticamente todos los sistemas
operativos, se usa normalmente acompañado de flex
aunque los analizadores léxicos se pueden también
obtener de otras formas.

Grammatica:
Es un generador de analizadores sintácticos de C# y
Java libre. Es similar a otras herramientas como Yacc o
ANTLR. Grammatica soporta el algoritmo LL(k) para
gramáticas con un número ilimitado de tokens de
anticipación. Está bastante bien probado, y ha sido auto
compilado desde la versión 0.1. La documentación
contiene una lista completa de características, así como
una comparación con otros generadores de
analizadores.

JavaCC:
(Java Compiler Compiler) es un generador de
analizadores sintácticos de código abierto para el
lenguaje de programación Java. JavaCC es similar a
Yacc en que genera un parser para una gramática
presentada en notación BNF, con la diferencia de que
la salida es en código Java. A diferencia de Yacc,
JavaCC genera analizadores descendentes (top-down),
lo que lo limita a la clase de gramáticas LL (K) (en
particular, la recursión desde izquierda no se puede
usar). El constructor de árboles que lo acompaña,
JJTree, construye árboles de abajo hacia arriba
(bottom-up).

Yacc:
Es un programa para generar analizadores sintácticos.
Las siglas del nombre significan Yet Another Compiler-
Compiler, es decir, "Otro generador de compiladores
más". Genera un analizador sintáctico (la parte de un
compilador que comprueba que la estructura del código
fuente se ajusta a la especificación sintáctica del
lenguaje) basado en una gramática analíticaescrita en
una notación similar a la BNF. Yacc genera el código
para el analizador sintáctico en el Lenguaje de
programación C.

También podría gustarte