Gramáticas - UTN

Anuncio
Sintaxis y Semántica del Lenguaje
Gramáticas
La tarea de proveer una descripción bien concisa y entendible de un lenguaje de
programación es difícil pero esencial para el éxito de un lenguaje.
Uno de los problemas en describir un lenguaje es la diversidad de gente que debe
comprender esas descripciones. Las personas que implementan obviamente deben ser
capaces de determinar cómo se forman las expresiones, sentencias y unidades de
programas y también el orden de ejecución de los mismos. La dificultad del trabajo que
tiene esta gente (los implementadores) está determinado en parte por la claridad y
complejidad de las descripciones del lenguaje.
La sintaxis de un lenguaje de programación describe la forma correcta en la cual
las sentencias, expresiones y unidades de programa se deben escribir, mientras que la
semántica denota el significado de esas sentencias, expresiones y unidades de programa.
Por ejemplo la sintaxis de una sentencia Pascal if then es:
if <condición> then <sentencia>
La semántica de esta sentencia es que si el valor actual de la condición es
verdadero, se ejecutará <sentencia>.
Describir sintaxis es más fácil que describir semántica, ya que existen notaciones
aceptadas universalmente para la descripción de sintaxis y no así de semánticas.
En la definición de un lenguaje de programación la sintaxis usualmente se expresa
en BNF (Backus- Naur Form) y la semántica está expresada en lenguaje natural
(español, inglés, etc).
BNF es un ejemplo de un metalenguaje, es decir, un lenguaje usado para definir
otros lenguajes. Algol 60 fue el primer lenguaje que utilizó BNF para su descripción
sintáctica.
Una gramática consiste de un conjunto de no-terminales, terminales y una serie de
reglas de producción. Un no-terminal está definido en una regla de producción, mientras
que un terminal es un símbolo del lenguaje que se está definiendo. En una regla de
producción el no-terminal (que aparece en la parte izquierda) está definido en términos
de una secuencia de no-terminales y terminales (que se encuentran en la parte derecha )
Ejemplo:
<dígito>::= 0|1|2|3|4|5|6|7|8|9
<letra>::= a|b|c……………|x|y|z
<identificador>::=<letra>|<identificador><dígito>|<identificador> <letra>
En BNF, un no-terminal se escribe entre < >, el símbolo ::= significa “se define
como” y el símbolo “|” significa “o”.
Estas reglas definen <dígitos> como uno de los símbolos 0, 1 al 9; <letra> como
una letra minúscula e <identificador> se define como una única letra, un identificador
seguido de una letra o un identificador seguido de un dígito.
Así, el identificador “ab1” puede ser derivado de <identificador>como sigue:
1
Sintaxis y Semántica del Lenguaje
<identificador>
<identificador><dígito>
<identificador> <letra>><dígito>
<letra><letra><dígito>
a <letra><dígito>
a
b
<dígito>
a
b
1
En cada etapa, el no-terminal de más a la izquierda es reemplazado por la parte
derecha de una de sus reglas de producción, la secuencia de terminales y no-terminales
producidos en cada etapa en una derivación se conoce como formas sentenciales. La
forma sentencia final (que ya no contiene símbolos no-terminales) se conoce como una
sentencia.
La estructura de una derivación se muestra mejor en un árbol de derivación .
El árbol de derivación que muestra cómo ab1 de <identificador> es:
<identificador>
|
<identificador>
<dígito>
|
|
<identificador> <letra>
1
|
|
<letra>
b
|
a
Un lenguaje de programación completo se define comenzando con un símbolo noterminal tal como <programa>, conocido como start symbol (símbolo inicial) y desde el
cual todos los posibles programas pueden ser derivados. En la práctica, los árboles de
derivación se crean de dos formas posibles. En un esquema top-down, la sentencia
requerida se deriva del símbolo inicial tal cual como hicimos en el ejemplo (ab1). En un
esquema bottom-up, el punto de partida es la sentencia requerida, la cual es reducida al
símbolo inicial reemplazando las partes derechas por sus correspondientes partes
izquierdas de las reglas de producción. Ambos esquemas se utilizan en la fase de
análisis sintáctico de muchos compiladores.
Una gramática que define un lenguaje de programación tiene un número finito de
reglas de producción, pero como las reglas de producción contienen recursión, es
posible generar infinitos programas posibles.
Ejemplo:
<ident>::=<ident><dígito>
Cuando se incluye recursión en las reglas de producción hay que tener cuidado.
Hay que asegurarse que la recursión termine. Una regla de producción tal como la
anterior se dice que es recursiva a izquierda. Existen definiciones similares pero
recursivas a derecha.
2
Sintaxis y Semántica del Lenguaje
Ambigüedad
Un problema surge cuando se definen gramáticas ambiguas. Esto es, una
gramática que permite diferentes interpretaciones para la misma sentencia.
Ejemplo: Veamos la siguiente definición para la sentencia condicional if
<sent-cond>::= if <condición> then <sent>
| if <condición> then <sent> else <sent>
<sent>::= <sent-cond> | begin <sent> end
La siguiente sentencia condicional tiene dos diferentes árboles de derivación:
if <cond> then if <cond> then <sent> else <sent>
árbol 1
<sent-cond>
!
if <cond> then <sent>
|
<sent-cond>
|
if <cond> then <sent> else <sent>
árbol 2
<sent-cond>
|
if <cond> then <sent> else <sent>
|
<sent-cond>
|
if <cond> then <sent>
Como hay dos interpretaciones, la gramática es ambigua. Una solución podría ser que la
sentencia anterior se escriba:
if <cond> then
begin
if <cond> then <sent> else <sent>
end
ó
if <cond> then
begin
if <cond> then <sent>
end
else
<sent>
Dependiendo de la interpretación que se desee.
Otra solución sería agregando un cierre del if (ya sea con un ; o con un endif)
3
Sintaxis y Semántica del Lenguaje
Ejemplo: Gramática para una sentencia de asignación
<asig>::= <id>:=<exp>
<id>::= A| B| C |D (también podría ser la definición de identificador anterior)
<exp>::= <exp> + <exp>
| <exp> * <exp>
| <id>
¿Qué pasa con esta gramática? ¿Es correcta? Veamos el árbol de derivación de la
siguiente sentencia.
A:= B + C * D
árbol 1
<asig>
|
<id> := <exp>
|
|
A
<exp> + <exp>
|
|
<id>
<exp> * <exp>
|
|
|
B
<id>
<id>
|
|
C
D
árbol 2
<asig>
|
<id> :=
<exp>
|
|
A
<exp> * <exp>
|
|
<exp> + <exp> <id>
|
|
|
<id>
<id>
D
|
|
B
C
En general la ambiguedad sintáctica de las estructuras de un lenguaje es un
problema, debido a que los compiladores basan la semántica de esas estructuras en su
estructura sintáctica. Si una estructura del lenguaje tiene más de un árbol de derivación,
entonces, el significado de la estructura no podría determinarse unívocamente.
¿Cómo desambiguamos la gramática anterior?
<asig>::= <id> := <exp>
<id>::= A| B| C| D
<exp>::= <exp> + <término> | <término>
<término>::= <término> * <factor> | <factor>
4
Sintaxis y Semántica del Lenguaje
Ejercicio: Hacer el árbol de derivación para la expresión anterior para ver que es único.
Pensar: ¿Cómo se modificaría esta gramática para incluir paréntesis en las expresiones?
Otras notaciones:
Existen otras formas de describir gramáticas (otras notaciones). Por ejemplo, la
siguiente sentencia:
<exp>::= <exp> + <término>
Se escribiría:
expà exp ‘+’ término
Además, existe otra forma más gráfica de representar gramáticas que son diagramas
sintácticos. Son utilizados por los lenguajes Pascal y Ada. La simbología es la siguiente:
No-terminales
terminales
Ejemplo:
El diagrama sintáctico para una expresión sería:
exp
término
término
+
término
factor
factor
*
/
factor
identificador
5
Sintaxis y Semántica del Lenguaje
Ejercicio:
Escribir una gramática para verificar la declaración de variables de un programa Pascal.
Resolución:
Básicamente la sentencia es:
var
a:integer;
b,c:real;
d:alumno;
en gral puede ser una declaración o un conjunto separados por “;”. Y una declaración
puede ser un identificador o un conjunto separados por “,”:
<declaración>::= var <lista-de-dcls>
<lista-de-dcls>::= <unaDeclaración> | <unaDeclaración>; <lista –de-dcls>
<unaDeclaración>::= <lista-de-ident> : <tipo>
<lista-de-ident>::= <ident> |<ident>, <lista-de-ident>
<ident>::= Se definió en la teoría!!!
<tipo>::= ………..no hacerlo, en este caso para acá
Las anteriores son sólo reglas de producción de la gramática. La gramática se
compone además de reglas de:
G={ N, T, P, S}
donde:
T = {var, “,” , “;” , : , etc……}
N = { <declaración>, <lista –de-dcls>, <unaDeclaración>, <lista-de-ident>,
<tipo>, <ident>}
P = { <declaración>::= var <lista-de-dcls>
<lista-de-dcls>::= <unaDeclaración> | <unaDeclaración>; <lista-de-dcls>
<unaDeclaración>::= <lista-de-ident> : <tipo>
<lista-de-ident>::= <ident> |<ident>, <lista-de-ident>
<ident>::= Se definió en la teoría!!!
<tipo>::= ………..no hacerlo, en este caso para acá
}
S = {<declaración>}
6
Descargar