Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Introduccion
La conversión del PF hacia un PO equivalente implica varios retos debido a las diferencias que
generalmente tienen ambos lenguajes. En el caso de un compilador habrán diferencias
significativas entre el LF, típicamente de alto nivel, y el LO, lenguaje de maquina.
Este anidamiento produce estructuras modulares, las cuales resaltamos en el PF a través del uso
de espacios en blanco, para visualizar mejor las estructuras.
En el Lenguaje de máquina, en cambio, todas las instrucciones son simples, cada una de ellas
ocupa una sola línea y no comprende ni está comprendida dentro de otra instrucción.
Tambien hay que considerar la relatividad de lo que es simple, por ejm en el alto nivel una
instrucción simple e indivisible es x = a + b * c , pero en el código de maquina esa es una
instrucción compuesta de 3 operaciones básicas (multiplicación, suma, asignación)
Linealizacion
Es el proceso por el cual rompemos la estructura modular para obtener una estructura lineal en su
lugar. Para esto tenemos que obtener “equivalencias estructurales” para las instrucciones de
control, las cuales son las que generan la estructura modular.
Definimos entonces las operaciones básicas, no estructuradas, de control que nos permitirán
plantear dichas equivalencias.
a=1;
b=3;
if ( b ¡= 1) goto E1
a++
goto E2
E1: b++
E2: cout << a << b // imprimirá 1, 4
Equivalencias Estructurales
1. Condicionales
if (cond) if ( ! cond ) goto E1
<MV> <MV>
else ---- > goto E2
<MF> E1: <MF>
E2:
2. Repetitivos
do E1: <MR>
<MR> ---- > if (cond) goto E1
while (cond)
3. Alternativo
Caso x vx = valor(x)
valor1 : modulo1 if (vx == valor1) goto E1
valor2 : modulo2 if (vx == valor2) goto E2
… …. ------ > ……..
valor_n : modulo_n if (vx == valor_n) goto En
sino modulo_y goto Ez
FinCaso E1: modulo1
goto fin
E2: modulo2
goto fin
…………..
En: modulo_n
goto fin
Ez: modulo_y
fin:
Ejemplos:
1. Linealizar el PF
int a, b=1;
while ( b < 8 ) {
a = b +1;
if ( a%2 == 0)
a++;
b++;
}
2. Cual seria el equivalente estructural para una sentencia switch del lenguaje C
Codigo Intermedio
Generalmente la conversion a codigo objeto se hace a traves primero de la conversion hacia un
codigo intermedio, el cual es generalmente independiente de la plataforma de hardware
1. El codigo intermedio esta a medio camino entre la complejidad del alto nivel y la
simplicidad del bajo nivel, esto facilita la conversion.
2. El codigo intermedio puede ser simple y estructurado, esto facilita los procesos de
optimizacion posteriores
1. Notacion de Tercetos
2. Notacion Postfija
3. Codigo P
Tercetos de control
condicionales
BP, <etiqueta>, // Bifurcar en positivo a <etiqueta>
BC, <etiqueta>, // Bifurcar en cero a <etiqueta>
BN, <etiqueta>, // Bifurcar en negativo a <etiqueta>
Incondicional
B, <etiqueta>, // Bifurca incondicionalmente a <etiqueta>
Ej. (1) =, a, 1
(2) = , b, 2
(3) -,a,b
(4) BP, 6,
(5) BN, 7,
(6) ++, a,
(7) ++, b,
(8) cout, a,
Ej. Usando tercetos implementar un programa que ingrese 2 números e imprima el mayor o 0 si
ambos son iguales.
(1) cin, a,
(2) cin, b,
(3) - , a , b
(4) BP, (8)
(5) BN, (10)
(6) cout, 0,
(7) B, (11)
(8) cout, a,
(9) B, (11)
(10) cout , b
(11) nop , ,
Llamadas a funciones
Consideremos el siguiente programa y su secuencia de ejecución:
void f1() {
……
……
}
void f2() {
…..
f1()
……
}
main () {
…..
f2()
……
f1()
}
El paso del punto de ejecución en el punto 2 desde main hacia f2() se hace a través de una llamada
CALL. Cuando la función termina regresa a través de una instrucción RET.
Tanto CALL como RET son instrucciones que tienen sus correspondientes en el código maquina. Se
diferencian de un GOTO en que guardan y recuperan la dirección de retorno en una pila.
Ejemplo:
void f1() {
int a; (1) B , 9 ,
a ++; (2) ++, a ,
} (3) RET, ,
void f2() { (4) =, c, 4
int b, c=4; (5) CALL, 2,
f1() (6) +, b, c
Tercetos
cout << b + c; (7) cout, (6),
------>
} (8) RET, ,
main () { (9) =, d, 5
int d=5; (10) CALL, 4,
f2() (11) ++, d,
d ++; (12) CALL, 2,
f1() (13) cout, d,
cout << d; (14) RET, ,
}
Descripcion:
• El terceto (1) bifurca hacia el inicio del main() pues es la primera función a ejecutar.
• Las funciones f1, f2 y main empiezan en los tercetos (2), (4) y (9) respectivametne.
• Toda función termina con la instrucción RET, incluso la función main.
Paso de parámetros
El potencial de las funciones es gracias al uso de los parámetros, los cuales les permiten adecuarse
a diversas variantes de procesamiento.
Implementación de la llamada por valor. Se puede implementar a través de una pila de datos,
donde, antes de hacer el CALL, se empilan los valores de los parámetros actuales. Luego, cuando
la función se ejecute, lo primero que debe hacer es desempilar esos valores y asignárselos a los
parámetros formales.
En el caso del return la función devuelve el valor igualmente a través de la pila. El modulo que
llamo a la función debe inmediatamente sacar de la pila dicho valor de retorno cuando se retorne
de la función.
Ejem.
(1) B,8, se bifurca al inicio del main
int f1(int a, int b) { (2) pop, b , inicio de f1, desempila sus
int c; (3) pop, a, parametros.
c = a + b; (4) +, a, b
return c; Tercetos
(5) =, c, (4)
} ------>
(6) push, c, empila el valor de retorno
main () { (7) RET, , fin de f1, retorna
int d, e; (8) cin, d, inicio de main
cin >> d >> e; (9) cin, e,
cout << f1(d, e); (10) push, d, empila parametros actuales
} (11) push, e,
(12) CALL, 2, llamada a f1
(13) pop, , desempila valor de retorno
(14) cout, (13),
(15) RET
Funciones con parámetros opcionales. En este caso podemos usar otra primitiva de la pila para
verificar si hay mas parámetros en la pila. Específicamente podemos usar empty() el cual nos
devuelve TRUE (valor +1) o FALSE (valor 0).
Ejem.
Ejm.
x= a + b*c;
xa bc*+=
Ejm.
x = (a + b) * c;
xab+c*=
Algoritmo:
1. Si es opn == > push (opn)
2. Si es opr == > op2 = pop()
op1 = pop()
push (op1 opr op2)
Ejm.
xab+c*=
Observaciones:
1. Recordar que toda expresión tiene valor.
2. En el caso de operadores unarios la expresión tiene que evaluarse acorde al tipo de
operador.
Ejm. x = -a + b * c;
Postfijo: x a – b c * + = ojo .. no es: x = b*c – a;
3. En la expresión postfija los operandos tienen la misma secuencia que en la expresión infija
Obs:
-a + b <> b – a
-a <> 0 – a
Ejm:
x = a ^b ^3; postfijo: x a b 3 ^ ^ = xab^3^=
CORRECTO INCORRECTO
y = a + b + c; postfijo: y a b + c += yabc++=
CORRECTO INCORRECTO
Ojo:
Los operadores tienen:
1. Representacion
2. Nro y tipo de operandos
3. Prioridad
4. Asociatividad
Algoritmo:
1. Precondicion: Empilar INICIO, el cual tiene la mas baja prioridad
2. Llamar al scanner
3. Si es OPN == > va a la SALIDA
4. Si es OPR ==>
Si prioridad (OPR) > prioridad ( CabezaPila() ) == > empilar (OPR)
Si prioridad (OPR) < prioridad ( CabezaPila() ) == > desempilar todos los de prioridad >
y enviar a la SALIDA , empilar(OPR)
Si prioridad (OPR) = prioridad ( CabezaPila() ) == > ver asociatividad
Si ASOC_IZQ => desempilar y enviar a la SALIDA, luego empilar(OPR)
Si ASOC_DER => empilar(OPR)
Observacion:
La expresión infija debe ser CORRECTA, lo cual implica que previamente le hemos realizado un
ANALISIS SINTACTICO.
Observacion:
En una expresión podemos tener diversos tipos de valor:
x = a + b * c + f1(4) – 5;
valor almacenado + valor calculado + valor procesado - valor inmediato