Sunteți pe pagina 1din 22

Sintaxis y Semántica de los Lenguajes

UNIDAD I - INTRODUCCIÓN

El estudio de los fundamentos teóricos de la informática, conocidos como Informática Teórica, es un


componente esencial del diseño curricular destinado a formar Ingenieros en Sistemas de Información. Para
poder seguir evolucionando, las ciencias de la computación dependen de que se construyan computadoras,
sistemas, programas y lenguajes que se comporten en la forma que desearon sus diseñadores. Surge aquí
la necesidad de que el profesional en sistemas sea capaz de describirlos en forma precisa y de caracterizar
correctamente su comportamiento.
Más precisamente se requiere: especificar formalmente programas, lenguajes y sistemas; desarrollar
métodos que prueben la corrección de los mismos; y, encontrar formalismos semánticos que provean
sustento matemático para el desarrollo de teorías básicas sobre comportamiento de programas y sistemas.
En lo referido a las máquinas que manejan información (computadoras, redes de comunicación, etc.) es
necesario comprender su comportamiento, investigar su real poderío, y determinar los límites en las tareas
que ellas pueden realizar.
Los puntos indicados precedentemente son áreas dentro de las ciencias de la computación. Para
estudiarlas o para contribuir a su desarrollo, es preciso contar con conocimientos de tipo más abstracto y
básico.
Atendiendo a estos requerimientos, en el dictado de la asignatura se presentan al futuro profesional
modelos abstractos que sirven de fundamento teórico a lenguajes de programación y máquinas reales,
orientando en particular el estudio hacia los formalismos que sustentan las etapas de análisis léxico y
sintáctico en el proceso de compilación de un programa.
Se propone el estudio de dos abstracciones:

. Lenguajes Abstractos

Una consideración importante es la distinción entre lenguajes abstractos o formales, que son los que se
estudiarán en esta asignatura, y lenguajes naturales (inglés, español, etc.). Se puede decir que la diferencia
estriba en que los lenguajes formales (como pueden ser los lenguajes de programación) obedecen a reglas
preestablecidas y por tanto, se ajustan a ellas, no evolucionan y han sido creados para un fin específico. Sin
embargo, los lenguajes naturales (utilizados por el hombre) existen "per se" y las reglas gramaticales que
rigen su estructura han sido desarrollados con posterioridad para explicar esta última.
Los lingüistas han buscado largamente la manera de establecer formalmente la gramática de los
lenguajes naturales como una forma de explicar la tremenda habilidad que poseen los seres humanos para
interpretar sentencias que nunca antes han escuchado. Si bien estos estudios han fallado en su cometido de
producir gramáticas para los lenguajes naturales, nos han brindado una cantidad considerable de
conocimiento acerca de clases de lenguajes abstractos.
Qué es un lenguaje abstracto? Un conjunto de sentencias o secuencias, que satisfacen ciertas
propiedades o reglas de construcción. Las secuencias en sí mismas son secuencias finitas de símbolos
extraídos de un conjunto denominado alfabeto del lenguaje.
Un lenguaje puede ser tan simple como una secuencia de ceros y unos, o tan complejo como toda la
colección de programas que puedan escribirse siguiendo las reglas de algún lenguaje de programación.

. Máquinas Abstractas o Autómatas

Una máquina abstracta o autómata es un dispositivo teórico capaz de recibir y transmitir información.
Para realizar esta labor manipula cadenas de símbolos que se le presentan a la entrada produciendo otras
tiras o cadenas de símbolos a su salida mediante un conjunto de estados que contienen la información
necesaria para poder deducir, dado un símbolo de entrada, cuál será el símbolo de salida en cada momento.
La primera noción de máquina abstracta de procesamiento de información fue concebida por Turing.
Definición del término algoritmo  Un procedimiento que puede ser llevado a cabo en un tipo
específico de autómata, hoy conocido como máquina de Turing.
La máquina de Turing puede ser vista como un modelo abstracto de un dispositivo físico que posee un
número infinito de estados de existencia funcionalmente distinguibles. A pesar de que el número de estados
en una computadora electrónica es finito, este número es generalmente tan grande que la máquina de
Turing puede considerarse un modelo adecuado de su poderío computacional. Existen otros sistemas más
simples para los cuales la máquina de Turing es un modelo demasiado poderoso. Para estos sistemas se
deben estudiar modelos que se aproximen más a la capacidad de los mismos. Existen dos modelos, dos
abstracciones, las máquinas de estados finitos y el autómata "pushdown" que han probado ser útiles para
una gran variedad de sistemas prácticos.

1
¿Por qué se estudian los lenguajes abstractos
y los autómatas?

Autómatas Lenguajes Abstractos


Dispositivos abstractos para Estudio de la Mecanismos a través de los
procesar información cuales la información puede
- Reconocerla o Aceptarla relación entre ser transmitida, transferida,
- Traducirla los mismos comunicada
- Generarla

A pesar de la conexión que existe entre estos conceptos, los trabajos iniciales sobre lenguajes,
gramáticas y máquinas abstractas tienen orígenes distintos.

Estudio de las Ciencias de la Computación


desde un punto de vista teórico

Matemáticas Ingeniería Lingüística


1930. Turing basándose en 1938. (Shannon) Las funciones de 1950. Chomsky caracteriza
lógica matemática devela las redes de “switching relays” se las gramáticas y los lenguajes
límites fundamentales en la pueden representar con la notación formales.
computación teórica. simbólica del álgebra de Boole.

El descubrimiento de Turing 1950. Caldwell & Huffman extienden


es reforzado por el trabajo de este trabajo obteniendo un enfoque
Church, Kleene y Post en formal de los circuitos de cambio
funciones recursivas y en la secuencial, que evolucionan en la
formalización de la lógica teoría de las máquinas de estados
matemática. finitos.

La teoría de los autómatas proviene del campo de la ingeniería eléctrica. Shannon publicó varios trabajos
donde demostraba la aplicación de la lógica matemática a los circuitos combinatorios y secuenciales.
Posteriormente, sus ideas se desarrollaron para dar lugar a la Teoría de Autómatas. Moore publicó el primer
estudio riguroso sobre autómatas y, a finales de los años 50, se comenzó a ver la utilidad de los autómatas
en relación con los lenguajes.
El concepto de gramática procede de los estudios de Chomsky en su búsqueda de una descripción
formalizada de las oraciones de un lenguaje natural. Chomsky clasificó las gramáticas en cuatro grandes
grupos (G0, G1, G2, G3) cada uno de los cuales incluye a los siguientes (G 3G2G1G0). Las gramáticas
Tipo 0 se denominan gramáticas no restringidas, sin restricciones o gramáticas de estructura de frases; las
gramáticas Tipo 1 se denominan sensibles al contexto; las gramáticas Tipo 2 se conocen como gramáticas
libres de contexto o independientes del contexto y, por último, las gramáticas Tipo 3 se denominan
gramáticas regulares. Cada tipo añade restricciones al tipo inmediatamente superior y la jerarquía va desde
la más general a la más restrictiva. Cada una de estas gramáticas es capaz de generar un tipo de lenguaje.
Un lenguaje L se llama del tipo i (i=0, 1, 2, 3) si existe una gramática G del tipo i capaz de generar o describir
ese lenguaje.
Estos estudios previos sobre teorías de gramáticas formales y lenguajes crearon las bases de la
lingüística matemática, la cual tendría aplicación no solo al estudio del lenguaje natural sino también a los
lenguajes formales.
La teoría de lenguajes y gramáticas formales tiene una relación directa con la teoría de máquinas
abstractas, siendo posible establecer entre ambas un isomorfismo. Dado que las gramáticas proporcionan
las reglas utilizadas en la generación de las cadenas de un lenguaje, se puede establecer una conexión
entre la clase de lenguajes generados por ciertos tipos de gramáticas, y la clase de lenguajes reconocibles
por ciertas máquinas. Así, se pueden identificar los lenguajes del tipo 0 con la clase de lenguajes
reconocidos por una Máquina de Turing; los lenguajes del tipo 1 con los Autómatas Linealmente Acotados;
los lenguajes de tipo 2 con los Autómatas "Pushdown" o a Pila y, por último, los lenguajes de tipo 3 con los
Autómatas Finitos. Al igual que ocurría con las gramáticas, cada tipo de máquina abstracta añade
restricciones al tipo de máquina del nivel superior. Todas poseen una cinta de donde leen los símbolos de
entrada, un conjunto de estados que representan diferentes fases del análisis de las palabras de entrada, un
lugar donde generar la salida, y, en algunos casos, cuentan con dispositivos auxiliares de memoria. Las
diferencias entre ellas estriban en la capacidad para escribir en la cinta de entrada, en los distintos tipos de
movimientos que pueden realizar sobre la cinta, si tienen o no memoria auxiliar, etc.

