Está en la página 1de 70

Compiladores e

Interpretes
Lenguajes regulares y expresiones regulares
UNET 2012
Repaso Cosas importantes para las
Regex (Expresiones Regulares)
Dado un alfabeto , definimos:
• k = {x | x es una palabra sobre  y |x| = k}
Ejemplo:  = {0, 1}
• 0 ={}
• 1 ={0, 1}
• 2 ={00, 01, 10, 11}
• 3 ={000, 001, 010, 011, 100, 101, 110, 111}
• ...

• * = 0  1  2  3 ... es el conjunto de todas las palabras que pueden


ser formadas con letras del alfabeto , incluyendo a . A * se le llama la
Cerradura de Kleene de .

• + = 1  2  3 ... es el conjunto de todas las palabras no-vacías que


pueden ser formadas con letras de , es decir,
+ = *  {}.
Repaso Cosas importantes para las
Regex (Expresiones Regulares)
Concatenación de lenguajes
• L1L2 = {w | w = xy, x  L1 y y  L2}
• Para un lenguaje L:
• L0 = {}
• L1 = L
• L2 = L L
• L3 = L L L
• ...

• L* = L0  L1  L2  L3 ... (Cerradura de Kleene)

• L+ = L1  L2  L3 ... = LL*
Repaso Cosas importantes para las
Regex (Expresiones Regulares)
Ejemplo de concatenación

• X = {a, b, c}; Y = {abb, ba}


• XY ={aabb, aba, babb, bba, cabb, cba}
• X0 = {}
• X1 = {a, b, c}
• X2 = XX = {aa, ab, ac, ba, bb, bc, ca, cb, cc}
• X3 = X2X = {aaa, aab, aac, aba, abb, abc, aca, acb, acc, baa, bab, bac, bba,
bbb, bbc, bca, bcb, bcc, caa, cab, cac, cba, cbb, cbc, cca, ccb, ccc}
Repaso Cosas importantes para las
Regex (Expresiones Regulares)
Ejemplo Cerradura de Kleene

• L = {0, 11}
• L0 = {}
• L1 = {0, 11}
• L2 = {00, 011, 110, 1111}
• L3 = {000, 0011, 0110, 01111, 1100, 11011, 11110, 111111}
• L4 = {0000, 00011, 00110, 001111, 01100, 011011, 011110, 0111111,
11000, 110011, 110110, 1101111, 111100, 1111011, 1111110, 11111111}
• L* son todas las que se pueden formar concatenando cualquier número de
veces (excepto ) palabras de L. Las palabras pueden ser iguales o
distintas.
Repaso Cosas importantes para las
Regex (Expresiones Regulares)
Ejemplos Varios
• L = {a, b}*{bb} {a, b}*
• Consiste de las cadenas sobre {a, b} que contienen la subcadena bb.
• Lenguaje que consiste de todas las cadenas que empiezan con
aa o terminan con bb.
• L = {aa}{a, b}*  {a, b}*{bb}
• L1 = {bb} y L2 = {, bb, bbbb}. L1* = ¿?, L2* = ¿?
• Tanto L1* como L2* consisten de cadenas que tienen un número par de b’s.
• {aa, bb, ab, ba}*
• Consiste de todas las cadenas sobre {a, b} de longitud par.
Repaso Cosas importantes para las
Regex (Expresiones Regulares)
En lingüística la jerarquía de Chomsky es una clasificación jerárquica de distintos
tipos de gramáticas formales que generan lenguajes formales.

