domingo, 15 de mayo de 2011

Introducción al Código Intermedio.

Representación de código intermedio.
Existen maneras formales para representar código intermedio.
Código P.
Triplos.
Cuádruplos.
Estas notaciones simplifican la traducción de nuestro código fuente a nuestro código objeto ya que ahorran y acotan símbolos de la tabla de símbolos
-> Código P
Un tipo de lenguaje interpretado. idiomas P-código son una especie de híbrido, que caen entre lenguajes compilados y lenguajes interpretados de la manera que se ejecuten. Al igual que un lenguaje interpretado, la programación del P-código se convierte en una forma binaria de forma automática cuando se ejecuta, en lugar de tener que ser compilado. Sin embargo, a diferencia de un lenguaje compilado el archivo binario ejecutable se almacena en pseudo-código, no en lenguaje de máquina. Además, a diferencia de un lenguaje interpretado, el programa no tiene que ser convertida a binario cada vez que se ejecute. Después de que se convierte en P-código la primera vez, la versión pseudo-código se utiliza para cada ejecución adicionales.
Código P (y por tanto sus programas) tienden a ser más lento que los lenguajes compilados y programas, pero más rápido que lenguajes interpretados, y por lo general tienen autorización para algunas funciones del sistema operativo de bajo nivel, pero no el acceso directo al hardware. No requieren compiladores a veces costosos, a menudo se incluyen junto con los sistemas operativos, y algunos lenguajes de código p-son más fáciles de programar que lenguajes compilados. Ejemplos de lenguas Pcode son Java, Python y REXX / REXX objetos.
->Triplos
En la historia de los compiladores han sido utilizadas una amplia variedad de representaciones intermedias como lo es la siguiente clase de representación de código intermedio de un árbol de 3 direcciones,2 para los operandos y una para la ubicación del resultado. esta clase incluye un amplio numero de representaciones diferentes entre las cuales encontramos cuadruplos y triples. la principal diferencia entre estas notaciones y la notación postfija es que ellos incluyen referencias explicitas para los resultados de los cálculos intermedios, mientras que la notación posfija los resultados son implícitos al representarlos en una pila.
  • La diferencia entre triples y cuadruplos es que con los triples es referenciado el valor intermedio hacia el numero del triple que lo creo, pero en los cuádruplos requiere que ellos tengan nombre implícitos.
  • Los triples tienen una ventaja obvia de ser mas consistente, pero ellos dependen de su posición, y hacen que la optimización presente cambios de código mucho mas compleja.
Para evitar tener que introducir nombres temporales en la tabla de símbolos, se hace referencia a un valor temporal según la posición de la proposición que lo calcula. Las propias instrucciones representan el valor del nombre temporal. La implementación se hace mediante registros de solo tres campos (op, arg1, arg2).
  • En la notación de tripletes se necesita menor espacio y el compilador no necesita generar los nombres temporales. Sin embargo, en esta notación, trasladar una proposición que defina un valor temporal exige que se modifiquen todas las referencias a esa proposición. Lo cual supone un inconveniente a la hora de optimizar el código, pues a menudo es necesario cambiar proposiciones de lugar.
  • Una forma de solucionar esto consiste en listar las posiciones a las tripletas en lugar de listar las tripletas mismas. De esta manera, un optimizador podría mover una instrucción reordenando la lista, sin tener que mover las tripletas en si
->Cuádruplos
Es una estructura tipo registro con cuatros campos que se llaman:
Operador
Operando1
Operando2
Resultado
Donde operando1, operando2 y resultado pueden ser constantes, identificadores y variables temporales definidos por el compilador mientras que operador representa una operación arbitraria.
Operador
Operando1
Operando2
Resultado
*
C
D
T1
+
B
T1
T2
=
T2
A
EJEMPLO:
A := B + C * D
Esquemas de generación.
¿Que son?
Los esquemas de generación son las estrategias o acciones que deberán realizarse y tomarse en cuenta en el momento de generar código intermedio.
Declaración de variables y constantes.
Las declaraciones de variables y constantes deben separarse de tal manera que queden las expresiones una por una de manera simple.

