Sei sulla pagina 1di 17

Cdigo de 3 direcciones parte 1

Me he recorrido la Internet buscando y buscando algo de informacin, lo ms completa posible, acerca de cdigo de 3
direcciones para desarrollar mis proyectos de la universidad y, desgraciadamente, no corr con suerte.

Antes de ver esas grandes sbanas de cdigo, comenzar a dar algo de teora para permitir la comprensin de los
temas. Esta informacin no pertenece a ningn libro sino es la forma a como yo lo conozco. Recomiendo que no lo
tomen como algo nico, estoy seguro que existen ms conceptos y tcnicas. Mi objetivo es presentar algo fcil de
entender en este tema tan complejo y les facilite un poco.
Introduccin
Compilador y sus fases
Un compilador no es ms que un programa que permite la interaccin entre un programador y una computadora, se los
describo lo ms coloquial posible.

Un compilador permite que un programa sea codificado sin errores y traduce el lenguaje de un programador a el
lenguaje que entienden las computadoras 1 y 0.
Est compuesto por dos fases:
Anlisis.
Sntesis.


Anlisis
Est dividida en 3 fases las cuales son:
Anlisis lxico (scanner): Descompone todo el cdigo en unidades lxicas llamadas tokens las cuales forman las
palabras de nuestro lenguaje. En si no importa que diga cada palabra solo importa que tipo de palabra es. Ac se
verifica tambin si cada smbolo forman parte del lenguaje.
Anlisis sintctico (parser): Rene todas las palabras del analizador lxico y fabrica una estructura gramatical que
reconozca el orden correcto de las palabras. Para eso es necesario que exista una gramtica definida.
Anlisis semntico: Permite darle sentido a la estructura gramatical existente. En toda la gramtica pueden existir
diferentes contextos los cuales son considerados como situaciones en los cuales se deba realizar cierta accin. En
esta fase se verifica si una variable que se accede ya fue declarada previamente, si se declaran variables repetidas, se
validan coherencias entre tipos de datos, etc. Son cuestiones que cumplen con la gramtica pero no tienen sentido.
En las fases antes mencionadas, se construye la tabla de smbolos y se verifican errores. Tomar en cuenta que
los errores lgicos tienen mucho que ver con la abstraccin del programador, por lo tanto no es posible que un
compilador pueda detectarlos.
Sntesis
En esta fase se inicia la traduccin del cdigo de alto nivel a uno de bajo nivel como lo es el ensamblador o incluso el
cdigo binario. Se divide en las siguientes fases:
Traduccin a cdigo intermedio (cdigo de tres direcciones): En esta parte concentraremos todo el estudio, consiste
en transformar todo el cdigo de alto nivel a uno equivalente pero ms sencillo. Esto significa que el lenguaje ser
menos inteligente en el sentido que no se tiene control de lo que se est ejecutando pero nos facilita la traduccin al
cdigo ensamblador.
Optimizacin de cdigo: Dado que una gramtica es quien se encarga de generar el cdigo de 3 direcciones, este
tiene el defecto que genera cdigo poco eficiente en tiempo de ejecucin y ocupa ms recursos de lo debido. Esta
fase consiste en optimizar y mejorar el rendimiento del programa. Por lo general se consiguen mejores resultados.
Traduccin a ensamblador: Es el paso del cdigo de 3 direcciones a ensamblador, realmente esta fase es bastante
trivial ya que tienen mucha similitud entre el cdigo de 3 direcciones y el ensamblador.
Traduccin a cdigo binario: Es el paso del cdigo ensamblador al binario. Esta parte queda fuera del alcance de
este blog pero cabe mencionar que est muy relacionado a como funciona la unidad lgica aritmtica (ALU) de
nuestro equipo as como la memoria ram.
Para estos temas recomiendo el libro del dragn profundiza un montn.
Comencemos entonces.