• Gramáticas de tipo 0 (sin restricciones), que incluye a todas las gramáticas formales.
Estas gramáticas generan todos los lenguajes capaces de ser reconocidos por una
máquina de Turing. Los lenguajes son conocidos como lenguajes recursivamente
enumerables. Nótese que esta categoría es diferente de la de los lenguajes
recursivos, cuya decisión puede ser realizada por una máquina de Turing que se
detenga.
• Gramáticas de tipo 1 (gramáticas sensibles al contexto) generan los lenguajes
sensibles al contexto. Estas gramáticas tienen reglas de la forma con A un no
terminal y α, β y γ cadenas de terminales y no terminales. Las cadenas α y β pueden
ser vacías, pero γ no puede serlo. La regla está permitida si S no aparece en la parte
derecha de ninguna regla. Los lenguajes descritos por estas gramáticas son
exactamente todos aquellos lenguajes reconocidos por una máquina de Turing no
determinista cuya cinta de memoria está acotada por un cierto número entero de
veces sobre la longitud de entrada.
Repaso Cosas importantes para las
Regex (Expresiones Regulares)
• Gramáticas de tipo 2 (gramáticas libres del contexto) generan los lenguajes
independientes del contexto. Las reglas son de la forma con A un no terminal y γ
una cadena de terminales y no terminales. Estos lenguajes son aquellos que
pueden ser reconocidos por un autómata con pila.
• Gramáticas de tipo 3 (gramáticas regulares) generan los lenguajes regulares. Estas
gramáticas se restringen a aquellas reglas que tienen en la parte izquierda un no
terminal, y en la parte derecha un solo terminal, posiblemente seguido de un no
terminal. La regla también está permitida si S no aparece en la parte derecha de
ninguna regla. Estos lenguajes son aquellos que pueden ser aceptados por un
autómata finito. También esta familia de lenguajes pueden ser obtenidas por
medio de expresiones regulares.
Expresiones Regulares desde el punto
de vista matemático
Son expresiones que permiten describir lenguajes regulares de manera simple,
sencilla y precisa.
En otras palabras permiten abreviar la descripción de conjuntos regulares.

• El conjunto regular {a} es representado por a.

•  y  son expresiones regulares.


• Cada símbolo de∑ es una expresión regular.

• Si e1 y e2 son expresiones regulares entonces:


• ( e1+ e2 ) es una expresión regular.
• ( e1 . e2 ) es una expresión regular.

• Si e es una expresión regular, entonces e* es una expresión regular.

• Ninguna otra es una expresión regular.


Definición Recursiva de las
Expresiones Regulares
• Sea  un alfabeto. Las expresiones regulares sobre  se
definen recursivamente de la siguiente manera:

• Base:  (Lenguaje que no contiene ninguna palabra, lenguaje vacio),  (Lenguaje que contiene la palabra vacía) y a,
para toda a  , son expresiones regulares sobre . (Importante: no se debe
confundir a  con  porque por ejemplo, si L{}={}L=L, mientras que L= L= )

• Paso recursivo: Si u y v son expresiones regulares sobre , entonces


las expresiones (u+v), (uv) y (u*) también lo son y representan a los
conjuntos {u}  {v}, {u}{v} y {u}*, respectivamente.

• Cerradura: u es una expresión regular sobre  sólo si puede ser


obtenido a partir de los elementos base mediante un número finito
de aplicaciones del paso recursivo.
Ejemplos Expresiones Regulares
Lenguaje Expresión Regular
{} 
{0} 0
{001} = {0}{0}{1} 001
{0, 1} = {0}{1} 0+1
{0, 10} = {0}{10} 0 + 10
{1, }{001} (1 + )001
{110}*{0, 1} (110)*(0 + 1)
{1}*{10} 1*10
{10, 111, 11010}* (10 + 111 + 11010)*
{0, 10}*({11}*{001, }) (0 + 10)*((11)* + 001 + )
(00 + 01 + 10 + 11)* ((0 + 1)(0 + 1))*
Convenciones Expresiones Regulares
• Tomando en cuenta que la unión y la concatenación son
asociativas, además conviniendo en que la precedencia u
orden de ejecución de las operaciones está dada por:
• Paréntesis
• Cerradura de Kleene
• Concatenación
• Unión

Igualmente las expresiones se pueden simplificar aún más


reduciendo el número de paréntesis:
• {a, b}*{bb}{a, b}* = (a + b)*bb(a + b)*
• {a}{a, b}*{b}{a, b}*{a} = a(a + b)*b(a + b)*a

• Notación
• u+ = uu*
• u2 = uu, u3 = u2u, ...
Ejemplo
 El conjunto {bawab | w  {a, b}*} es regular sobre {a, b}