2
Establecidas las reglas de una gramática, una cadena de símbolos pertenecerá al correspondiente
lenguaje si tal cadena se ha formado obedeciendo esas reglas. A partir de una gramática se puede construir
una máquina reconocedora o aceptora del lenguaje generado por esa gramática, de tal forma que cuando
reciba a su entrada una determinada cadena de símbolos indicará si dicha cadena pertenece o no al
lenguaje. Una máquina reconoce un lenguaje L si es capaz de reconocer todas las sentencias
pertenecientes a L y de no reconocer ninguna sentencia que no pertenezca a L. La siguiente figura muestra
la relación existente entre los tres conceptos manejados.

equivale
Gramática Máquina

describe reconoce
genera genera
Lenguaje

Dado que una gramática describe formalmente un lenguaje y que este lenguaje puede ser reconocido o
aceptado por una determinada máquina abstracta o autómata, entonces es posible establecer una jerarquía
de máquinas abstractas y gramáticas, que se muestra a continuación:

Gramáticas no Reconocedor o
Restringidas L0 Máquina de
Tipo 0 Turing
Lenguajes más restringidos

Máquinas más poderosas


Gramáticas
Autómatas
Sensibles al L1 Linealmente
Contexto
Acotados
Tipo 1

Gramáticas
Libres de L2 Autómatas
Contexto Pushdown
Tipo 2

Gramáticas
L3 Autómatas de
Regulares
Estados Finitos
Tipo 3

La teoría de gramáticas y máquinas teóricas tiene múltiples aplicaciones en diversas áreas de


conocimiento. Algunos ejemplos de las aplicaciones de los lenguajes regulares y los autómatas finitos son
los analizadores léxicos que se utilizan en los compiladores de lenguajes de programación. Normalmente, un
analizador léxico es un autómata que se utiliza para el reconocimiento de las palabras empleadas en un
lenguaje de programación (variables, palabras reservadas, números, etc.). También se utilizan en los
editores de texto para buscar y reemplazar palabras que se equiparan con una expresión regular.
La sintaxis de los lenguajes de programación (Pascal, C, etc.) está descrita mediante gramáticas de Tipo
2 (libres de contexto) aunque también pueden contener algunos aspectos que requieran de una gramática
sensible al contexto.
En cuanto a las Máquinas de Turing, se corresponden con las gramáticas sin restricciones. La
peculiaridad de este tipo de gramáticas es que pueden describir cualquier suceso computable. Un suceso
computable es aquél que mediante un procedimiento es capaz de transformar unas determinadas entradas
en las salidas requeridas. Ésta es precisamente la capacidad que se necesita en un computador; el poder
describir un procedimiento (programa) que, a partir de unas entradas, sea capaz de producir una salida. Así
pues, existe una asociación entre las Máquinas de Turing y los fenómenos computables, de forma que se
dice que un suceso es computable si existe una Máquina de Turing capaz de describirlo.

Comenzaremos por las gramáticas regulares o de tipo 3, que son las más simples, y descubriremos
problemas o lenguajes que exceden la capacidad de las máquinas de ese nivel; así nos moveremos hacia
niveles con lenguajes y máquinas más poderosos. En cada nivel vamos a encontrar el mismo tipo de
inconvenientes hasta llegar al nivel de las gramáticas de tipo 0 y las máquinas de Turing.

3
Lenguaje:
Un lenguaje L es simplemente un conjunto de "strings" de longitud finita sobre un conjunto finito V,
denominado alfabeto. En esta definición un "string" es una abstracción de objetos familiares tales como
palabras u oraciones. Un lenguaje posee un alfabeto o vocabulario y una gramática. El alfabeto son los
bloques a partir de los cuales se construye el lenguaje. La gramática indica cómo combinar esos bloques en
una manera sintácticamente aceptable. Las gramáticas intentan capturar aquello relacionado con la
estructura o sintaxis del lenguaje.
Dado un alfabeto V, el conjunto V* es el conjunto de todos los posibles "strings" formados a partir de V.
La descripción de un lenguaje L se dice suficiente si es que puede ser usada para decidir si un miembro de
V* es o no miembro de L. Esta noción de suficiencia parece ignorar cuestiones concernientes al significado
de las sentencias, es decir a la semántica de las mismas.

Tomemos por ejemplo la oración:


La bibliotecaria me da pocos libros. (1)
vemos que es gramaticalmente correcta.
Libros pocos da me bibliotecaria la. (2)
es gramaticalmente incorrecta.
Cómo reconocemos que una oración es gramaticalmente correcta? Mediante el "parsing" o análisis de la
oración averiguamos si las categorías gramaticales de la misma ocurren en los lugares correctos. Esta
clasificación puede ser expresada por medio de un árbol conocido como "parse tree" (o árbol de derivación)
de la oración.
Para la oración (1) tenemos:
Oración

Sujeto Predicado
(Sintagma Nominal) (Sintagma Verbal)

Modificador Directo Núcleo Objeto Indirecto Núcleo Objeto Directo

Artículo Sustantivo Pronombre Verbo Adjetivo Sustantivo

La bibliotecaria me da pocos libros

Puede observarse que este proceso de "parsing", análisis gramatical o clasificación sintáctica es
reversible, de aquí que una gramática pueda ser empleada como un mecanismo para generar sentencias
aceptables.
El árbol presentado anteriormente puede utilizarse para generar oraciones que tengan la misma
estructura gramatical. Por ejemplo:
El estudio le produce muchas satisfacciones.
Ese árbol me da excelentes naranjas.
La madre le brinda mucha ternura.

Así una gramática es un conjunto (vocabulario), más un conjunto finito de reglas que gobierna cómo se
pueden generar las oraciones. Desde el punto de vista de lenguaje formal esto es cuanto importa. Es decir,
la definición de lenguaje formal se basa solamente en sus estructuras sintácticas o gramaticales,
ignorándose la estructura semántica. Por ejemplo, con el árbol antes presentado se puede generar la
oración:
Esa verdura me produce excelente carne.
la cual es obviamente absurda en cuanto a su significado, pero correcta desde un punto de vista sintáctico.
Estos problemas ocurren al tratar lenguajes naturales, pero no intervienen en el tratamiento de los lenguajes
formales, donde nos limitaremos a estudiar los lenguajes y sus gramáticas.

Autómatas:
Las relaciones entre lenguajes abstractos y máquinas abstractas se establecen a partir del estudio de
tres tipos de autómata:
- Reconocedores o Aceptores.
- Generadores.
- Traductores.

4
Los símbolos de la secuencia s(1) s(2) .... s(i) .... s(t) son presentados secuencialmente a una máquina
M, que responde a cada símbolo con una señal binaria (lamparita ON/OFF). Los símbolos son tomados de
un conjunto finito conocido como alfabeto de entrada a M. Antes de que un "string" ingrese a M, se supone
que M está funcionando en un estado predeterminado (inicialización).
Se supone que esta máquina opera determinísticamente  La respuesta depende de:
- Estado Inicial.
- La secuencia de entrada presentada.

Si M acepta precisamente las sentencias de algún lenguaje, decimos que M reconoce al lenguaje L.

Cuando el generador es arrancado a partir de un estado inicial, emite una secuencia de símbolos
r(1) r(2) .... r(i) .... r(t)
que es seleccionada de un conjunto conocido como alfabeto de salida. Si cada vez que M es arrancado
produce la misma secuencia de salida, el lenguaje generado no resulta interesante. Se le permite entonces
exhibir cierto comportamiento no-determinístico (al azar), y producir distintas secuencias cada vez que es
operado.
Luego el lenguaje generado por M es el conjunto de todas las secuencias que la máquina pudiera
generar.