Cdigo de 3 direcciones
Se le llama de esa forma porque solo permite referenciar a 3 direcciones de memoria al mismo tiempo. Esto significa
que expresiones como (a+3)*(4-5)/2 no pueden ser operadas como una sola instruccin. Por lo tanto se debe
descomponer la expresin en expresiones ms sencillas y debemos auxiliarnos de variables temporales para almacenar
los resultados mientras no los hayamos usado. Para efectos de notacin, vamos a denotar las variables temporales con
una t_n donde n representa un subndice.

Llevemos a la prctica lo que hemos dicho, primero vamos a suponer que en este momento no existen las funciones ni
las clases. Solo estamos en un ambiente global con un lenguaje estructurado y todas las variables son globales:
Para la expresin x=(a+3)*(4-5)/2 vamos a transformarla a cdigo de 3 direcciones:
t_1=a+3;
t_2=4-5;
t_3=t_1*t_2;
t_4=t_3/2
x=t_4;
Como pueden ver he utilizado temporales para ir almacenando el resultado de las expresiones simples y utilizarlas
posteriormente. Esto nos permite hacer operaciones ms complejas con un lenguaje simple. Estoy asumiendo que
tenemos una precedencia que beneficia la multiplicacin y la divisin sobre la suma y la resta; tambin estamos
asociando las operaciones de izquierda a derecha. Es interesante observar que siempre creamos un nuevo temporal
cuando podramos reutilizarlos; esto se debe a que nuestro analizador sintctico no es tan inteligente como para
identificar eso por lo tanto solo complicara la programacin y adems veremos que eso no permite optimizar cdigo.

La pregunta del milln sera para que me va servir la gramtica? Mi respuesta sera, la idea es que permita generarse
cdigo de 3 direcciones a partir de un lenguaje de alto nivel. Veamos entonces como queda la gramtica:

E->E +T{E.val=gen_temp(); write(E.val,=,E1.val,+,T.val); }
|E-T{E.val=gen_temp(); write(E.val,=,E1.val,-,T.val); }
|T {E.val=T.val}
T->T*F{E.val=gen_temp(); write(T.val,=,T1.val,*,F.val); }
|T/F{E.val=gen_temp(); write(T.val,=,T1.val,/,F.val); }
|F{T.val=F.val}
F->(E){F.val=E.val}
|num {F.val=num.val}
|id {F.val=id.val}