Demostración:
Conjunto Expresión Justificación
1. {a} a Base
2. {b} b Base
3. {a}{b}={ab} ab Concatenación de 1 y 2
4. {a}  {b}={a,b} a+b Unión de 1 y 2
5. {b}{a}={ba} ba Concatenación de 2 y 1
6. {a,b}* (a+b)* Cerradura Kleene de 4
7. {ba}{a,b}* ba(a+b)* Concatenación de 5 y 6
8. {ba}{a,b}*{ab} ba(a+b)*ab Concatenación de 7 y 3
Lenguajes Regulares

• Definición: Un lenguaje es regular si


se puede representar por una
expresión regular o conjunto regular.
Más Ejemplos

• (a+b)*aa(a+b)*+(a+b)*bb(a+b)*:
• Representa al conjunto de cadenas sobre {a, b} que contienen a la
subcadena aa o a la subcadena bb o a ambas subcadenas.

• Expresión regular que represente al conjunto de cadenas sobre


{a, b} que contienen exactamente dos b’s:
• a*ba*ba*

• a*ba*b(a+b)*, (a+b)*ba*ba*, (a+b)*b(a+b)*b(a+b)*


• representan al conjunto de cadenas sobre {a, b} que contienen dos o más
b’s.
Más Ejemplos
• a*(a*ba*ba*)* y a*(ba*ba*)*
• Representan cadenas con un número par de b’s.

• Expresión regular para el lenguaje sobre {a, b} en cuyas palabras


inmediatamente antes de toda b aparece una a:
• (a+ab)*.

• Expresión regular que representa a las palabras que contienen


exactamente una vez dos b’s contiguas:
• (ba+bc+a+c)*bb(a+c+ab+cb)*
Ejercicio
•Escriba una expresión regular
para el lenguaje sobre {0 ,1}
que consiste de las palabras en
las que no hay dos símbolos
iguales contiguos, es decir, los
0’s y los 1’s se alternan.
Posible Solución al Ejercicio

• (01)* + (10)* + 0(10)* + 1(01)*


• ( + 1)(01)*( + 0)
• ( + 0)(10)*( + 1)
Equivalencia en las expresiones
regulares
• Una expresión regular define un patrón; una palabra
pertenece al lenguaje definido por esa expresión regular si y
sólo si sigue el patrón.

• Una expresión regular que represente un lenguaje debe


cumplir dos condiciones:
• Correcta: todas las palabras representadas por la expresión regular
deben ser parte del lenguaje.
• Completa: toda palabra del lenguaje debe ser representada por la
expresión regular.

• Concatenación indica orden de los símbolos, la cerradura de


Kleene permite repeticiones y + indica selección.

• Dos expresiones que representan al mismo conjunto son


llamadas equivalentes.
Identidades en expresiones regulares
• u = u = 
• u = u = u
• * = 
• * = 
• u+v = v+u
• u+ = u
• u+u = u
• u* = u* u* = (u*)*
• u(v+w) = uv + uw
• (u+v)w = uw+vw
• (uv)*u = u(vu)*
• (u+v)* = (u*+v)* = u*(u+v)* = (u+vu*)* = (u*v*)* = u*(vu*)* = (u*v)*u*
• u*(u + ) = u*
• u*u* = u*
• u* + v* = v* + u*
• (u*v*)* = (u + v)* = (u + v)*uv(u + v)* + v*u*
Ejemplos
• Expresión que representa las cadenas sobre {a, b} que no contienen la subcadena aa:
• b*(ab+)*+b*(ab+)*a =
b*(ab+)*(+a) =
b*(abb*)*(+a) =
(b+ab)*(+a)

• Expresión regular que representa las cadenas sobre {a, b, c} que contienen la
subcadena bc:
• (a+b+c)*bc(a+b+c)*

• c*(b+ac*)*
• Representa las cadenas que no contienen la subcadena bc.

• Cadenas sobre {0, 1} de longitud igual a 6:


• (0 + 1)(0 + 1)(0 + 1)(0 + 1)(0 + 1)(0 + 1) = (0 + 1)6

• Cadenas sobre {0, 1} de longitud mayor o igual a 6:


• (0 + 1)(0 + 1)(0 + 1)(0 + 1)(0 + 1)(0 + 1)(0 + 1)* = (0 + 1)6(0 + 1)*

• Cadenas sobre {0, 1} de longitud menor o igual a 6:


• (0 + 1 + )(0 + 1 + )(0 + 1 + )(0 + 1 + )(0 + 1 + )(0 + 1 + )
= (0 + 1 + )6
Ejercicios
• Obtenga una expresión regular para el
conjunto de palabras sobre {a, b, c} que
tienen al menos una a y al menos una b.

• Obtenga una expresión regular para el


lenguaje sobre {0 , 1} que consiste de las
palabras cuyo décimo símbolo contado de
la derecha a la izquierda es un 1.
Posibles soluciones a los
ejercicios

 c*a(a + c)*b(a + b + c)* + c*b(b + c)*a(a + b + c)*

 (0+1)*1(0+1) (0+1) (0+1) (0+1) (0+1) (0+1) (0+1) (0+1) (0+1)


= (0 + 1)*1(0 + 1)9
Pregunta

¿Hay lenguajes para los que no existe una


expresión regular que los represente?
Respuesta
Construcción intuitiva del autómata
finito de una expresión regular
Si tenemos en cuenta que:
1. El lenguaje  es reconocido por el autómata:

2. El lenguaje {} es reconocido por el autómata:

3. El lenguaje {a} es reconocido por el autómata:


Construcción intuitiva del autómata
finito de una expresión regular
• Tomando en cuenta los procedimientos de construir los
autómatas de unión, concatenación y cierre de Kleene en
lenguaje regulares, podemos construir el autómata que reconoce
el lenguaje de una expresión regular, a partir de los autómatas
mostrados en la diapositiva anterior.

• Por ejemplo:
• Dada la expresión regular b*(a+b)a, construir el autómata que
reconozca el lenguaje descrito por esta expresión regular.
Construcción intuitiva del autómata
finito de una expresión regular
1. El autómata que reconoce b es el siguiente:

2. A partir de este hallamos el que reconoce b*:


Construcción del autómata finito de
una expresión regular
3. El autómata que reconoce a + b a partir de los que conocen a y b
respectivamente, es el siguiente:

4. A partir de los anteriores se encuentra el que reconoce b*(a+b):


Construcción del autómata finito de
una expresión regular
5. A partir del que reconoce a y del que reconoce b*(a+b) se
encontraría el que reconoce b*(a+b)a:

6. Aunque ya esta listo el autómata, el ultimo paso que deberíamos


llevar a cabo es la determinación (DFA) y minimización del
resultado obtenido.
Desde las expresiones regulares hasta
el analizador léxico
Normalmente durante la construcción de un analizador
léxico, el primer método usado para reconocer los patrones de
los testigos de un lenguaje son las expresiones regulares, es por
esto que la generalización del analizador léxico comienza por
estas y continua luego hacia la construcción de un NFA para las
mismas, que luego es convertido en un DFA y por ultimo se
construye con este un programa analizador léxico final. Por esta
razón es de nuestro interés estudiar las conversiones antes
mencionadas:

Expresión
NFA DFA programa
regular
Desde una expresión regular hasta un
NFA
Para esta conversión estudiaremos la construcción de
Thompson, que utiliza transiciones Ɛ, para “pegar” las maquinas de
cada segmento de una expresión regular con el fin de formar una
máquina que corresponda a la expresión completa. (estado aceptador con doble
circulo en esta notación)

Expresiones regulares básicas:


Una expresión regular básica como un carácter, Ɛ, ɸ tiene
como NFA equivalente a:
Desde una expresión regular hasta un
NFA
(estado aceptador con doble circulo en esta notación)

Concatenación:
Si deseamos construir un NFA para la expresión regular rs,
donde r y s son expresiones regulares. Primero suponemos que
tenemos los NFA equivalentes de r y s, al escribir (ej. solo para r):

Y con estos construimos el NFA equivalente, al conectar el


estado de aceptación de la maquina r, con el inicial de la
maquina s, mediante una transición Ɛ.
Desde una expresión regular hasta un
NFA
(estado aceptador con doble circulo en esta notación)

Selección entre alternativas:


Si deseamos construir un NFA para la expresión regular
r|s, donde r y s son expresiones regulares. Agregamos un nuevo
estado de inicio y un nuevo estado de aceptación y los
conectamos como se muestra utilizando transiciones Ɛ.
Desde una expresión regular hasta un
NFA
(estado aceptador con doble circulo en esta notación)