El traductor M produce una sentencia


r(1) r(2) .... r(i) .... r(t)
en respuesta a la presentación de la sentencia
s(1) s(2) .... s(j) .... s(m)
Esta máquina que opera determinísticamente traduce cada sentencia de un lenguaje de entrada en una
sentencia específica de un lenguaje de salida. Si L es el lenguaje de entrada, el conjunto de sentencias
producidas por M en respuesta a sentencias que pertenecen a L es la traducción de L por medio de M.

5
UNIDAD II - LINGÜÍSTICA MATEMÁTICA

I.- STRINGS Y LENGUAJES

Vamos a introducir el sistema matemático que sirve como nuestro modelo abstracto de lenguaje.
Como abstracción de lenguaje vamos a emplear conjuntos que tienen "strings" como miembros. A
continuación vamos a desarrollar las propiedades matemáticas básicas de estas abstracciones.

1. Strings y Operaciones con Strings

ALFABETO: Conjunto finito de símbolos atómicos (no divisibles) que se emplean en la construcción de
sentencias.

STRING de longitud k sobre un alfabeto V: Es una secuencia de k símbolos en V.


 = v1, v2, ......, vk vi  V
Comúnmente vamos a omitir las comas y a escribir  como
 = v1 v2 ...... vk
Vamos a reservar letras griegas minúsculas para indicar strings.
Indicamos que un string  tiene longitud k mediante:
||=k

Ejemplos: Sea V = {a, b, c}, luego  = abcbc es un string en V y |  | = 5


V’ = { a, bc } ’ = abcbc es un string en V' y | ’ | = 3

String Vacío: Es el único string que no contiene símbolos y es indicado con la letra  (lambda). Luego
||=0

W k: Vamos a indicar con esta notación al conjunto de todos los strings de longitud k sobre un alfabeto V:
Wk = {  / |  | = k }
W o es el conjunto que contiene el string vacío, empleamos  (negrita) para indicar este conjunto.
Wo = {  } = 
El conjunto

W = U Wk
k=0
es el conjunto de todos los strings definidos sobre V, incluyendo el string vacío.

CONCATENACIÓN DE STRINGS: Operación también denominada multiplicación de strings, es una operación


binaria definida sobre el conjunto W:
m: W x W  W
Dados dos strings  y  tales que:
 = v1 v2 ...... vm  = u1 u2 ...... un
La concatenación de los strings  y  es la secuencia de símbolos formada al extender la secuencia 
con la secuencia .
m(,) = v1 v2 ...... vm u1 u2 ...... un
A la operación m(,) la vamos a indicar también . o simplemente .

k: La notación k es empleada para indicar la concatenación de k copias del string .
k = ...…..
k veces

Propiedades de la concatenación de strings:


1.  ,   W: .  W
2.  , ,   W: .(.) = (.).
3.    W: . = . = 
4. . no necesariamente igual a .  la concatenación de strings no es conmutativa
5.  ,   W: | . | = |  | + |  |

6
REVERSO DE UN STRING: Dado el string  = v1 v2 ...... vn se define el reverso:
R = vn ...... v2 v1

2. Operaciones con Lenguajes

LENGUAJE definido sobre un alfabeto V es cualquier conjunto de strings definidos sobre el conjunto V. Así,
cada lenguaje definido en V es un subconjunto del conjunto W de todos los strings en V: L  W.

Las operaciones básicas que se aplican a lenguajes son también las operaciones básicas aplicables a
conjuntos, es decir unión, intersección y complemento relativo a W.

CONCATENACIÓN DE LENGUAJES
Dados dos lenguajes A y B, definimos a la concatenación de los mismos
A.B = { . /   A,   B }
al conjunto de strings que resulta de extender cada string definido en A con cada string definido en B.
A.B también puede ser escrito como AB.

Ejemplo: Dados A = { a, ab } , B = { c, bc } luego AB = { ac, abc, abbc }

Propiedades de la concatenación de lenguajes:


a)  A  W: A. = .A = A
El conjunto  = {  } es un elemento identidad en la concatenación de lenguajes.

Ejemplo: Sean A = { a, bc } ={}


luego A. = { a., bc.} = { a, bc } = A

b)  A  W: A. = .A = 
No debe confundirse el conjunto  (conjunto unitario que contiene al string vacío) con el conjunto 
(elemento absorbente).
A. = { . /   A,    } = 

c)  A, B, C  W: A.(B.C) = (A.B).C

Ejemplo: Sean A = { , b }; B = { , ab }; C = { ab, b }


luego A.(B.C) = { , b } . { ab, b, abab, abb } = { ab, b, abab, abb, bab, bb, babab, babb } 
y (A.B).C = { , ab, b, bab } . { ab, b } = { ab, b, abab, abb, bab, bb, babab, babb } 

d) A.B no necesariamente igual a B.A  la concatenación de lenguajes no es conmutativa

e)  A, B, C  W: A.(B  C) = A.B  A.C  la concatenación distribuye sobre la unión

Ejemplo: Sean A = { , b }; B = { , ab }; C = { ab, b }


luego A.(B  C) = { , b } . { , ab, b } = { , ab, b, bab, bb } 
y A.B  A.C = { , ab, b, bab }  { ab, b, bab, bb } = { , ab, b, bab, bb } 

f) A.(B  C) no necesariamente igual a A.B  A.C  la concatenación no distribuye sobre la intersección

Ejemplo: Sean A = { , b }; B = { , ab }; C = { ab, b }


luego A.(B  C) = { , b } . { ab } = { ab, bab }
y A.B  A.C = { , ab, b, bab }  { ab, b, bab, bb } = { ab, b, bab }

CLAUSURA DE UN LENGUAJE
Sea V un alfabeto y W el universo de strings generados a partir de V; para un lenguaje A  W, definimos
los conjuntos Ak , k  0 mediante
A0 =  Ak+1 = Ak.A
luego A = A .A = .A = A
1 0
A2 = A1.A = A.A
A3 = A2.A
A4 = A3.A

7
Cada uno de estos conjuntos Ak es un subconjunto de W dado que W es cerrado a la operación
concatenación.
Luego la unión de todos estos conjuntos

A* = U Ak
k=0
es también un subconjunto de W y es llamado clausura de A. El operador * se conoce como estrella de
Kleene (Kleene Star). La clausura de un lenguaje contiene cada string que puede formarse mediante la
concatenación de un número arbitrario de strings tomados del lenguaje.

Ejemplos:
1. Sea A = { a, ab }
A =={}
0
luego
1
A = A = { a, ab }
2
A = A.A = { aa, aab, aba, abab }
3 2
A = A .A = { aaa, aaba, abaa, ababa, aaab, aabab, abaab, ababab }
es posible ver que { a, ab }  { a , (ab) }
3 3 3
...
A = { , a, ab, aa, aab, aba, abab, aaa, aaba, abaa, ababa, aaab, aabab, abaab, ababab, ... }
*

2. Sea B = { a }
B ={}
0 1 2 3
luego B ={a} B = { aa } B = { aaa } ...
B = { , a, aa, aaa, aaaa, aaaaa, ... } = { a / k  0 }
* k

3. Sea C = { 0, 1 }
C = { , 0, 1, 00, 01, 10, 11, 000, 001, 010, 011, 100, 101, 110, 111, ... }
*
luego
*
B es el conjunto de todos los strings posibles definidos sobre B, lo mismo es válido para C.

Definición: Dado que el conjunto de todos los strings de longitud k+1 definidos sobre un alfabeto V puede ser
escrito como
k+1 k
W k+1 = W k . V y V =V .V
se deduce que
W k = Vk y W = V*
Luego V* es el universo de strings que pueden ser definidos a partir del alfabeto V.

REVERSO DE UN LENGUAJE
El reverso de un lenguaje L contiene el reverso de cada string definido en L.
LR = { R /   L }

Consideremos ahora al lenguaje


L = { R /   V* }
Cada string en L es el reverso de sí mismo, por esta razón L es conocido como lenguaje imagen
especular sobre el alfabeto V. Si cada string de L es a su vez su propio reverso, luego tendremos que
L = LR
Sin embargo la afirmación inversa no es necesariamente cierta, es decir si un lenguaje es igual a su
reverso ello no implica que cada uno de los strings del mismo sea igual a su reverso.
( L es Lie     L:  =  )  L = L
R R