En la gramtica anterior asumimos que ya existe una funcin llamada gen_temp que nos genera los temporales que no
son ms que cadenas de texto concatenadas con un nmero XD.
Como se puede observar hasta ahora el tema no es complejo y en los prximos post hablaremos ms de este tema. Mi
objetivo es cubrir todo lo relacionado a cdigo de 3 direcciones para lenguajes de alto nivel orientados a objetos.
Es importante tener en cuenta que el cdigo de 3 direcciones no est conciente de que es lo que est
haciendo, ni tampoco sabe de la existencia de variables, clases, sentencias, etc. Solo ejecuta todo el cdigo
que se encuentra a su paso y su comportamiento debera ser exctamente igual al que tendra un lenguaje de
alto nivel.
Hagamos otro ejercicio:
Asumimos que nos encontramos en un ambiente global donde, eventualmente, todas las variables son
globales y solo contamos con las operaciones aritmticas bsicas (suma, resta, multiplicacin, divisin y
mdulo).
int x;
int y=2;
x=20;
int z=x+y/5;
Resolucin:
y=2;
x=20;
t_1=y/5;
t_2=x+t_1;
z=t_2
Ntese que en ningn momento utilic ms de 3 referencia por lnea. Adems, como nos encontramos en un
ambiente global, podemos nombrar las variables de la misma manera como fueron nombradas en el cdigo de
alto nivel.
Expresiones lgicas
Se entiende por expresin lgica como aquella que solo nos puede devolver dos tipos de valores: true o false.
Las operaciones lgicas, aunque no comparten las mismas caractersticas que las operaciones numricas,
deben ir junto con las expresiones aritmticas. Sin embargo las operaciones lgicas o booleanas tienen menor
presedencia que las operaciones aritmticas.
Las operaciones aritmticas se dividen en:
Preposiciones simples.
Preposiciones compuestas.
Preposiciones simples
Se les llama as a aquellas que solo dependen del resultado de una preposicin lgica. Estn divididas en:
Literales booleanas (true, false).
Expresiones relacionales (<,>,<=,>=).
Expresiones de igualdad (==, !=).
Ejemplos:
Para las siguientes asignaciones, vamos a demostrar las preposiciones simples con su respectiva traduccin
a cdigo de 3 direcciones:
1. bool x=true; //Literal booleana
2. bool y=false; //Literal booleana
3. bool z=4>20;//Expresiones relacionales
4. bool w=a+1==10; //Expresin de igualdad
5. bool N= 7!=5; //Expresin de igualdad
Recuerden que todas estas expresiones nos dan como resultado valores true o false y luego le asignan el
valores a la variables que se encuentran del lado izquierdo de la asignacin es decir el id que se encuentra a
la izquierda del signo =. Es importante recalcar que es imposible que se den expresiones que no sean
binarias o unarias. Por ejemplos es imposible que se de 1<2>5 ya que por asociatividad se operaran los
primeros dos operandos y nos dara false y luego se pretendera comparar un booleano con un entero es decir
false>5 lo cual es un error semntico. Este tipo de expresiones no pueden ser unidas por smbolos de
igualdad ni relacionales. Un caso vlido podra ser 4==5==true sin embargo no tiene sentido.
Antes de resolver las expresiones antes mencionadas, introducir notacin adicional al cdigo de 3
direcciones.
Tenemos los operadores lgicos (>, >=, <=, <, ==, !=)
Tenemos los goto etiqueta que permite llevarnos hasta la lnea de cdigo en donde se encuentre la etiqueta
nombrada.
Tenemos Etiqueta: que es la etiqueta nombrada como tal.
Tenemos la evaluacin de una condicion por medio de la sentencia if
A continuacin resolver las expresiones que mencion anteriormente:
1. bool x=true;
x=1;
2. bool y=false;
y=0;
3. bool z=4>20;
if(4>20) goto L1;
goto L2;
L1: z=1;
goto L3;
L2: z=0;
L3:
4. w=a+1==10;
t_1=a+1;
if(t_1==10) goto L1;
goto L2;
L1: w=1;
goto L3;
L2: w=0;
L3:
5. N=7!=5;
if(7!=5) goto L1;
goto L2;
L1: N=1;
goto L3;
L2: N=0;
L3:
Como se habrn dado cuenta, nunca utilic true o false sino 1 0. Tambin es importante recalcar que en ningn
momento he utilizado ms de 3 referencias por instruccin.
Preposiciones compuestas
Las proposiciones compuestas son aquellas que contienen dos o ms proposiciones en una misma expresin. Estas
expresiones tambin devuelven un valor true o false. Los operadores que permiten componer preposiciones simples son:
&&, || ! o tambin and, or not.
Los operadores and, or y not no existen en el cdigo de 3 direcciones pero se pueden simular mediante un control
en las etiquetas y goto.
Otro punto muy importante a considerar es que la idea es que se ejecute la menor cantidad posible de
instrucciones para hacer ms eficiente el programa.
Comencemos con la instruccin OR
Segn la siguiente tabla de verdad veremos:
X Y Resultado
0 0 0
0 1 1
1 0 1
1 1 1
Como pueden observar, siempre y cuando una de las dos preposiciones sea verdadera el resultado ser
verdadero y en caso contrario ser falso. Por lo tanto podramos evitar que se ejecute la preposicin
compuesta por completo si en caso alguna de todas sea verdadera.
Veamos el siguiente ejemplo:
bool x=n<10 || y==10;
if(n<10) goto L1;
goto L2;
L2:if(y==10) goto L3;
goto L4;
L4: x=0;
goto L5;
L1:L3: x=1;
L5:
bool w=a+n/4>=10||x+1==10||x!=0;