Repetición:
Si deseamos construir un NFA para la expresión regular r*,
donde r es una expresión regular. Agregamos un nuevo estado de
inicio y un nuevo estado de aceptación y mediante una transición
Ɛ proporcionamos la repetición de esta máquina:
Desde una expresión regular hasta un
NFA
(estado aceptador con doble circulo en esta notación)

Ejemplo:
Traduciremos la expresión regular ab|b en un NFA de acuerdo con la
construcción de Thompson:

Entonces formamos la maquina ab:

Ahora formamos la maquina ab|b, al unir una copia de a con ab para la


selección:
Expresiones regulares para
analizadores léxicos
• Una expresión regular r se encuentra definida por el conjunto de
cadenas con las que concuerda, denominándose este conjunto el
lenguaje generado por la expresión regular, escribiéndose como
L(r).

• Este lenguaje solo se refiere a cadenas de caracteres y por el


momento no tiene nada que ver con los lenguajes de programación.

• Una expresión regular usara símbolos de un alfabeto (∑), pero estos


caracteres tendrán un significado diferente, ya que todos indicaran
patrones.
Expresiones regulares
Definición básica:
1. Una expresión regular básica esta constituida por un solo carácter
proveniente del alfabeto definido, el meta-carácter Ɛ (palabra
vacía) o el lenguaje vacio (ɸ).

2. Una expresión de selección entre alternativas, de la forma r|s,


donde r y s son expresiones regulares.

3. Una expresión de concatenación de caracteres, de la forma rs


(algunas veces se usa un •, aunque este se puede obviar) donde tanto r
como s son expresiones regulares.

4. Una expresión de repetición de la forma r*, para expresar una


cerradura de Kleene, donde r es una expresión regular.
Expresiones regulares
Precedencia, paréntesis y nombres.
• La precedencia viene dada por:
• Repetición (*) Mayor precedencia.
• Concatenación.
• Selección entre opciones (|) Menor precedencia.

• Si deseamos una precedencia diferente debemos usar


paréntesis ( ), por ejemplo a|b * sin usar paréntesis se puede
interpretar como:
• ( a| b) *, interpretación incorrecta, pero era lo que se
quería expresar.
• a | (b *), interpretación correcta
Expresiones regulares
Precedencia, paréntesis y nombres.
• A las expresiones regulares largas se le pueden asignar nombres
como una forma de simplificar la notación, de modo que no
tengamos que escribirla cada vez que deseemos utilizarla, por
ejemplo podríamos escribir una expresión regular para la secuencia
de uno o más dígitos numéricos como:

(0|1|2|3|4|5|6|7|8|9)(0|1|2|3|4|5|6|7|8|9)*

o podríamos usar nombres

dígito dígito*

donde

digito = 0|1|2|3|4|5|6|7|8|9
Ejemplos con expresiones regulares
Consideremos el alfabeto ∑= {a,b,c}, como sería una expresión regular
que genere las cadenas de este alfabeto que contengan
exactamente una b:

(a|c)*b(a|c)*
Ejemplos con expresiones regulares
Consideremos el alfabeto ∑= {a,b,c}, considere el conjunto de todas
las cadenas que contienen como máximo una b:

(a|c)*|(a|c)*b(a|c)*

(a|c)*(b|Ɛ)(a|c)*
Ejemplos con expresiones regulares
Consideremos el alfabeto ∑= {a,b,c}, y la expresión regular que se muestra, determine para la
misma una descripción concisa del lenguaje que genera:

((b|c)*a(b|c)*a)*(b|c)*

Esto genera el lenguaje de


todas las cadenas que
contengan un numero par de
letras a.
Expresiones regulares
Extensiones para las expresiones regulares:

1. Una o más repeticiones, también conocida como la clausula positiva de


Kleen, se expresa con el símbolo +, por ejemplo (1|0)+, es equivalente a
la expresión regular (1|0)(1|0)*

2. Cualquier carácter, para generar cualquier carácter en el alfabeto, se