( L = L ) no  L es Lie
R

Ejemplos:
1. Sea L1 = { 00, 1001 }
R R R
L1 = { (00) , (1001) } = { 00, 1001 } = L1 L1 es Lie

2. Sea L2 = { 01, 10 }
R R R
L2 = { (01) , (10) } = { 10, 01 } = L2
sin embargo, 01  10 y 10  01, es decir L2 no es Lie

8
Puede llegar a ser conveniente caracterizar a los lenguajes en función de operaciones tales como la unión,
la concatenación y la clausura aplicadas sobre elementos simples de un dado alfabeto. Por ejemplo:
L = ( { 0 } . { 0, 1 } )*
es el lenguaje que contiene todos los strings obtenidos por concatenación sucesiva del conjunto
{ 0 } . { 0, 1 } = { 00, 01 }
L = { 00, 01 }*
Expresiones de este tipo se simplifican generalmente omitiendo las llaves de los conjuntos unitarios y
escribiendo sus elementos en negritas
{0}=0
{ 0, 1 } = 0  1
Luego
( { 0 } . { 0, 1 } )* = ( 0 . ( 0  1 ))*
En este tipo de notación también se permite el empleo de los símbolos  y .
Ejemplos:
1. ( 1   ) ( 0  1 ) = ( 1   ) . ( 0  1 ) = { 1,  } . { 0, 1 } = { 10, 11; 0, 1 }
2. ( 1   ) ( 0  1 ) = ( 1   ) . ( 0  1 ) = { 1 } . { 0, 1 } = { 10, 11 }

La desventaja de este tipo de notación compacta es que un dado conjunto de strings puede ser definido
por una variedad de expresiones diferentes, y a veces no es obvio que esas expresiones describen el mismo
conjunto. Tal es el caso de las expresiones ( A  1 )* y ( 1* A* )* que describen el mismo conjunto cualquiera
sea la definición de A.

II.- GRAMÁTICAS FORMALES

La traducción de texto de un lenguaje a otro lenguaje ha sido por largo tiempo el sueño de lingüistas y
computadores científicos. Si bien luego de la introducción de lenguajes de alto nivel hubo un período de gran
actividad en la búsqueda de formalizaciones precisas de lenguajes para apoyar y simplificar la descripción
del proceso de traducción, los mayores frutos no se dieron en el área de traducción de lenguajes naturales.
Sin embargo la teoría desarrollada tuvo y tiene una gran influencia en el diseño y procesamiento de
lenguajes de programación.
A continuación presentamos los conceptos fundamentales de las gramáticas formales y su relación con
los lenguajes que ellas describen. A pesar de que se han estudiado muchas clases de gramáticas y
lenguajes, nuestra atención se va a concentrar en aquellas incluidas en la jerarquía propuesta por Chomsky.

1. Representaciones de Lenguajes

De acuerdo al diccionario, "un lenguaje es el agregado de palabras y métodos de combinar las mismas
que es empleado por una determinada nación, comunidad, grupo étnico, religioso, etc.". Esta definición no
provee una descripción matemática de un lenguaje. Los lingüistas piensan que cualquier formalismo que
represente adecuadamente un lenguaje natural deberá poder representar la infinidad de sentencias que
puedan aparecer en el uso del lenguaje. Sin embargo, no podemos esperar que todas las posibles
sentencias del lenguaje sean enumeradas.
De la misma forma, y pasando a los lenguajes de programación, no podemos esperar que se listen todos
los posibles programas a ser escritos por los usuarios de lenguajes como ALGOL, FORTRAN, PASCAL, etc.
Luego, el problema central en la descripción de un lenguaje es proveer una especificación finita para una
clase de objetos esencialmente infinita.
Dado un alfabeto V, el conjunto V* es la totalidad de todos los posibles strings definidos sobre V. Un
lenguaje L definido sobre V es un subconjunto arbitrario de V*. Ya dijimos que una descripción de L es
suficiente si puede ser empleada para decidir cuándo un miembro dado de V* es o no miembro de L.
En castellano, el alfabeto posee 29 letras que se pueden escribir en mayúsculas y minúsculas, tenemos
10 dígitos, el espacio, signos de puntuación, admiración, etc. Si existiese una gramática formal de este
lenguaje, la misma debería poder discernir si una oración dada es correcta o no.
Para un lenguaje de programación, el alfabeto es la colección de símbolos no divisibles que pueden
aparecer en el programa. Una sentencia es generalmente considerada un string formado por esos símbolos
que representa un programa completo. Una gramática satisfactoria para un lenguaje de programación debe
permitirnos discernir por un procedimiento mecánico si una secuencia arbitraria de símbolos es un programa
"bien conformado".
El requerimiento de que la representación de un lenguaje sea finita parece ser tremendamente severa e
implica casi directamente que muchos lenguajes no poseen una representación finita. El problema es si los
lenguajes naturales pertenecen o no a esta categoría, sin embargo a nosotros por ahora no nos ocupa.
9
Una forma de representar un lenguaje ya fue sugerida y es la de emplear un aceptor del lenguaje. Si el
aceptor posee un número finito de estados funcionalmente relevantes, él en sí mismo constituye una
representación finita. Sin embargo, vamos a encontrar que las clases de lenguajes para los cuales estas
máquinas de estado finito proveen una descripción estructural es bastante limitada  vamos a considerar
otras máquinas abstractas más poderosas como posibles representaciones finitas de lenguajes.
La definición de lenguaje por medio de una máquina aceptora es un enfoque analítico a la representación
del lenguaje, ya que la máquina verifica directamente si la sentencia de entrada es o no miembro del
lenguaje.
Otra alternativa es emplear una representación generativa del lenguaje, es decir emplear un conjunto
finito de reglas que si son seguidas en un orden válido van a permitir solamente la construcción de strings
que sean sentencias válidas del lenguaje.
Puede una representación generativa de un lenguaje ser empleada para decidir si un string dado es
miembro del lenguaje??... Veremos que sí, este procedimiento funciona para todas las gramáticas, con
excepción de la forma más general. Para esa forma general veremos que no existe procedimiento mecánico
que pueda decidir si un dado string es generado por la gramática.

2. Conceptos básicos de Gramáticas

2.1. Un ejemplo del Castellano

Concepto clave  construcción de oraciones mediante la sucesiva aplicación de reglas gramaticales


Una regla especifica cómo una frase puede ser reescrita concatenando sus frases constitutivas.
Jorge y Pablo subieron a la terraza
sujeto predicado
La construcción de esta oración puede ser descripta mediante la regla gramatical
1.- <oración>  <sujeto><predicado>
La inclusión entre ángulos tiene el propósito de evitar cualquier posible confusión con palabras o
símbolos que aparezcan en las oraciones del lenguaje que está siendo analizado.

En este ejemplo el sujeto es un par de sustantivos vinculados por una conjunción, o sea
<sujeto>  <sustantivo><conjunción><sustantivo>
Sin embargo, preferimos derivar reglas que expliquen la estructura como una colección de frases en
tanto ello se pueda.
2.- <sujeto>  <frase nominal>
3.- <frase nominal>  <sustantivo>
4.- <frase nominal>  <sustantivo><conjunción><frase nominal>
Vemos que en esta regla <frase nominal> tiene a <frase nominal> como uno de sus elementos
constitutivos, es decir tenemos una definición recursiva. Esta propiedad recursiva de las reglas gramaticales
nos permite la representación finita de un conjunto infinito de sentencias.

Luego el sujeto del ejemplo se genera mediante la aplicación de las reglas 2 a 4 y una aplicación de cada
una de estas reglas:
5.- <conjunción>  y
6.- <sustantivo>  Jorge
7.- <sustantivo>  Pablo
de la siguiente forma:
<sujeto>
<frase nominal> (regla 2)
<sustantivo><conjunción><frase nominal> (regla 4)
<sustantivo> y <frase nominal> (regla 5)
Jorge y <frase nominal> (regla 6)
Jorge y <sustantivo> (regla 3)
Jorge y Pablo (regla 7)