t_1=n/4;
t_2=a+t_1
if(t_2>=10)goto L1;
goto L2;
L2:
t_3=x+1;
if(t_3==10)goto L3;
goto L4;
L4:if(x!=0)goto L5;
goto L6;
L1:L3:L5: w=1;
goto L7;
L6:w=0;
L7:
Comencemos con la instruccin AND
Segn la siguiente tabla veremos:
X Y Resultado
0 0 0
0 1 0
1 0 0
1 1 1
Como se puede observar, el resultado ser falso si una de las dos preposiciones es falsa. Por lo tanto
podramos evitar que se ejecute la preposicin compuesta por completo si en caso alguna de todas sea falsa.
Veamos el siguiente ejemplo:
bool x=n<10 && y==10;
if(n<10) goto L1;
goto L2;
L1:if(y==10) goto L3;
goto L4;
L3: x=0;
goto L5;
L2:L4: x=1;
L5:
Como ven este caso es muy similar al de las or por lo tanto obviaremos la explicacin.
Instruccin not

Es la negacin de una expresion booleana, es decir si antes era falsa ahora es verdadera y viceversa. Podemos agrupar una
expresin booleana completa y negarla. Por ejemplo en el caso de la operacin anterior:
bool x=!(n<10 && y==10);
if(n<10) goto L1;
goto L2;
L1:if(y==10) goto L3;
goto L4;
L2:L4:: x=0;
goto L5;
L3: x=1;
L5:
Aparentemente qued igual pero como pueden observar se intercambiaron las etiquetas de salto que quedan al final. Eso
significa que se ha negado la expresin booleana. Es decir el resultado verdadero me da uno falso y viceversa.
Conclusin:
Las expresiones lgicas solo nos permiten cambiar el flujo de ejecucin del programa. Ms adelante veremos que estas
expresiones booleanas nos permitirn hacer sentencias de control y cclicas.
Y antes de dar fin a este post haremos la gramtica que genera todo lo que acabo de explicar:
Asig->id = ExpOr{
if(ExpOr.tipo==int||(ExpOr.tipo==bool&&ExpOr.v==&&ExpOr.f==)){
write(id.val,=,ExpOr.val);
}
else{
write(ExpOr.v,:);
write(id.val,=1);
tmp=genTemp();
write(goto ,tmp);
write(ExpOr.f,:);
write(id.val,=0);
write(tmp,:);
}
}
ExpOr->ExpOr{
if(ExpOr1.tipo!=bool)
abortar(Error Linea X, posicion X, los tipos no son coherentes para el operador ||);
if(ExpOr1.v==){
ExpOr1.v=genEtiq();
ExpOr1.f=genEtiq();
write(if(,ExpOr1.val,==1)goto,ExpOr1.v);
write(goto ,ExpOr1.f);
}
write(ExpOr.f,:)
} || ExpAnd{
if( ExpAnd.tipo!=bool)
abortar(Error Linea X, posicion X, los tipos no son coherentes para el operador ||);
ExpOr.v=concat(ExpOr1.v,:,ExpAnd.v);
ExpOr.f=ExpAnd.f;
ExpOr.val=;
ExpOr.tipo=bool
}
|ExpAnd{ExpOr.val=ExpAnd.val;ExpOr.tipo=ExpAnd.tipo;ExpOr.v=ExpAnd.v;
ExpOr.f=ExpAnd.f}
ExpAnd->ExpAnd {
if(ExpAnd1.tipo!=bool)
abortar(Error Linea X, posicion X, los tipos no son coherentes para el operador &&);
if(ExpAnd1.v==){
ExpAnd1.v=genEtiq();
ExpAnd1.f=genEtiq();
write(if(,ExpAnd1.val,==1)goto,ExpAnd1.v);
write(goto ,ExpAnd1.f);
}
write(ExpAnd.v,:)
}&& ExpIg{
if( ExpIg.tipo!=bool)
abortar(Error Linea X, posicion X, los tipos no son coherentes para el operador &&);
ExpAnd.f=concat(ExpAnd1.f,:,ExpIg.f);
ExpAnd.v=ExpIg.v;
ExpAnd.val=;
ExpAnd.tipo=bool
}
|ExpIg{ExpAnd.val=ExpIg.val;ExpAnd.tipo=ExpIg.tipo;ExpAnd.v=ExpIg.v; ExpAnd.f=ExpIg.f}
ExpIg->ExpIg == ExpRe{
if(ExpIg1.tipo!=int && ExpRe.tipo!=int)
abortar(Error Linea X, posicion X, los tipos no son coherentes para el operador ==);
ExpIg.v=genEtiq();
ExpIg.f=genEtiq();
write(if(,ExpIg1.val,==,ExpRe,)goto ,ExpIg.v);
write(goto ,ExpIg.f);
ExpIg.val=;
ExpIg.tipo=bool
}
|ExpIg != ExpRe{
if(ExpIg1.tipo!=int && ExpRe.tipo!=int)
abortar(Error Linea X, posicion X, los tipos no son coherentes para el operador !=);
ExpIg.v=genEtiq();
ExpIg.f=genEtiq();
write(if(,ExpIg1.val,!=,ExpRe,)goto ,ExpIg.v);
write(goto ,ExpIg.f);
ExpIg.val=;
ExpIg.tipo=bool
}
|ExpRe{ExpIg.val=ExpRe.val;ExpIg.tipo=ExpRe.tipo;ExpIg.v=ExpRe.v; ExpIg.f=ExpRe.f}
ExpRe->ExpRe> ExpSum{
if(ExpRe1 .tipo!=int && ExpSum.tipo!=int)
abortar(Error Linea X, posicion X, los tipos no son coherentes para el operador >);
ExpRe.v=genEtiq();
ExpRe.f=genEtiq();
write(if(,ExpRe1.val,>,ExpSum,)goto ,ExpRe.v);
write(goto ,ExpRe.f);
ExpRe.val=;
ExpRe.tipo=bool
}
|ExpRe >= ExpSum{
if(ExpRe1 .tipo!=int && ExpSum.tipo!=int)
abortar(Error Linea X, posicion X, los tipos no son coherentes para el operador >=);
ExpRe.v=genEtiq();
ExpRe.f=genEtiq();
write(if(,ExpRe1.val,>=,ExpSum,)goto ,ExpRe.v);
write(goto ,ExpRe.f);
ExpRe.val=;
ExpRe.tipo=bool
}
|ExpRe < ExpSum{
if(ExpRe1 .tipo!=int && ExpSum.tipo!=int)
abortar(Error Linea X, posicion X, los tipos no son coherentes para el operador <);
ExpRe.v=genEtiq();
ExpRe.f=genEtiq();
write(if(,ExpRe1.val,<,ExpSum,)goto ,ExpRe.v);
write(goto ,ExpRe.f);
ExpRe.val=;
ExpRe.tipo=bool
}
|ExpRe <= ExpSum
{
if(ExpRe1 .tipo!=int && ExpSum.tipo!=int)
abortar(Error Linea X, posicion X, los tipos no son coherentes para el operador <=);
ExpRe.v=genEtiq();
ExpRe.f=genEtiq();
write(if(,ExpRe1.val,<=,ExpSum,)goto ,ExpRe.v);
write(goto ,ExpRe.f);
ExpRe.val=;
ExpRe.tipo=bool
}
|ExpSum{ExpRe.val=ExpSum.val;ExpRe.tipo=ExpSum.tipo;ExpRe.v=ExpSum.v; ExpRe.f=ExpSum.f}
ExpSum->ExpSum + ExpPor{
ExpSum .val=genTemp();
if(ExpSum .tipo!=int && ExpPor.tipo!=int)
abortar(Error Linea X, posicion X, los tipos no son coherentes para el operador +);
write(ExpSum .val,=,ExpSum 1.val,+,ExpPor.val);
ExpSum .v=; ExpSum .f=;
ExpSum .tipo=int
}
|ExpSum -ExpPor{
ExpSum .val=genTemp();
if(ExpSum .tipo!=int && ExpPor.tipo!=int)
abortar(Error Linea X, posicion X, los tipos no son coherentes para el operador -);
write(ExpSum .val,=,ExpSum 1.val,-,ExpPor.val);
ExpSum .v=; ExpSum .f=;
ExpSum .tipo=int
}
|ExpPor{ExpSum .val=ExpPor.val;ExpSum .tipo=ExpPor.tipo;ExpSum .v=ExpPor.v; ExpSum .f=ExpPor.f}
ExpPor->ExpPor * ExpUn{
ExpPor.val=genTemp();
if(ExpPor1.tipo!=int && ExpUn.tipo!=int)
abortar(Error Linea X, posicion X, los tipos no son coherentes para el operador *);
write(ExpPor.val,=,ExpPor1.val,*,ExpUn.val);
ExpPor.v=; ExpPor.f=;
ExpPor.tipo=int
}
|ExpPor/ExpUn{
ExpPor.val=genTemp();
if(ExpPor1.tipo!=int && ExpUn.tipo!=int)
abortar(Error Linea X, posicion X, los tipos no son coherentes para el operador /);
write(ExpPor.val,=,ExpPor1.val,/,ExpUn.val);
ExpPor.v=; ExpPor.f=;
ExpPor.tipo=int
}
|ExpPor%ExpUn {
ExpPor.val=genTemp();
if(ExpPor1.tipo!=int && ExpUn.tipo!=int)
abortar(Error Linea X, posicion X, los tipos no son coherentes para el operador /);
write(ExpPor.val,=,ExpPor1.val,/,ExpUn.val);
ExpPor.v=; ExpPor.f=;
ExpPor.tipo=int
}
EpxUn->!ExpSimple{EpxUn.val=ExpSimple.val;EpxUn.tipo=ExpSimple.tipo;EpxUn.v=ExpSimple.f;
EpxUn.f=ExpSimple.v}
|ExpSimple{EpxUn.val=ExpSimple.val;EpxUn.tipo=ExpSimple.tipo;EpxUn.v=ExpSimple.v; EpxUn.f=ExpSimple.f}
ExpSimple->(ExpOr) {ExpSimple.val=ExpOr.val;ExpSimple.tipo=ExpOr.tipo;ExpSimple.v=ExpOr.v;
ExpSimple.f=ExpOr.f}
|Id {ExpSimple.val=Id.val;ExpSimple.tipo=tablaSimbolos.getTipo(Id.val);ExpSimple.v=; ExpSimple.f=}
|Num {ExpSimple.val=Num.val;ExpSimple.tipo=int;ExpSimple.v=; ExpSimple.f=}
|true {ExpSimple.val=1;ExpSimple.tipo=bool;ExpSimple.v=; ExpSimple.f=}
|false {ExpSimple.val=0;ExpSimple.tipo=bool;ExpSimple.v=; ExpSimple.f=}