• Por ejemplo int a,b,c;
se descompone a int a;
int b; intc; respectivamente.
Las variables utilizadas en los programas se clasifican en dos tipos:
variables locales y variables globales.
Variables locales:
Aquella que está declarada para el programa o algoritmo completo.
Para definir variables locales, la definición debe hacerse inmediatamente después de una llave de inicio ({), y la variable deja de existir fuera de la llave de fin(}) que corresponde a la llave de inicio después del cuál fue definida la variable.
Ejemplo:
{
int a,b;
a=5;
b=a + 100;
}
Variables globales:
Aquella que está declarada y definida dentro de una función y sólo es válida dentro de la misma función y no podrá utilizarse en otra parte del programa.
Una variable global se declara fuera de cualquier función y primero que cualquier función que requiera de ella. Una variable se declara de la siguiente forma:
tipo identificador1, identificador2..ident n;
Ejemplos:
Algunos ejemplos de cómo definir variables son los siguientes:
int alumnos, contador;
float A,B,C;
char Letra;
Declaracion de Constantes.
Para declarar constantes en el lenguaje C, sólo basta anteponer el calificador const seguido del tipo de dato que manejará esa constante (int,char,etc), y por último el valor que tomará esa variable.
Ejemplo:
const int k = 12
Estatuto de asignación.
Las operaciones de asignación deben quedar expresadas por una expresión sencilla, si está es compleja se debe reducir hasta quedar un operador sencillo.
Ejemplo:
x = a+b/5; debe quedar de la
forma y = b/5; z = a+y; x=z
Estatuto Condicional.
Las condiciones deben expresarse de manera lo más sencilla posible de tal forma que puedan evaluarse en cortocircuito. Por ejemplo una instrucción como:
if (a == b && f!=5 && f%3==0)
se evalúa primero x = (a==b && f!=5) y = x && f%3==0; if (y)
Las instrucciones de decisión compleja como switch se reducen a una versión complejas de if’s
Son por así decirlo las normas que regulan nuestras expresiones regulares, (if,then, else) donde una expresión condicional nos permite elegir que pautas deben ser encontradas, en base a una condición.
Nuestros programas pueden tomar decisiones sirviéndose de las expresiones condicionales.
Una expresión condicional responde a los valores de True o False y de acuerdo al resultado podemos decidir si ejecutar o no un pedazo de código o simplemente no hacer nada.
if (expresion condicional) {
bloque de código}
if (expresion condicional) {
bloque de código ; }
else {
bloque de código ;
}
Estatus condicional.
Ejecutara instrucciones dependiendo del valor de una condición que resulta en un dato tipo boolean.
Ejemplo:
If Expresión lógica Then
Instrucciones Afirmativo
Else
Instrucciones negativo
End If
Hay otra forma, abreviada, de escribir una sentencia if, aunque es importante observar que esta expresión no sólo compara valores, sino que también asigna un valor a una variable. su sintaxis es:
Variable = (condición) ? valor1 : valor2
Esta expresión emplea el operador ? para asignar valores a una variable. Si la condición resulta verdadera entonces asigna valor1, de lo contrario asignará valor2.
var sCondición = (user = "Juan") ? "Jefe" : "Empleado" ;
Estatuto de Ciclos.
Los ciclos se descomponen en un ciclo genérico, por lo que ciclos «while», «for» y «do-while» tienen la misma representación interna.
Todo queda en forma de «while»
Ejemplo:
While (A>B) and (A<=2*B-5) do A:= A+B
el Código intermedio generado será:
L1: if A>B goto L2
goto L3
L2: T1:= 2*B
T2:= T1-5
if A
goto L3
L4: A:= A+B
goto L1
L3: . . .
Arreglos.
Los Arreglos se descomponen en estructuras básicas de manejo de manera simple:
Ejemplo:
char a= ‘Hola’;
Se reduce a:
a[0]=‘H’; a[1]=‘o’; a[2]=‘l’; a[3]=‘a’;
Funciones.
Las funciones pueden reducir a en línea. Lo que se hace es expandir el código original de la función.
Las funciones se descomponen simplificando los parámetros de manera individual al igual que el valor de retorno.
Ejemplo:
cuadrado(); //Declaramos la función
// regresar el cuadrado de un número
double cuadrado(double n)
{
return n*n;
}
Generación de código intermedio: Lenguajes intermedios.
Optimización de Código.
Mejorar el código intermedio, de modo que resulte un código de máquina más rápido de ejecutar.
->Administrador de la tabla de símbolos:
El administrador de la tabla de símbolos se encarga de manejar los accesos a la tabla de símbolos, en cada una de las etapas de compilación de un programa.
->Manejador de errores:
En cada fase del proceso de compilación es posibles encontrar errores.
Es conveniente que el tratamiento de los errores se haga de manera centralizada a través de un manejador de errores.
De esta forma podrán controlarse más eficientemente los errores encontrados en cada una de las fases de la compilación de un programa.
[…]
Notaciones.
Sistema convencional de signos de una determinada disciplina
Para nosotros es una forma especial en la que se pueden expresar una expresión matemática en tres formas: Infija, Prefija y Posfija.
->Infija.
Es la notación común de fórmulas aritméticas y lógicas, en la cual se escriben los operadores entre los operandos en que están actuando (ej. 2 + 2) usando un estilo de infijo
En la notación de infijo, a diferencia de las notaciones de prefijo o posfijo, es necesario rodear entre paréntesis a los grupos de operandos y operadores, para indicar el orden en el cual deben ser realizadas las operaciones. En la ausencia de paréntesis, ciertas reglas de prioridad determinan el orden de las operaciones.
->Notacion Polaca.
También conocida como notación de prefijo o notación prefija, es una forma de notación para la lógica, la aritmética, y el álgebra. Su característica distintiva es que coloca los operadores a la izquierda de sus operandos. Si la aridad de los operadores es fija, el resultado es una sintaxis que carece de paréntesis u otros signos de agrupación, y todavía puede ser analizada sin ambigüedad.
La notación de prefijo ha visto una amplia aplicación con las S-expressions, donde son requeridos los paréntesis debido a los operadores aritméticos que tienen aridad variable. El lenguaje de programación Ambi usa la notación polaca para operaciones aritméticas y la construcción del programa. La posfija notación polaca inversa es usada en muchos lenguajes de programación basados en pila como PostScript, y es el principio de operación de ciertas calculadoras, notablemente las de Hewlett-Packard.
Aunque sea obvio, es importante observar que el número de operandos en una expresión debe igualar al número de operadores más uno, de lo contrario la sentencia no tiene ningún sentido (asumiendo que solamente son usados operadores binarios en la expresión). Esto puede ser fácil de pasarlo por alto cuando se trata con expresiones más largas y más complicadas con varios operadores, así que se debe tener cuidado de comprobar con minuciosidad que una expresión tiene sentido al usar la notación de prefijo.
Prefija:
Se evalúa de izquierda a derecha hasta que encontrémosle primer
operador seguido inmediatamente de un par de operandos.
->Notacion Polaca Inversa
Notación Polaca Inversa, Notación de Posfijo o Notación Posfija
Método algebraico alternativo de introducción de datos."
Introducida en 1920 por el matemático polaco Jan Lukasiewicz, en donde cada operador está antes de sus operandos.
El esquema polaco inverso fue propuesto en 1954 por Burks, Warren, y Wright y reinventado independientemente por Friedrich L. Bauer y Edsger Dijkstra a principios de los años 1960.
Es frecuentemente usada en lenguajes deprogramación concatenativos y basados en pila. También es común en sistemas basados en flujo de datos y tuberías, incluyendo las tuberías de Unix.
Su funcionamiento
Su principio es el de evaluar los datos directamente cuando se introducen y manejarlos dentro de una estructura LIFO (Last In First Out), lo que optimiza los procesos a la hora de programar.
El orden de los operandos es importante cuando se manejan operadores no conmutativos (como la resta o la división), así, si dividimos 10 entre 2, por ejemplo, en las tres notaciones se debe escribir de la siguiente manera: "10 / 2", "/ 10 2", "10 2 /".
Operadores
* / + - ^
Operandos
a b c...
1 2 3 4...
Cada operador se escribe detrás (a la derecha) de sus operandos.
Ventajas De La Notacion Posfija
1) En notación postfija nunca son necesarios los paréntesis.
2) En notación postfija no es necesario definir prioridades entre operadores.
3) Una expresión postfija puede evaluarse de forma muy sencilla.

Autor: Luis Eduardo Fernandez Rocha (Contacto Linkedin)

No hay comentarios.:

Publicar un comentario