Estas líneas, conocidas como formas sentenciales, conforman lo que se conoce como derivación de la
frase sujeto de acuerdo a la aplicación de las reglas 2 a 7. Cada línea de la derivación resulta de la re-
escritura de la línea precedente de acuerdo a alguna regla establecida.

El predicado de nuestro ejemplo consiste de un verbo y de una frase preposicional.


subieron a la terraza
verbo frase preposicional
predicado

10
8.- <predicado>  <verbo><frase preposicional>
9.- <verbo>  subieron

La estructura de la frase preposicional es descripta mediante la siguiente regla:


10.- <frase preposicional>  <preposición><artículo><sustantivo>
en la cual cada uno de los elementos constitutivos puede ser reemplazado por:
11.- <preposición>  a
12.- <artículo>  la
13.- <sustantivo>  terraza

Las reglas 1 a 13 constituyen una gramática. La oración que tomamos como ejemplo es un miembro del
lenguaje generado por esta gramática, cuya descripción estructural puede ser representada por el siguiente
diagrama en árbol.

Cada nodo de este árbol corresponde a la aplicación de una dada regla gramatical (ello se indica
mediante los números). Dado que estas producciones especifican cómo un tipo de frase se forma mediante
la combinación de otros tipos de frases, estas gramáticas se denominan gramáticas descriptas mediante
estructuras de frases.
Cada oración para la cual una dada gramática brinda una descripción estructural se conoce como una
sentencia válida del lenguaje generado por la gramática. Por ejemplo, nuestra gramática también genera las
siguientes oraciones:
Pablo y Jorge subieron a la terraza
Pablo y Pablo y Jorge subieron a la terraza
Pablo y Jorge y Pablo y Jorge subieron a la terraza
.................................
Estos ejemplos muestran cómo un conjunto finito de reglas, a través de sucesivas aplicaciones, puede
generar un conjunto arbitrariamente grande de sentencias. Sin embargo, estas oraciones también son
generadas por la gramática:
terraza subieron a la Jorge
Pablo y terraza subieron a la Jorge
Las mismas son incorrectas semánticamente, pero verifican la gramática propuesta.

2.2. Un ejemplo del ALGOL

Como segundo ejemplo de una gramática descripta mediante estructuras de frases vamos a considerar
un trozo de lenguaje ALGOL. Algol fue el primer lenguaje de programación práctico para el cual se formuló
un conjunto completo de reglas gramaticales. Consideremos una forma simplificada de una sentencia de
asignación del ALGOL, de la cual la frase
w := if x < y then 0 else x + y + 1
es un ejemplo. Esta oración puede interpretarse de la siguiente forma: Si x < y asigne valor 0 a w, de lo
contrario asigne el valor (x + y + 1) a w. Esta frase es escrita con símbolos que provienen del alfabeto { w, x,
y, 0, 1, <, :=, if, then, else } que es solamente una parte de todo el alfabeto del ALGOL. Es importante notar
que if, then y else se consideran símbolos simples, no divisibles.

w := if x < y then 0 else x + y + 1


parte izquierda expresión

1.- <declaración>  <parte izquierda><expresión>

El significado de <expresión> es el siguiente: <expresión> representa un valor numérico que debe


asignarse a la variable definida en la parte izquierda. En este ejemplo, <expresión> es una construcción
condicional formada de acuerdo a la regla gramatical número 3. Para la parte izquierda tenemos:
11
2.- <parte izquierda>  <identificador> :=
3.- <expresión>  if <expresión booleana> then <expresión aritmética> else <expresión>

Aquí la frase tipo expresión booleana es interpretada representando un valor de verdadero o falso. La
construcción condicional se interpreta así: Si el valor de la expresión booleana es verdadero, su valor es el
de la expresión aritmética, de lo contrario, su valor es el de <expresión>. La recurrencia de la frase tipo
expresión en esta regla permite que las frases tipo se aniden en una profundidad arbitraria.

La estructura de la frase: x+y+1


podría describirse de la siguiente forma:
<expresión>  if <identificador> + <identificador> + 1
sin embargo pretendemos una representación más general, así tenemos:
4.- <expresión>  <expresión aritmética>

De la misma forma podríamos indicar a: x<y


<expresión booleana>  <identificador> < <identificador>
sin embargo esta regla carece de generalidad y por lo tanto proponemos la siguiente en su reemplazo:
5.- <expresión booleana>  <expresión aritmética> < <expresión aritmética>
Pero una expresión booleana también puede tener la siguiente forma:
6.- <expresión booleana>  <expresión aritmética> = <expresión aritmética>

Siguiendo ahora con la regla 4, y considerando la expresión: x + y + 1


podemos seguir el desarrollo de la siguiente manera:
7.- <expresión aritmética>  <término> + <expresión aritmética> (regla recursiva)
8.- <expresión aritmética>  <término>

La frase tipo <término> se interpreta como un número o como letra que identifica una variable
(<identificador>); luego el valor de un término es el número en sí mismo o el valor de la variable. La regla 7,
que es recursiva con respecto a la frase tipo <expresión aritmética>, permite la generación de frases que
contengan un número arbitrario de frases tipo <término> separadas por el símbolo +. La aparición de
números y letras que indican variables requiere del desarrollo de las siguientes reglas:
9.- <término>  <identificador>
10.- <término>  0
11.- <término>  1

En forma más general, un término puede ser también una expresión encerrada entre paréntesis:
12.- <término>  (<expresión>)

Asimismo, como identificadores podemos incluir las tres letras que aparecen en nuestro ejemplo:
13.- <identificador>  w
14.- <identificador>  x
15.- <identificador>  y

Para simplificar nuestro tratamiento vamos a introducir una convención notacional que se va a emplear a
lo largo del estudio de las gramáticas formales: Se emplearán letras mayúsculas para indicar frases tipo:
S <Statement> <declaración>
L <Left Part> <parte izquierda>
E <Expression> <expresión>
I <Identifier> <identificador>
B <Boolean> <expresión booleana>
A <Arithmetic> <expresión aritmética>

Luego, el árbol que indica el análisis sintáctico de nuestra


declaración de asignación es el de la derecha.

12
Expresada en función de estas letras, una gramática para frases del tipo <declaración> está definida por
el siguiente conjunto de reglas:

1.- S  LE 6.- B  A = A 11.- T  1


2.- L  I := 7.- A  T + A 12.- T  (E)
3.- E  if B then A else E 8.- A  T 13.- I  w
4.- E  A 9.- T  I 14.- I  x
5.- B  A < A 10.- T  0 15.- I  y

Este conjunto de reglas de re-escritura es una gramática formal en la cual las reglas se conocen como
producciones. Con esta gramática se puede generar una gran variedad de declaraciones de asignación. La
pregunta es ahora: Sabemos que esta gramática es incompleta, pero es una representación adecuada de la
declaración de asignación del ALGOL? La respuesta es: Cada string generado por la gramática es una frase
legal de acuerdo a la sintaxis "oficial" del ALGOL..
Pero la gramática genera frases como
x := x
x := if w < w then x + y else x
que no tienen ningún sentido en un programa computacional, pero que son aceptadas por la mayoría de los
compiladores del ALGOL.

3. Gramáticas Formales

Una gramática tiene tres componentes fundamentales:


- El alfabeto o colección de símbolos a partir del cual se construyen las sentencias que describen el
lenguaje. Estas se denominan letras o símbolos terminales de la gramática, dado que la generación de
una sentencia a través de la aplicación de reglas gramaticales debe terminar en un string que contiene
sólo estos símbolos.
- Un conjunto de símbolos que indiquen o denoten frases llamados letras o símbolos no terminales.
- Un conjunto o colección de reglas gramaticales o producciones.

Para completar la especificación del lenguaje, es necesario un punto inicial para la aplicación de las
producciones. El signo  (sigma), conocido como símbolo sentencia o símbolo distinguido, se reserva para
esta función en nuestro trabajo con gramáticas formales.

Vamos a comenzar ahora con una definición más general de gramática formal que excede los
requerimientos presentados en ejemplos anteriores.

Definición: Una gramática formal es una cuaterna:


G = (N, T, P, )
donde
N Conjunto finito de símbolos no terminales
T Conjunto finito de símbolos terminales
N y T son disjuntos: N  T = 
P Conjunto finito de producciones
 Símbolo sentencia o elemento distinguido;   (N  T)

