miércoles, 18 de mayo de 2011

UNIDAD 5: ANALISIS SEMANTICO



ANÁLISIS SEMÁNTICO

5.1  FUNCION DEL ANÁLISIS SEMÁNTICO

Los programas que se compilan no son solamente cadenas de símbolos sin ningún significado que pueden ser aceptadas como correctas o no por una máquina abstracta. El lenguaje no es más que el vehículo por el cual se intenta transmitir una serie de instrucciones a un procesador para que éste las ejecute produciendo unos resultados. Por ello, la tarea del compilador requiere la extracción del contenido semántico incluido en las distintas sentencias del programa.
Por todo ello, se hace necesario dotar al compilador de una serie de rutinas auxiliares que permitan captar todo aquello que no se ha expresado mediante la sintaxis del lenguaje y todo aquello que hace descender a nuestro lenguaje de programación de las alturas de una máquina abstracta hasta el nivel de un computador real. A todas estas rutinas auxiliares se les denomina genéricamente análisis semántico.
El análisis semántico, a diferencia de otras fases, no se realiza claramente diferenciado del resto de las tareas que lleva a cabo el compilador, más bien podría decirse que el análisis semántico completa las dos fases anteriores de análisis lexicográfico y sintáctico incorporando ciertas comprobaciones que no pueden asimilarse al mero reconocimiento de una cadena dentro de un lenguaje.
La fase de análisis semántico revisa el programa fuente para tratar de encontrar errores semánticos y reúne la información sobre los tipos para la fase posterior de generación de código. En ella se utiliza la estructura jerárquica determinada por la fase de análisis sintáctico para identificar los operadores y operandos de expresiones y proposiciones.
Un componente importante del análisis semántico es la Verificación de Tipos. Aquí, el compilador verifica si cada operador tiene operandos permitidos por la especificación del lenguaje fuente.

Por ejemplo, las definiciones de muchos lenguajes de programación requieren que el compilador indique un error cada vez que se use un número real como índice de una matriz. Sin embargo, la especificación del lenguaje puede permitir ciertas coerciones a los operadores.


Para más información consulta:

5.2 REGLAS SEMANTICAS

Un lenguaje está formado por reglas semánticas que proporcionan el significado a una sentencia o instrucción del lenguaje. Las reglas semánticas vienen dadas en función de los atributos de los demás símbolos que componen la regla. La evaluación de las reglas semánticas puede generar código, guardar información en una tabla de símbolos, emitir mensajes de error o realizar otras actividades. La traducción de la cadena de componentes léxicos es el resultado obtenido al evaluar las reglas semánticas. La evaluación de las reglas semánticas define los valores de los atributos en los nodos del árbol de análisis sintáctico para la cadena de entrada. Una regla semántica también puede tener efectos colaterales, por ejemplo, imprimir un valor o actualizar una variable global.

Para más información consulta:
5.3 COMPATIBILIDAD DE TIPOS

Un compilador debe comprobar si el programa fuente sigue tanto las convenciones sintácticas como las semánticas del lenguaje fuente. Esta comprobación, llamada comprobación estática (para distinguirla de la comprobación dinámica que se realiza durante la ejecución del programa objeto), garantiza la detección y comunicación de algunas clases de errores de programación. Los ejemplos de comprobación estática incluyen: 
  1. Comprobaciones de tipos. Un compilador debe informar de un error si se aplica un operador a un operando incompatible, por ejemplo, si se suman una variable tipo matriz y una variable de función. 
  2. Comprobaciones del flujo de control. Las proposiciones que hacen que el flujo del control abandone una construcción deben tener algún lugar a dónde transferir el flujo de control Por ejemplo, una proposición break en C hace que el control abandone la proposición que la engloba, while, for o switch más cercana si dicha proposición englobadora no existe, ocurre un error. 
  3. Comprobaciones relacionadas con nombres. En ocasiones, el mismo nombre debe aparecer dos o más veces en un mismo bloque de instrucciones, el compilador debe comprobar que se utilice el mismo nombre en ambos sitios.
Un comprobador de tipos se asegura de que el tipo de una construcción coincida con el previsto en su contexto.

Puede necesitarse la información sobre los tipos reunida por un comprobador de tipos cuando se genera el código.
Para más información consulta:

5.4 SISTEMAS DE TIPOS

El diseño de un comprobador de tipos para un lenguaje se basa en información acerca de las construcciones sintácticas del lenguaje, la noción de tipos y las reglas para asignar tipos a las construcciones de lenguaje. En Pascal y en C, los tipos son básicos o construidos. Los tipos básicos son los tipos atómicos sin estructura interna por lo que concierne al programador (bolean, carácter, integer, real). Los tipos de subrango como 1.10, y los tipos enumerados, como (violeta, índigo, azul, verde, amarillo, naranja) se pueden considerar como tipos básicos. Además, los apuntadores y las funciones también pueden considerarse como tipos construidos.

Expresiones de tipos 
El tipo de una construcción de un lenguaje se denotará mediante una “expresión de tipo”. De manera informal, una expresión de tipo es, o bien un tipo básico o se forma aplicando un operador llamado constructor de tipos a otras expresiones de tipos. Los conjuntos de tipos y constructores básicos dependen del lenguaje que deba comprobarse. Cada lenguaje de programación requerirá unas expresiones de tipos adecuadas a sus características. A continuación, a modo de ejemplo, se definen las expresiones de tipos más comunes:
  •      Tipos simples: Son expresiones de tipos los tipos simples del lenguaje, y algunos tipos especiales:
    •       Integer 
    •       Real 
    •       Char 
    •       Boolean 
    •       Void 
    •       Error
Los cuatro primeros son los tipos de datos simples más comunes en los lenguajes de programación, los dos últimos son tipos simples especiales que usaremos para su atribución a diversas partes de un programa, a fin de homogeneizar el tratamiento de todo el conjunto mediante el método de las expresiones de tipos.

Tomando el lenguaje C como ejemplo, el segmento de código al que está asociada la expresión de tipos integer es aquella en que aparece la palabra reservada int, etc.
  •      Constructores de tipos: Permiten formar tipos complejos a partir de otros más simples. La semántica de cada lenguaje tiene asociada unos constructores de tipos propios. En general, en los lenguajes de programación se definen los siguientes constructores:
  •      Matrices: Si T es una expresión de tipos, entonces array(R,T) es también una expresión de tipos que representa a una matriz de rango R de elementos de tipo T.  Ejemplo: Sea el segmento de código C:  char a[10] 
  •      Productos: Sea T1 y T2 expresiones de tipos, T1 x T2 es una expresión de tipos que representa al producto cartesiano de los tipos T1 y T2. A fin de simplificar consideraremos que el constructor u operador de tipos x es asociativo por la izquierda. 
  •      Registros: Sea un registro formado por los campos u1, u2 ... uN, siendo cada uno de ellos de los tipos T1,T2 ... TN, entonces, la expresión de tipos asociada al conjunto es: record ( (u1:T1) x (u2:T2) x ... x (uN:TN) ) 
  •    Punteros: Si T es una expresión de tipos, entonces pointer(T) es una expresión de tipos que representa a un puntero a una posición de memoria ocupada por un dato de tipo T. 
  •    Funciones: Sean T1,T2 ... TN, las expresiones de tipos asociadas a los segmentos de código correspondientes a los argumentos de una función, y sea T el tipo devuelto por la función. Entonces, la expresión de tipos asociada a la función es:     ((T1xT2 x... xTN) -> T )
Las expresiones de tipo pueden contener variables cuyos valores son expresiones de tipos.

 Sistemas de tipos
 
Un sistema de tipos es una serie de reglas para asignar expresiones de tipos a las distintas partes de un programa y verificar su corrección. En concreto, formarán el sistema de tipos las definiciones y reglas que permiten comprobar cuál es el dominio asignado a una variable, y en qué contextos puede ser usada. Cada lenguaje tiene un sistema de tipos propio, aunque a veces puede variar de una a otra implementación. Un sistema de tipos incluye tanto comprobaciones estáticas como dinámicas, según si se realizan en tiempo de compilación o en tiempo de ejecución. Como es lógico, mientras más comprobaciones puedan realizarse en la fase de compilación, menos tendrán que realizarse durante la ejecución, con la consiguiente mejora en la eficiencia del programa objeto. En la práctica ningún lenguaje es tan fuertemente estructurado que permita una completa comprobación estática de tipos.

Para más información consulta:

5.5 COMPROBACION ESTATICA Y DINAMICA DE LOS TIPOS

Se dice que la comprobación realizada por un compilador es estática, mientras que la comprobación hecha al ejecutar el programa objeto se denomina dinámica.  En principio, cualquier verificación se puede realizar dinámicamente, si el código objeto carga el tipo de un elemento con el valor de dicho elemento.
Un sistema de tipos seguro elimina la necesidad de comprobar dinámicamente errores de tipos ya que permite determinar estáticamente que dichos errores no pueden ocurrir cuando se está ejecutando el programa objeto. Se dice que un lenguaje es fuertemente tipificado si su compilador puede garantizar que los programas que acepte se ejecutarán sin errores de tipo.

Para más información consulta: ANALISIS SEMANTICO

Recuperación de errores
Un sistema de tipos seguro elimina la necesidad de comprobar dinámicamente errores de tipos ya que permite determinar estáticamente que dichos errores no pueden ocurrir cuando se está ejecutando el programa objeto. Se dice que un lenguaje es fuertemente tipificado si su compilador puede garantizar que los programas que acepte se ejecutarán sin errores de tipo.

5.6 COMPROBACIÓN DE TIPOS EN EXPRESIONES, SENTENCIAS Y FUNCIONES

A partir de reglas semánticas se desarrolla la comprobación de tipos para expresiones, sentencias y funciones. El comprobador de tipos es un esquema de traducción que sintetiza el tipo de cada expresión a partir de los tipos de las sub-expresiones. El comprobador de tipos puede manejar matrices, apuntadores, proposiciones y funciones.
Para más información consulta: Análisis-Semántico

5.7 CONVERSIONES DE TIPOS, SOBRECARGA DE FUNCIONES Y OPERADORES, FUNCIONES POLIMÓRFICAS

Conversiones de tipos
Considérense expresiones como x + i donde x es de tipo real e i es de tipo entero. Como representación de enteros y reales es distinta dentro de un computador, y se utilizan instrucciones de máquina distintas para las operaciones sobre enteros y reales, puede que el compilador tenga que convertir primero uno de los operandos de + para garantizar que ambos operandos sean del mismo tipo cuando tenga lugar la suma.
La definición del lenguaje especifica las conversiones necesarias. Cuando un entero se asigna a un real, o viceversa, la conversión es al tipo del lado izquierdo de la asignación. En expresiones, la transformación más común es la de convertir el entero en un número real y después realizar una operación real con el par de operandos reales obtenidos. Se puede utilizar el comprobador de tipos en un compilador para insertar estas operaciones de conversión en la representación intermedia del programa fuente.
Para más información consulta:Análisis-Semántico

Coerciones
La definición del lenguaje especifica las conversiones necesarias. Cuando un entero se asigna a un real, o viceversa, la conversión es al tipo del lado izquierdo de la asignación. En expresiones, la transformación más común es la de convertir el entero en un número real y después realizar una operación real con el par de operandos reales obtenidos. Se puede utilizar el comprobador de tipos en un compilador para insertar estas operaciones de conversión en la representación intermedia del programa fuente.
Sobrecarga de funciones y operadores
Un símbolo sobrecargado es el que tiene distintos significados dependiendo de su contexto. La sobrecarga se resuelve cuando se determina un significado único para un caso de un símbolo sobrecargado. La resolución de la sobrecarga a menudo aparece referida como identificación de operadores porque determina la operación que denota un símbolo de operador.  
Los operadores aritméticos están sobrecargados en la mayoría de los lenguajes. Sin embargo, la sobrecarga que afecta a los operadores aritméticos como + se puede resolver observando únicamente los argumentos del operador.
Para más información consulte el libro: ANALISIS SEMANTICO

Funciones polimórficas
El término “polimórfico” se aplica a cualquier parte de código que pueda ejecutarse con argumentos de tipos distintos, de modo que se puede hablar de funciones, así como de operadores polimórficos. 
Los operadores predefinidos para indicar matrices, aplicar funciones y manipular apuntadores son generalmente polimórficos porque no se limitan a una determinada clase de matriz, función o apuntador.
Para más información consulte el libro: ANALISIS SEMANTICO

Más información acerca de este tema y compiladores 
Compilador





1 comentario:

Vistas a la página totales