Sentencia If
Se valida una condicin y de ser verdadera, se ejecuta el cuerpo de la instruccin, veamos un ejemplo
sencillo:
int x=0;
int y=5;
if(x==0){
y=y+5*2;
x=y/5;
}
y=y+1;
El cdigo equivalente sera el siguiente:
x=0;
y=5;
if(x==0)goto L1;
goto L2;
L1:
t_1=5*2;
t_2=y+t_1;
y=t_2;
x=y/5;
L2:y=y+1
Como pueden ver utilic mucho de lo que vimos en el post anterior. La nica diferencia que se puede percibir
es que el resultado de la condicin no es almacenado en ninguna variable sino nicamente lo usamos para
manipular el flujo de ejecucin. Eso significa que el hecho que nosotros evaluemos una condicin en un if
eso no altera ninguna variable solo cambia la forma como se ejecutar el programa.
La estructura general de un if va como sigue:
if(condicion) goto Lv
goto Lf
Lv:
<instrucciones>
Lf:
<instrucciones>
Si son observadores se darn cuenta que el contenido de la etiqueta falsa se ejecutar sin importar cual sea
el resultado de la condicin del if y eso es correcto para este caso ya que si es falso slamente no ejecutar lo
que est dentro del if pero el resto lo har de todas maneras eso significa que la instruccin y=y+1 siempre
se ejecuta para este caso. En la siguiente sentencia se pueden hacer dos bloques excluyentes, es decir si no
se ejecuta un bloque entonces se ejecuta el otro.
Sentencia If-Else
Se valida la condicin si es verdadera, se ejecuta el cuerpo del if y se omite el cdigo del cuerpo del else.
De lo contrario no se ejecuta el cdigo del cuerpo del if pero si el del cuerpo del else Veamos el siguiente
ejemplo:
int x=0;
int y=5;
if(x==0){
y=y+5*2;
x=y/5;
}else{
y=y+1;
}
Vemos que es bastante parecido al ejemplo anterior pero ahora no queremos que se incremente en uno y si la
condicin resulta verdadera.
x=0;
y=5;
if(x==0)goto L1;
goto L2;
L1:
t_1=5*2;
t_2=y+t_1;
y=t_2;
x=y/5;
goto L3;
L2:y=y+1
L3:
Lo que cambia es que al final del cuerpo del if se usa una etiqueta de salida que har que se salte el cdigo
del else. Por lo tanto la forma general del cdigo de 3 direcciones para la sentencia if es como sigue:
if(condicion) goto Lv;
goto Lf;
Lv:
<instrucciones>
goto Lsalida;
Lf:
<instrucciones>
Lsalida:
Ahora veremos como podemos jugar con las etiquetas de salto para generar ciclos.
Sentencia while
La sentencia while es una especie de if, con la nica diferencia que al finalizar el cuerpo del if vuelve a
evaluar la condicin y si resulta verdadera la ejecuta otra vez y luego repite el ciclo hasta que sea falsa la
condicin. Por ejemplo si queremos que haya un contador:
int x=0;
int y=20;
while(x<10 && y>=0){
x=x+1;
y=y-1;
}
Su cdigo de 3 direcciones equivalente va como sigue:
x=0;
y=20;
L1: //etiqueta de salto de retorno
if(x<10) goto L2;
goto L3;
L2:if(y>=0)goto L4;
goto L5;
L4: //si es verdadera la condicion
x=x+1;
y=y-1;
goto L1; //salto para volver a evaluar la condicion
L3:L5://si es falsa
Bien ntese que usamos una condicin compuesta, segn vimos el operador and no existe en el cdigo de 3
direcciones, por lo tanto tenemos que hacer un pequeo jugueteo con las etiquetas y los saltos para
simularlo. Al final llegamos a la parte donde la condicin es verdadera y all se imprime el cuerpo del
while. Luego debe haber un salto que nos devuelva al inicio de la instruccin para volver a evaluar la
condicin. Si al final es falsa, entonces nos saca.
La estructura bsica de un while es como sigue:
Lregreso: if(condicion) goto Lv;
goto Lf;
Lv:
<instrucciones>
goto Lregreso;
Lf:
<instrucciones>
Alguna variantes:
Instruccin until
Ocurre lo mismo que el while con la diferencia que se ejecuta el cuerpo de la instruccin hasta que sea verdadera la
instruccin.
Lregreso: if(condicion) goto Lv;
goto Lf;
Lf:
<instrucciones>
goto Lregreso;
Lv:
<instrucciones>
Instruccin do-while
Esta variante primero ejecuta el cuerpo de la instruccin y por ltimo valida si la condicin es verdadera; esto
garantiza que al menos se ejecutar una vez el cuerpo de la sentencia. La forma como se debe expresar en
cdigo de 3 direcciones es como sigue:
Lregreso:
<instrucciones>
if(condicion) goto Lregreso;
goto Lf:
<instrucciones>
Asumiendo que la instruccin venga as:
do{
<instrucciones>
}while(condicion);
Si, en caso, se deseara el mismo funcionamiento, es decir que ejecute el cuerpo y despues valide la condicin
pero se quisiera presentar as
do while(condicion){
<instrucciones>
}
Su cdigo de 3 direcciones ira as:
goto L1;
Lregreso:if(condicion) goto Lv;
goto Lf;
L1:Lv:
<cuerpo>
goto Lregreso;
Lf:
<dems cdigo>
El cdigo puede parecer absurdo pero recuerden que eso debe generarse por medio de una gramtica y sera
la nica forma de hacerlo. No lo podemos expresar como el cdigo de 3 direcciones anterior ya que a la hora
de terminar de reconocer el cuerpo del do-while, ya no podemos volver a reconocer la condicion porque ya
pasamos por all.
Instruccin for
Es una variante ms del while, para este caso lo vamos a presentar tal como lo hace c#.
for(<asignacion>;<condicion>;<actualizacin>){<cuerpo>}
<asignacion de variables>
Lregreso: if(condicion) goto Lv;
goto Lf;
Lactualizar:
<actualizacion>
goto Lregreso;
Lv:
<instrucciones>
goto Lactualizar;
Lf:
<instrucciones>
Sigue pareciendo absurdo pero se genera de esa manera ya que se trata de una gramtica y esta obliga a
generar cdigo ineficiente pero a la hora de optimizar cdigo se pueden corregir muchas cosas.
Sentencia switch
Esta instruccin obtiene el valor de una variable y la evala en un conjunto de casos definidos y, en la primer
coincidencia, ejecuta el cdigo asociado a dicho caso. Si, en dado momento, ninguno de los casos coincide
puede existir un caso alterno el cual se ejecuta en ese momento.
Para una sintax siguiente:
switch(id){
case caso_1:
<cuerpo_1>
case caso_2:
<cuerpo_2>
case caso_3:
<cuerpo_3>
.
.
.
case caso_n:
<cuerpo_n>
defult:
<cuerpo_default>
}
El cdigo de 3 direcciones puede expresarse as:
if(id!=caso_1) goto L1
<cuerpo_1>
L1:if(id!=caso2)goto L2;
<cuerpo_2>
L2:if(id!=caso3)goto L3;
<cuerpo_3>
L3:if(id!=caso4)goto Ldef;
Ledf:
<cuerpo_def>
Si en caso se deseara ejecutar como lo hace java, que en el primer caso que coincida comienza a ejecutar
todos los cuerpos de cada caso restante a menos que se encuentre con un brake:
goto Lx;
L1:<cuerpo_1>
L2:<cuerpo_2>
L3:<cuerpo_3>
L_def:<cuerpo_def>
goto Lfin;
Lx:
if(id!=caso_1) goto L1
if(id!=caso2)goto L2;
if(id!=caso3)goto L3;
if(id!=caso4)goto Ldef;
Lfin:
Se expresa de esa manera ya que, como siempre, se genera por medio de una gramtica y su estructura
conveniente fue la descrita anteriormente.
A continuacin veremos un sencillo ejemplo:
Ejemplo
Queremos hacer un ciclo que sume en uno a una variable global siempre y cuando su valor sea impar, si en
algn caso el valor pasa por 4 que convierta en true a una variable booleana global.
int x=0,contador=1,y=0;
bool n=false;
for(contador=1;contador<100;contador++){
if(contador%2==0){
x=x+1;
n=x!=4;
}
y=y+1;
}
Lo haremos con colorcitos XD usaremos verde para la parte del for, azul para la parte del if y negro para el
resto:
x=0;
contador=1;
n=1;
contador=1;
L1:if(contador<100) goto L2;
goto L3;
L4:t1=contador+1;
contador=t1;
goto L1;
L2:
t2=contador%2;
if(t2==0)goto L5;
goto L6;
L5:
t3=x+1;
x=t3;
if(x!=4) goto L7;
goto L8;
L7:n=1;
L8:n=0;
L6:
t4=y+1;
y=t4;
goto L4;
L3:

Potrebbero piacerti anche