Cada producción en P es un par ordenado de strings (  ,  )


=A , ,   (N  T)*
 = AN{}
tales que ,  y  son strings en (N  T)* –inclusive vacíos– y A es  o un símbolo no terminal. Vamos a
escribir una producción ( ,  ) como


Las gramáticas formales definidas aquí se conocen también como gramáticas descriptas mediante
estructuras de frase. El proceso de generación de una oración de acuerdo a una gramática formal es el
proceso de re-escritura sucesiva de formas sentenciales a través del uso de producciones de la gramática,
comenzando con la forma sentencial . La secuencia de formas sentenciales requeridas para generar una
sentencia se conoce como derivación de la sentencia de acuerdo a la gramática.

Definición: Sea G una gramática formal. Un string de símbolos en (N  T)*  {  } se conoce como forma
sentencial.

13
Si    es una producción de G y  =    y ’ =    son formas sentenciales, se dice que ' se
deriva inmediatamente de  en G. Esta relación se indica de la siguiente forma:
  ’

Si 1, 2, ... n es una secuencia de formas sentenciales tales que 1  2  ...  n , se dice que n es
derivable de 1 . Esta relación se indica de la siguiente forma:
1 n

La secuencia 1  2  ...  n se denomina derivación de n a partir de 1 de acuerdo a la gramática G.

Definición: El lenguaje L(G) generado a partir de la gramática formal G es el conjunto de strings terminales
derivables a partir de 
L(G) = {   T* /  }

Si   L(G), decimos que  es un string, una sentencia o una palabra en el lenguaje generado por G.

De acuerdo a las definiciones, cada producción de una gramática formal es una regla gramatical que
permite la modificación de una forma sentencial mediante la sustitución de una letra particular que denota
una frase por un string arbitrario.
La aplicación de la producción  A      para re-escribir la forma sentencial i como i+1 se ilustra
a continuación.

La aplicación de esta producción muestra dos generalizaciones que van más allá de los conceptos
empleados en los ejemplos:
1. La sustitución está condicionada a que A aparezca en un contexto particular definido por los strings  y 
de la producción.
2. El string  que sustituye a A puede ser el string vacío. Esto permite el borrado arbitrario de ciertos
símbolos no terminales mediante la aplicación de una producción, y, en consecuencia, que una forma
sentencial decrezca en longitud en una etapa de derivación.

Sin embargo, parecería que es posible definir clases de gramáticas más generales. Por ejemplo,
podríamos permitir producciones del tipo    en las cuales
 puede ser cualquier string no vacío que pertenezca al conjunto (N  T)* - {  } y
 puede ser cualquier string –inclusive vacío– perteneciente a (N  T)*

Las gramáticas en las cuales se permite este tipo de reglas se denominan gramáticas o sistemas Semi-
[Anexo]
Thue . Se puede demostrar que estas gramáticas no son más generales que aquellas que verifican
nuestra primera Definición. Es decir, estas reglas aparentemente irrestrictas no permiten la representación
de ningún lenguaje que no pueda ser representado mediante nuestras gramáticas.

En virtud de la equivalencia entre las formas irrestrictas de producciones y aquellas que son permitidas
por nuestras definiciones, nos vamos a permitir el uso de producciones similares a
AB  BA
cuando esto sea conveniente en discusiones formales. Si quisiéramos cumplir estrictamente con lo que
determina la Definición de gramática formal sería necesario emplear las siguientes cuatro producciones
AB  XB
XB  XY
XY  BY
BY  BA
donde X e Y no se emplean en ningún otro lugar de la gramática.

14
Ejemplo 1: Sea G1 definida mediante N = { A, B, C } , T = { a, b, c } y el siguiente conjunto de producciones:
A CB  BC cC  cc
A  aABC bB  bb
A  abC bC  bc

Derivación de a1b1c1 Derivación de a2b2c2 Derivación de a3b3c3

Por inducción puede verse que las palabras akbkck pertenecen a L(G1)  k  1, y que estas son las
únicas palabras que pertenecen al lenguaje. Luego
L(G1) = { akbkck / k  1 }

Si seguimos la convención de utilizar letras mayúsculas para denotar símbolos no terminales y letras
minúsculas para símbolos terminales, el alfabeto de una dada gramática queda explicitado a partir de la lista
de producciones, y por lo tanto no es necesario especificarlo en forma separada.

Ejemplo 2: Sea G2 una gramática que es una modificación de la gramática G1


G2:   A CB  BC
A  aABC bB  bb
A  abC bC  b

Derivación de ab Derivación de a2b2 Derivación de a3b3

Por inducción puede verse que las palabras akbk pertenecen a L(G2)  k  1, y que estas son las únicas
palabras definidas en el lenguaje. Luego
L(G2) = { akbk / k  1 }

15
La última regla bC  b elimina todas las C's de las derivaciones, y es sólo esta regla la que sustrae todos
los C no terminales de una dada derivación.

Ejemplo 3: Una gramática más simple que genera un lenguaje de tipo { akbk / k  1 } es la gramática G3
G3:   S
S  aSb
S  ab

Derivación de ab:   S  ab

Derivación de a2b2:   S  aSb  aabb

Derivación de a3b3:   S  aSb  aaSbb  aaabbb

Luego, por inducción se verifica que L(G3) = { akbk / k  1 }

No es obvio bajo ningún punto de vista que las gramáticas G 2 y G3 generen el mismo lenguaje. En
realidad, no existe ningún procedimiento general que permita determinar cuándo ciertas gramáticas
arbitrarias generan el mismo tipo de lenguaje.

4. Tipos de Gramáticas

Mediante la restricción de la forma de las producciones permitidas en una dada gramática, es posible
especificar cuatro tipo importantes de gramáticas formales, con sus correspondientes clases de lenguajes.
La tabla siguiente muestra estas restricciones:

En esta tabla se verifica que:


AN{}
, ,   (N  T)*
BN
aT

Moviéndonos en la tabla, de arriba hacia abajo, la forma de las producciones permitidas es cada vez más
restringida. Es obvio que la gramática de un dado tipo pertenece también a los tipos de gramáticas ubicados
más arriba en la tabla. Empleando la notación G i para indicar una clase de gramáticas de tipo i, tendremos
G 0G 1G 2G 3
En consecuencia, cada gramática debe poder generar lenguajes que sean subclases de los lenguajes
generados por cualquier gramática que se encuentra en estratos superiores de la tabla. Si L i denota la
clase de lenguajes generados por las gramáticas incluidas en la clase G i , luego
L 0L 1L 2L 3
Con el conocimiento que disponemos hasta el presente no podemos concluir que estas inclusiones sean
inclusiones propias. Esto lo verificaremos más adelante a medida que avancemos en el estudio de las
gramáticas y los lenguajes.

16
4.1. Gramáticas no restringidas. Tipo 0

Al no tener restricciones, esta clase de gramáticas formales tiene una tremenda generalidad. El agregado
de formas más generales de reglas de reemplazo no agrega nada a la fuerza descriptiva que poseen estas
gramáticas. Conjuntos de strings que requieran de esta generalidad para su representación no presentan
gran interés como lenguajes artificiales. Sin embargo estos sistemas de reglas de sustitución son de gran
interés para los lógicos en los estudios del proceso de deducción.
Debe notarse que la aplicación de la regla gramatical  A      en la etapa de derivación i  i+1
produce una nueva forma sentencial i+1 que tiene una menor longitud que i. Estas reglas se conocen
como producciones que producen contracción.
La gramática del Ejemplo 2, tiene una regla de este tipo: bC  b; una gramática que tiene al menos una
de estas producciones se conoce como gramática que produce contracción. Sólo las gramáticas de este tipo
pueden tener estas características.

4.2. Gramáticas Sensibles al Contexto. Tipo 1