utiliza el metacarácter punto “.”, con lo que podemos escribir por ejemplo
una expresión regular para todas las cadenas que contengan una b como
.*b.*

3. Un intervalo de caracteres, a menudo se necesita escribir un intervalo de


caracteres, para esto tenemos dos opciones los enumeramos con una
selección de alternativas como por ejemplo 0|1|2|…|9 o usamos una
clase de caracteres como [0-9], otros ejemplos son: [abc] que equivale a
(a|b|c) o [a-zA-Z] que equivale a todas las letras mayúsculas y
minúsculas.
Expresiones regulares
Extensiones para las expresiones regulares:
4. Cualquier carácter que no este en un conjunto dado, es decir se lleva a cabo
la búsqueda del complemento de un conjunto dado, normalmente se
representa con la tilde “~” o el carat “^”, y se usa por ejemplo para generar un
carácter que no sea a como ~a (equivale a [^ a]), o un carácter que no sea a, ni b
ni c, como ~(a|b|c) (equivale a [^ abc]).

5. Subexpresiones opcionales, sirve para cadenas que contienen partes


opcionales que pueden o no aparecer en cualquier cadena generada, se
expresa con el símbolo “?”, por ejemplo un numero puede o no tener signo
inicia + o -, lo cual se puede expresar como

natural = [0-9]+
naturalconsigno = natural | + natural | - natural

o con

natural=[0-9]+
naturalconsigno=(+|-)? natural
Expresiones regulares para testigos o
tokens de lenguajes de programación
• Los testigos de los lenguajes de programación tienden en varias
categorías limitadas que son fácilmente clasificables de manera
estándar:

• Números: Pueden ser solo secuencias de dígitos (números naturales),


números decimales o números con exponente, pudiéndose escribir
expresiones regulares para los mismos:

nat = [0-9]+
natconSigno = (+|-)? nat
exponencial = natconSigno (“.” nat)?(E natconsigno)

El punto va entre “” para enfatizar que no debe ser interpretado como un


metacarácter.
Expresiones regulares para testigos o
tokens de lenguajes de programación
• Identificadores y palabras reservadas: son las más simples de escribir
como expresiones regulares, ya que están representadas por secuencias
fijas de caracteres., a excepción de los identificadores que no son fijos pero
por lo general comienzan por una letra, contienen solo letras o números,
Por ejemplo:

reservada = if | while | do | …

letra = [a-zA-Z]
digito = [0-9]
identificador = letra (letra|digito)*
Expresiones regulares para testigos o
tokens de lenguajes de programación
• Comentarios: ya que los comentarios se deben ignorar durante la fase de
análisis léxico, el analizador léxico debe ser capaz de reconocerlos e
ignorarlos, por lo tanto se necesitan conocer sus expresiones regulares así
no se usen durante el análisis, los comentarios pueden tener varias formas,
por ejemplo los comentarios de pascal
• {éste es un comentario}:

{(~})*}
Expresiones regulares para testigos o
tokens de lenguajes de programación
• Ambigüedades, espacios en blanco y búsquedas hacia adelante: Un lenguaje
debe proporcionar reglas de no ambigüedad, pero cadenas como <> se
pueden interpretar como el símbolo mayor, menor o el no igual a, por lo que
existen ciertas reglas que ayudan a lidiar con estos y otros aspectos y estas
son:

• Cuando una cadena puede ser un identificador o una palabra reservada, se prefiere
por lo general la interpretación como palabra reservada.
• Cuando una cadena puede ser un testigo (token) simple o una secuencias de varios
testigos (tokens), por lo general se prefiere la interpretación de testigo simple. Esto
a menudo se conoce también como el principio de la subcadena más larga, que
dice: la cadena más larga de caracteres que podrían constituir un testigo simple en
cualquier punto se supone que representa el siguiente testigo
• Pero esto trae a juego la cuestión de los delimitadores de testigos o testigos que
implican que una cadena más larga no puede pasar del punto donde este se consiga,
es decir son parte no ambigua de otro testigos, ejemplo xvar=yvar, donde el signo de
igualdad limita el tamaño de un identificador, para lidiar con esto se crean
pseudotestigos como:
espacioenblanco=(nuevalinea|blanco|tabulación|comentario)+
Autómatas finitos para testigos o
tokens de lenguajes de programación
• Los autómatas son un manera matemática para describir clases
particulares de algoritmos (o “máquinas”), en nuestro caso
específicamente los mismos se utilizan para describir el proceso de
reconocimiento de patrones en cadenas de entrada, y de este modo
se pueden usar para construir analizadores léxicos.