Una gramática sensible al contexto G = (N, T, P,  ) es una gramática formal en la cual todas las
producciones son de la forma  A      con   
Esta gramática puede también contener la producción   . Si G es una gramática sensible al contexto
o de tipo 1, luego L(G) es un lenguaje sensible al contexto (tipo 1).
Una producción    que satisface |  |  |  | se conoce como producción que no produce contracción.
Se requiere que cada producción de esta gramática no produzca contracción.
En consecuencia, las formas sentenciales en cualquier derivación sensible al contexto
1  2  ...  n
no decrecen en longitud
| 1 |  | 2 |  ...  | n |
De aquí se desprende que, si G es una gramática sensible al contexto,   L(G) sí y sólo sí G contiene la
producción   . Sin esta producción, no sería posible generar el string vacío y tendríamos por lo tanto una
teoría de lenguajes formales poco elegante.
La gramática del Ejemplo 1 es sensible al contexto, mientras que la del Ejemplo 2 no lo es pues
contiene la producción bC  b que produce contracción.

4.3. Gramáticas Libres de Contexto. Tipo 2

En una gramática libre de contexto, se requiere que los strings  y , que denotan contexto en la
producción  A     , sean vacíos. Así, la posibilidad de reemplazar una letra o símbolo no terminal en
una forma sentencial, es independiente de los símbolos adyacentes, es decir, es independiente del contexto.
Una gramática libre de contexto G = (N, T, P, ) es una gramática formal en la cual todas las
producciones poseen la forma A   donde A  N  {  } y   (N  T)* - {  }
La gramática puede también contener la producción   ; nuevamente,   L(G) sí y sólo sí    es
una producción de la gramática.
Si G es una gramática libre de contexto o de tipo 2, luego L(G) es un lenguaje libre de contexto (tipo 2).
La gramática del Ejemplo 3 es libre de contexto, al igual que las correspondientes a los ejemplos vistos
en la introducción.

En una gramática libre de contexto, si A es  o una letra no terminal y   T* es un string derivable a


partir de A, decimos que  es denotado por A, o que  es una frase de tipo A. Emplearemos la notación
L(G, A) = {   T* / A }
para indicar el conjunto de frases de tipo A.
Si una forma sentencial A es derivable a partir de , luego L(G, A)  L(G), pero la inclusión no es válida
en el otro sentido. No es generalmente cierto que L(G, A)  L(G).
Las gramáticas libres de contexto son comúnmente empleadas para representar lenguajes artificiales, en
especial lenguajes de programación.

4.4. Gramáticas Regulares. Tipo 3

Las gramáticas regulares son una clase altamente restringida de las gramáticas libres de contexto en las
cuales el lado derecho de la producción puede contener como máximo un sólo símbolo no terminal.

Una producción de la forma A  aB o A  a donde A  N  {  }, B  N y a  T se denomina producción


lineal por derecha.
17
Una producción de la forma A Ba o A  a donde A  N  {  }, B  N y a  T es una producción lineal
por izquierda.
Una gramática formal es lineal por derecha si contiene solamente producciones lineales por derecha. De
la misma forma, una gramática es lineal por izquierda si contiene sólo producciones lineales por izquierda.
Cada forma de gramática lineal puede contener la producción   . Las gramáticas lineales por derecha e
izquierda se conocen como gramáticas regulares.
Si G es una gramática regular o de tipo 3, luego L(G) es un lenguaje regular (tipo 3).

Ejemplo 4: Sean G1 y G2 dos gramáticas lineales; G1 es una gramática lineal por derecha y G2 lo es por
izquierda. Poseen las siguientes producciones:
G1:   1B G2:   B1
1 1
A  1B A  B1
B  0A B  A0
A1 A1

Se puede verificar que: L(G1) = (10)*1 = 1(01)* = L(G2)




5. Árboles y Diagramas de Derivación

Los ejemplos ya desarrollados muestran cómo una derivación puede ser representada en forma de un
diagrama en árbol que muestra claramente la estructura de una sentencia. Nos interesa ver ahora cómo
estos diagramas en árbol se relacionan con las derivaciones de acuerdo a una gramática formal arbitraria.
Un árbol es interpretado como un grafo dirigido acíclico, en el cual cada nodo está conectado con el nodo
que representa el símbolo distinguido (nodo raíz del árbol) por medio de un camino dirigido único; por
convención se asume que los arcos están siempre dirigidos hacia abajo.
Nodo raíz

Nodos interiores

Nodos hojas

Un nodo dado de un árbol es llamado descendiente de aquellos nodos a partir de los cuales puede ser
alcanzado por un camino dirigido. Así, el nodo raíz no es descendiente de ningún nodo, y los nodos hojas
son aquellos que no poseen descendientes. Todos aquellos nodos que no son ni el nodo raíz ni nodos
correspondientes a hojas se denominan nodos interiores del árbol.
El diagrama en árbol correspondiente a la derivación   con   (N  T)* en una gramática libre de
contexto es un árbol en el cual el nodo raíz se identifica como , los nodos interiores se asocian con
símbolos pertenecientes a N y los nodos hojas se asocian con las letras de la forma sentencial . Para
construir el diagrama en árbol se comienza con un árbol simple, que consiste sólo del nodo , y que se va
extendiendo en cada etapa de la derivación. En cada etapa de esta construcción, los nodos hojas del árbol
(leídos de izquierda a derecha) serán identificados con las letras de las formas sentenciales
correspondientes. Consideremos una etapa de derivación arbitraria:
 A    B1 B2 ..... Bn 
en la cual se aplica la producción A  B1 B2 ..... Bn donde A  N  {} y Bi  (N  T)
Antes de aplicar esta producción, estamos en presencia de un árbol cuyos nodos hojas están indicados
con las letras  A . Al aplicar la producción, el nodo A se transforma en un nodo interior que tiene como
descendientes a los nodos B1 B2 ..... Bn.
Luego, el diagrama en árbol resultante tiene los nodos hoja  B1 B2 ..... Bn .

 A   

B1 B2 ..... Bn

18
Ejemplo 5: Consideremos la gramática libre de contexto G
G: S T  (S)
S  ST T()
ST

El lenguaje generado por G se conoce como lenguaje de paréntesis Lp y consiste de todos los strings
bien conformados que corresponden a juegos de paréntesis abiertos y cerrados que pueden surgir en
expresiones matemáticas bien escritas. Por ejemplo, veamos la derivación correspondiente al string ( ) ( ( ) ).

La gramática G en realidad permite diez derivaciones distintas del string terminal ( ) ( ( ) ). Todas ellas se
corresponden con el mismo árbol de derivación, pero representan distintos órdenes de llevar a cabo las
sustituciones indicadas en el siguiente diagrama de derivación; cada camino desde la raíz hasta la parte
inferior del diagrama es una derivación válida del string ( ) ( ( ) ).

Los árboles de derivación son medios apropiados de representar la estructura de sentencias de acuerdo
con una gramática libre de contexto, dado que la asociación de un string terminal con cada símbolo no
terminal de una forma sentencial es completamente independiente de las otras letras en la forma. Esto es, si
A1 A2 ... Ai ... An Ai  N  T
es una forma sentencial, la aplicación de una producción correspondiente a una gramática libre de contexto
para reescribir la letra Ai no puede eliminar ni crear posibles sustituciones para los otros símbolos no
terminales de la forma sentencial.
Luego, el orden en el cual se reemplazan los símbolos no terminales en una derivación libre de contexto
no afecta los resultados a obtener. Esto no es generalmente cierto para una gramática sensible al contexto,
dado que la aplicación de una producción puede modificar el contexto requerido para la aplicación de otra
producción. Por estas razones, preferimos el uso de derivaciones como nuestra herramienta básica en el
estudio formal de las gramáticas, y dejar los árboles de derivación sólo con fines ilustrativos.
En el caso de las gramáticas regulares, no existe manera de derivar a partir de  una forma sentencial
que contenga más de una letra no terminal. En consecuencia, los árboles de derivación tienen una forma
muy simple.

Ejemplo 6: Sea G la gramática lineal por derecha 1 A
G:   1A A  0B
  0B B  0B 1 A
A  1A B0 0 B
Se puede verificar que con esta gramática se genera el lenguaje L(G) = 1*000*. 0 B
Vemos, por ejemplo, el árbol correspondiente a la derivación del string 110000.
0 B
0 19
Por cierto, una derivación que se efectúa de acuerdo a una gramática lineal por derecha siempre va a
tener la forma vista en el ejemplo anterior. Una derivación debe comenzar con la aplicación de una
producción que tenga la siguiente forma:   ,   a, o   aB. En los dos primeros casos la derivación
se completa inmediatamente; en el tercero, la derivación debe continuar con la aplicación de producciones
que tengan la forma A  aB y finalizar con la aplicación de una producción de la forma A  a. Los mismos
conceptos se aplican a gramáticas lineales por izquierda. Los correspondientes árboles de derivación son:
  t1 t2 … tn 