• Sus principales componentes son: los estados, las transiciones, el


estado inicial y los estados de aceptación.

• Existen dos tipos principales de autómatas finitos:


• Los determinísticos (DFA).
• Los no determinísticos (NFA).
Autómatas finitos para testigos o
tokens de lenguajes de programación
Autómatas finitos determinísticos (DFA):
• Son autómatas donde el estado siguiente está dado de forma
inequívoca por el estado actual y el carácter de entrada actual.
• Por ejemplo el DFA que reconoce identificadores

letra

letra
inicio Entrada_id

otro otro + dígito


otro

error
cualquiera
Autómatas finitos para testigos o
tokens de lenguajes de programación
Autómatas finitos determinísticos (DFA):
• Aspectos importantes para facilitar su uso:
• Los estados se pueden llamar de cualquier forma u omitirse el nombre si
este no es necesario.
• Se pueden etiquetar las transiciones con nombres que representen un
conjunto de caracteres y no necesariamente con caracteres.
• Las transiciones de error son opcionales y se puede suponer su
existencia, pero se debe tener cuidado con no confundir el DFA con un
NFA.
• Se asume que el estado de error es también de no aceptación.
Autómatas finitos para testigos o
tokens de lenguajes de programación
Ejemplo #1: usando las recomendaciones
El conjunto de cadenas que contienen exactamente una b.

nob
nob

+
Autómatas finitos para testigos o
tokens de lenguajes de programación
Ejemplo #2
El conjunto de cadenas que contienen como máximo una b:

nob
nob

+ +
Autómatas finitos para testigos o
tokens de lenguajes de programación
Ejemplo#3
El DFA para la definición de constantes numéricas en notación científica:

digito = [0-9]
nat = digito+
natconSigno =(+|-)? Nat
numero = natconSigno (“.” nat)?(E natconsigno)?

Para esto procedemos paso a paso a construir el DFA.


Autómatas finitos para testigos o
tokens de lenguajes de programación
Ejemplo#3 continuación
Primer paso es escribir un DFA para los números naturales:

nat = digito+
digito

digito

+
Autómatas finitos para testigos o
tokens de lenguajes de programación
Ejemplo#3 continuación
El segundo paso es escribir un DFA para los numeros naturales con signo:

natconSigno = (+|-)?nat

+ digito

digito

- +
digito
Autómatas finitos para testigos o
tokens de lenguajes de programación
Ejemplo#3 continuación
Como tercer paso agregamos la parte fraccional:

natconSigno = (+|-)?nat

+ digito digito
digito . digito
-
digito + +
Autómatas finitos para testigos o
tokens de lenguajes de programación
Ejemplo#3 continuación

Por ultimo agregamos la parte exponencial opcional


numero = natconSigno (“.” nat)?(E natconsigno)?

digito digito digito


+ +
digito . digito E digito
- -
digito + + digito +
E
Autómatas finitos para testigos o
tokens de lenguajes de programación
Ejemplo#4
Los comentarios en pascal son aceptados por el siguiente DFA:

otro

} }

+
Autómatas finitos para testigos o
tokens de lenguajes de programación
Búsqueda hacia adelante y retroseguimiento:
• Debemos recordar que el diagrama de un DFA no representa todo lo
que debe tener un DFA sino que solo proporciona un esbozo de su
funcionalidad, e incluso su definición matemática no describe todos
los aspectos del comportamiento de un algoritmo del DFA.

• Por ejemplo no se especifica que hará un programa de un DFA al


alcanzar un estado de aceptación, o incluso cuando iguale un
carácter durante una transición.
Autómatas finitos para testigos o
tokens de lenguajes de programación
Búsqueda hacia adelante y retroseguimiento:
• Siempre se debe mover hacia adelante el carácter de la cadena de
entrada al llevar a cabo una transición, hasta que se consiga un
token o testigo simple, denominándose a esto búsqueda hacia
adelante.

• Cuando se alcanza un estado aceptador se debe devolver el testigo


o token que se reconoció, junto con cualquier atributo asociado.

• Cuando se alcanza un estado de error normalmente se retrocede


hacia la entrada o se genera un testigo de error.
Autómatas finitos para testigos o
tokens de lenguajes de programación
Búsqueda hacia adelante y retroseguimiento:
Pero por ejemplo el DFA propuesto para un testigo de identificador,
no muestra el comportamiento que queremos de una analizador léxico
porque:
• El estado de error no es un error absoluto, ya que puede representar
dos cosas, el identificador no va a ser reconocido o se ha detectado un
delimitador y ahora debe ser reconocido el testigo o token.
letra

letra Entrada
inicio
_id

+ dígito
otro otro
otro

error
cualquiera
Autómatas finitos para testigos o
tokens de lenguajes de programación
Búsqueda hacia adelante y retroseguimiento:
Entonces podemos indicar que se ha detectado un delimitador desde el
estado entrada_id y debería generarse un testigo o token.
letra
ID de
[Otro] retorno
inicio letra entrad
a_id final

dígito +

Otro se encuentra entre [ ] para indicar que el carácter delimitador


debería considerarse como una búsqueda hacia adelante, es decir debería ser
devuelto a la entrada y no consumido por el autómata, además el estado de
error se ha convertido en uno de aceptación y no hay transiciones fuera del
estado de aceptación.
Autómatas finitos para testigos o
tokens de lenguajes de programación
Búsqueda hacia adelante y retroseguimiento:
• Lo anterior es lo que queremos puesto que el analizador léxico
debería reconocer un testigo a la vez y comenzar de nuevo en su
estado de inicio después de reconocer un testigo.

• Allí también se expresa el principio de la subcadena más larga,


hasta que se encuentra un delimitador.

• En contraste el DFA original permitía aceptar un identificador en


cualquier punto mientras se leía una cadena de identificador.
Autómatas finitos para testigos o
tokens de lenguajes de programación
Autómatas finitos No Determinísticos
Supongamos que tenemos varios testigos que comienzan por el
mismo carácter como <,<= y <>.
Ahora no se puede escribir un DFA puesto que dado un estado y
carácter (ej. <) siempre debe haber una transición única hacia un
estado único, pero aquí existen tres: hacia aceptado como <, hacia = o
hacia >, aunque podría arreglar esto añadiendo una transición
delimitadora para el (<)

¿Cómo?, la complejidad de una tarea así se vuelve enorme.


Autómatas finitos para testigos o
tokens de lenguajes de programación
Autómatas finitos No Determinísticos
Una solución a esto es ampliar la definición de autómata finito para incluir el caso
de que pueda existir más de una transición para un carácter en particular.

=
2 5 +
<

< >
1 3 6 +
+
<

4 +

Esta nueva clase de autómatas se conoce como autómatas finitos no


determinísticos, e introducen nuevos conceptos útiles como la transición Ɛ las cuales
ocurren sin una búsqueda hacia adelante y sin modificación de la cadena de entrada, es
decir de manera “espontanea”, teniendo como ventaja que mantienen intacto el
autómata original y solo agrega un nuevo estado de conexión.
Autómatas finitos para testigos o
tokens de lenguajes de programación
Autómatas finitos No Determinísticos
Una consecuencia de esto es que una cadena puede ser aceptada por diferentes
secuencias de transiciones, por ejemplo:

Ɛ
2
a b

a Ɛ
1 3 4 +

En este NFA la cadena abb puede ser aceptada de las siguientes maneras:

• -> 1 ->2 ->4 ->2 ->4


• ->1 ->3 ->4 ->2 ->4 ->2 ->4
Paso siguiente
• Todo comienza por una expresión regular que de algún modo se
convirtió en un NFA, y luego en DFA y posteriormente en un
algoritmo.

• El paso siguiente es la construcción del analizador léxico.

letra
ID de
[Otro] retorno
1 letra 2 3

dígito +
EJERCICIO JFLAP
• Expresiones regulares.
• Transformación regex a NFA.
• Determinización DFA.
• Minimización DFA.

También podría gustarte