t1 tn

t2 tn-1
Árbol de derivación Árbol de derivación
correspondiente a tn-1 t2 correspondiente a
una gramática lineal una gramática lineal
por derecha tn t1 por izquierda

6. Ambigüedad

Según vimos con anterioridad, una misma sentencia puede ser generada mediante múltiples
derivaciones diferentes en una gramática dada; esto no causa problemas siempre que todas ellas se
correspondan a un único árbol de derivación. El problema de la ambigüedad surge cuando, para
determinada sentencia, la gramática provee árboles de derivación distintos.
Cuando el significado de una sentencia perteneciente a cierto lenguaje depende de su estructura, la
ambigüedad será causa de conflictos. Por ejemplo, en un lenguaje natural como el castellano, la frase “Juan
vio a un hombre con un telescopio” es ambigua.
La ambigüedad es importante en el análisis de lenguajes dado que el análisis de una sentencia ambigua
puede conducir a distintas asignaciones de significado. Otro ejemplo de ambigüedad en el castellano es el
siguiente:
Frase Frase

Frase Nominal Frase Verbal

Sustantivo Adjetivo Verbo Objeto

cuento cien cuento Adjetivo

cien
Este tipo de ambigüedad es el que dificulta el proceso de traducción automática del lenguaje natural.

En un lenguaje de programación, la expresión algebraica a*b+c puede presentar problemas similares de


interpretación por parte de un compilador, si se emplea una gramática diseñada sin cuidado. Por ejemplo, la
siguiente gramática es ambigua, puesto que permite construir dos árboles de derivación diferentes, los
cuales no dejan claro si su evaluación es (a*b)+c o a*(b+c):
EE+E|E*E|(E)|I E E
Ia|b|c
E + E E * E
La interpretación usual (a*b)+c corresponde al primer diagrama,
E * E c a E + E
pero el segundo también representa una derivación válida según la
gramática. La misma expresión adquiere distinta significación; de a b b c
acuerdo a un sentido semántico, el segundo diagrama es erróneo.

Estamos interesados en determinar si una gramática genera sentencias ambiguas; luego, debemos
definir precisamente lo que se entiende por sentencia ambigua o por gramática ambigua. Si bien la noción
de ambigüedad se aplica a todas las clases de gramáticas formales, nos vamos a limitar a la clase de
gramáticas libres de contexto debido a que han probado ampliamente su utilidad en la representación de
lenguajes de programación.

Definición: Sea G una gramática libre de contexto y sea   L(G); la sentencia  es ambigua si existen
derivaciones de  que se corresponden con árboles de derivación diferentes. Una gramática G es ambigua
si L(G) contiene una sentencia ambigua.

20
Si una gramática es ambigua, posiblemente (no necesariamente) existe una gramática no ambigua que
genere el mismo lenguaje; sin embargo, no existe un algoritmo que permita decidir si tal gramática existe.
Un lenguaje L es inherentemente ambiguo si todas las gramáticas que lo generan son ambiguas; igual
que en el caso anterior, no hay manera de decidir si un lenguaje es inherentemente ambiguo.
i j k
Por ejemplo, el lenguaje L = { a b c / i=j o j=k } es inherentemente ambiguo. Toda gramática para tal
lenguaje proveerá un árbol de derivación cuando i=j y otro cuando j=k; en consecuencia, todo string con
i=j=k tendrá dos árboles de derivación distintos.

Ejemplo 7: Consideremos la siguiente gramática libre de contexto, y el string  = ababab  L(G).

G: S
S  SS
S  ab

La sentencia ababab es ambigua, y por lo tanto la gramática G también lo es. Las derivaciones se
corresponden con dos árboles de derivación diferentes, y si éstos hubiesen sido empleados para darle
significado al string generado, hubieran ocurrido interpretaciones erróneas.

La siguiente figura muestra en forma de diagrama de derivación todas las derivaciones posibles del string
ababab de acuerdo a la gramática dada.

A partir de la forma sentencial SS, el diagrama muestra


dos caminos en los cuales el string S ubicado más a la
izquierda es reemplazado primero. El camino 1 corresponde
al árbol (a), mientras que el 2 corresponde al árbol (b).

Estos dos caminos tienen una propiedad única: En cada


etapa de la derivación se reescribe el símbolo no
terminal ubicado más a la izquierda en la forma
sentencial. Estas derivaciones, conocidas como
derivaciones por izquierda, permiten una definición precisa
de las gramáticas ambiguas.

Definición: Sea G una gramática libre de contexto. Una derivación


0  1  2  ...  n
es una derivación por izquierda sí y sólo sí el símbolo no terminal ubicado más a la izquierda de i es
reemplazado para poder obtener i+1. Esto se verifica para 0  i < n.
i =  A  i+1 =      T*, A  N,   (N  T)* y A    P

Partiendo de un árbol de derivación del string  = t1 t2 ... tn (ti  T), es posible construir una única
derivación por izquierda siguiendo el camino desde el nodo raíz al nodo hoja indicado por t i (para i = 1, 2, ...,
n) mediante la aplicación de la producción asociada con cada nodo.
Los nodos interiores de los árboles del Ejemplo 7 han sido numerados para indicar el orden en que las
producciones se aplican en la derivación por izquierda.
A la inversa, es elemental obtener un árbol de derivación único a partir de cualquier derivación. Vemos
entonces que existe una correspondencia uno a uno entre árboles de derivación y derivaciones por
izquierda. Cada oración generada por una gramática libre de contexto y asociada con un dado árbol, tiene
una única derivación por izquierda.

Definición: Una gramática libre de contexto G es ambigua sí y sólo sí genera una dada sentencia a partir de
dos o más derivaciones por izquierda distintas.

La gramática del Ejemplo 7 es ambigua, mientras que las gramáticas de los Ejemplos 3 y 5 no lo son.

21
Hay dos maneras por las cuales puede surgir ambigüedad en una gramática libre de contexto:
1. Alguna sentencia tiene dos árboles de derivación estructuralmente diferentes.
2. Alguna sentencia tiene dos árboles de derivación estructuralmente similares pero con diferentes rótulos
en sus nodos interiores.

Estos dos casos se ilustran en los siguientes ejemplos.

Ejemplo 8: (Caso de Ambigüedad estructural)


G:   A
A  A0A
A1

 = 10101 posee dos árboles de derivación


distintos.

Ejemplo 9: (Caso de Ambigüedad asociada a rótulo)


G:   A B  B0
A  B0 A1
A  A0 B1

 = 100 posee –al menos– dos árboles de derivación, con


idéntica estructura, pero con distintos símbolos no
terminales asociados a los nodos interiores.

Dado que en una gramática regular cada forma sentencial de una derivación puede contener solamente
un único símbolo no terminal, la gramática será ambigua sólo si los árboles de derivación tienen distintos
rótulos en los nodos interiores.

[Anexo]
Sistemas Semi-Thue
Let us start with some basic definitions. For a finite alphabet ,  will denote the set of all words over the alphabet 

where  presents the empty word, i.e., the word of length zero.  will denote the identity on . A semi-Thue system T

over  is a subset of   . The elements (l,r) of T are called rules and will often be written as l  r. The single-step

reduction relation on  induced by a semi-Thue system T is defined as follows: For any u,v in , u v if and

only if there exist x,y in  and (l,r) in T such that u  xly and v  xry. The reduction relation on  induced by T is the

reflexive transitive closure of and is denoted by . [...] If u v holds then one says that u reduces to

v. In case u has no descendant except itself it is called irreducible.

ZCA Kaiserslautern
Zentrum für Computeralgebra (Centre for Computer Algebra) - University of Kaiserslautern
Introducing Reduction to Polycyclic Group Rings - A Comparison of Methods
http://www.mathematik.uni-kl.de/~zca/Reports_on_ca/09/paper_html/node2.html

22

S-ar putea să vă placă și