Está en la página 1de 11

UNIVERSIDAD DE SAN CARLOS DE GUATEMALA

FACULTAD DE INGENIERIA
ORGANIZACIÓN DE LENGUAJES Y COMPILADORES 2
ING. BAYRON LOPEZ
AUX. BYRON BOBADILLA
SECCION A

REPORTE

OTRAS FORMAS DE CODIGO INTERMEDIO


RESPECTO AL CODIGO DE TRES DIRECCIONES Y SUS COMPARACIONES

PABLO SERGIO ROMERO VELIZ


CARNE NO. 200512059
GUATEMALA 14 DE NOVIEMBRE DE 2008
FORMAS DE CODIGO INTERMEDIO

INTRODUCCION

Durante el proceso de compilación, una de las etapas de la fase de síntesis es la


Generación de Código intermedio. El código intermedio es un código que se genera a
partir del código fuente original, y debe cumplir un objetivo: debe ser fácil de traducir
desde el análisis sintáctico ser fácil de traducir a código objeto. Esta es la razón por la
cual existe el código intermedio. Es un paso más en el proceso de compilación. Es decir,
un paso más hacia el código objeto.

Por lógica, el código intermedio debe ser un código de nivel mas bajo que el código
fuente, y cabe mencionar que en algunos compiladores, dicho código no es generado,
sino se genera directamente el código máquina.

Uno de los códigos intermedios más utilizados es el código de tres direcciones, que no
se describirá en este documento, sino mas bien, se irá a la búsqueda de otros tipos de
código intermedio, que se pueden utilizar y que, de hecho, utilizan algunos
compiladores para algunos lenguajes de programación.

Dicho código intermedio pasará luego a ser optimizado, en la fase de Optimización de


Código, en la cual se eliminará código que esté de más, o que no sea óptimo y que
retrase el proceso de compilación, o que ocupe mucho espacio en la memoria.

Cabe mencionar que el código intermedio, no pertenece a ningún lenguaje en especial.


Es el hecho de que cumpla el objetivo lo que lo hace código intermedio. En el caso de el
código de tres direcciones, no es que deba ser escrito en algún lenguaje específico,
puede estar en C, C++, pascal, etc., siempre y cuando cumpla con ser código intermedio
(en este caso 3 direcciones) no importará si es de algún lenguaje específico ya conocido,
o inventado por el diseñador el compilador.

2
FORMAS DE CODIGO INTERMEDIO

OBJETIVOS

OBJETIVO GENERAL:

Encontrar algunas otras formas de Código Intermedio aparte del muy conocido
código de tres direcciones.

OBJETIVOS ESPECIFICOS:

Ir a la búsqueda de por lo menos dos tipos de código intermedio, y tener una


visión más amplia de la generación de código intermedio aparte del código de
tres direcciones.

Dados los códigos intermedios encontrados, investigar sus propiedades


características y compararlas con las del código de tres direcciones.

Encontrar compiladores de lenguajes que generen los códigos intermedios


investigados.

Identificar la complejidad de cada uno de los códigos intermedios que se han


investigado con respecto al código de tres direcciones.

3
FORMAS DE CODIGO INTERMEDIO

PLANTEAMIENTO DEL PROBLEMA

Luego de pasar la fase de análisis (Análisis del Léxico, Sintáctico y Semántico), el


proceso de compilación procede a realizar la generación de código intermedio. El
código más utilizado es el código de tres direcciones, por su versatilidad, su simplicidad
y su utilidad para posteriormente optimizarlo. El investigador desea compartir acerca de
por lo menos dos tipos de código intermedio y cuáles son sus características principales,
algunos compiladores que los utilizan e implementación (si es posible implementarlos
en una gramática igual que el código de tres direcciones).

Este documento no pretende ser un exhaustivo manual de cómo implementar todos los
tipos de código intermedio que existen y la forma en que se genera en una gramática,
sino mas bien pretende responder a la pregunta de si existen más tipos de código
intermedio diferente al de 3 direcciones, sus características comparadas con dicho
código y algunos lenguajes que le utilicen.

En este caso nos centraremos en describir brevemente tres tipos de Codigo Intermedio:
Uno del tipo gráfico (se representa gráficamente pero se implementa con estructuras de
datos), otro que ha sido estandarizado y por último un tipo de código de máquina
virtual.

MARCO TEORICO

Tipos de Código Intermedio

¿Qué es Código Intermedio?

El código intermedio es un código que puede generar un compilador y que es un punto


de acercamiento al código objeto al que se desea llegar. Los códigos intermedios pueden
ser códigos de alto nivel, nivel medio o bajo nivel dependiendo de cómo se haya
diseñado dicho código y a qué código objeto se desea llegar al final. Pero lo que sí debe
de cumplir el código intermedio es ser un código de más bajo nivel que el código fuente
original. Recordemos que la definición de compilador es un programa que se encarga de
transformar un código fuente de alto nivel a otro código objeto de más bajo nivel.

A continuación se describen 3 tipos de códigos intermedios:

Uno de tipo gráfico (Representación gráfica, no implementación gráfica)


Un código estandarizado (Originalmente diseñado por Microsoft)
Un código de máquina virtual (De un lenguaje compilado y posteriormente
interpretado)

4
FORMAS DE CODIGO INTERMEDIO

Arboles Sintácticos Abstractos

Los árboles sintácticos abstractos o AST (Abstract Syntax Trees por sus siglas en
inglés) es un tipo de representación intermedia, construida en forma de árbol. En este
árbol, todos los operadores vienen representados por nodos, y sus hijos son los
operandos. Trabajar con un árbol sintáctico abstracto es una forma fácil y efectiva de
manejar lo esencial del lenguaje, pues se abstraen las instrucciones y se eliminan todos
los elementos que vienen de sobra o extras.

Se trabaja con una estructura del tipo árbol, ya que esta estructura es la que se utiliza en
el análisis sintáctico, y es la que resulta más fácil de construir utilizando una gramática.

Por ejemplo a la hora de analizar las expresiones aritméticas, teniendo una línea de
código como la siguiente: 2 + 3 * (7 + 5). El árbol resultante para dicha expresión sería
el siguiente:

EE+E
|E–E
|E*E
|E/E
| (E)
| num
| id
Gramática para Expresiones Aritméticas

+ +

2 3 7 5
AST resultante para la expresión 2 + 3 * (7 + 5)

En un árbol sintáctico abstracto, los nodos vienen representados por los símbolos no
terminales. Entonces el AST se construye en base a una definición dirigida por la
sintaxis, es decir, es necesario hacer una gramática para generarlo, tal y como se hace
con el código de tres direcciones.

Ejemplo de Generación de AST:

Generar un Árbol de Sintaxis Abstracto para el siguiente código:

5
FORMAS DE CODIGO INTERMEDIO

a:double;
if (a>2) then
write a;
else
write g(5, a) + 8;
endif
write f(a * 2);

Ejemplo de generación de un AST para código fuente.


Fuente: http://www.di.uniovi.es/procesadores/Apuntes/Sintactico/Sintactico%20Clase%202.pdf

Una forma de implementar un árbol a la hora de generar uno, es la utilización de


estructuras de datos, éstas pueden ser de tipo string y que en dichas cadenas se guarde el
código que se vaya leyendo para su posterior recorrido y optimización.

Entre los compiladores que generan un AST como representación intermedia, están los
de los lenguajes del .net Framework (C#, Visual Basic, J#, etc). Ya que el compilador
de dichos lenguajes hace uso de esta técnica para la generación de código intermedio y
posteriormente se genera un código llamado CIL.

Algo muy importante es que un AST es solamente una representación intermedia, y no


se puede trabajar una optimización directa sobre él. Puede resultar muy útil para
abstraer el código fuente y eliminar los “adornos” del lenguaje a compilar, y obtener lo
esencial del código original. Luego se puede recorrer el árbol para obtener el código y
generar otro para optimizarlo o ir directamente a un código ensamblador por ejemplo.

6
FORMAS DE CODIGO INTERMEDIO

Código CIL

CIL es un código intermedio de bajo nivel y su nombre viene dado por las siglas
Common Intermediate Language (Lenguaje Intermedio Común). Básicamente, el CIL
es un lenguaje ensamblador orientado a objetos, y fue originalmente desarrollado por
Microsoft. Es por ello que muchos le conocen como MSIL, pero mas tarde fue
estandarizado para poder utilizarlo en otros lenguajes.

El código CIL trabaja en base a pilas y es utilizado por los lenguajes del .NET
Framework como código intermedio antes de pasarlo a lenguaje objeto.

El código CIL es un lenguaje completo, como se describió anteriormente fue diseñado


como un lenguaje ensamblador orientado a objetos, y con su estandarización ha sido
utilizado como código intermedio para otras aplicaciones que no son de Microsoft,
como el proyecto MONO de software libre.

CIL trabaja en base a pilas, es así como por ejemplo para mandar a ejecutar una función
o procedimiento, sus parámetros son colocados en la pila para poder llamarlos desde
ahí. Esto es exactamente lo que hace el código de tres direcciones, por su similitud con
el código ensamblador que trabaja directamente con la memoria de la computadora.

Entonces el proceso de traducción de código fuente a código CIL, es prácticamente el


mismo que se utiliza para la traducción a código de tres direcciones, solamente varía el
código y el Lenguaje Intermedio Común es un lenguaje definido y tiene una sintaxis
definida, y su estructura es de más bajo nivel, siendo prácticamente un lenguaje
ensamblador.

Por ejemplo, para las expresiones aritméticas, suponiendo que se quiere hacer la
siguiente suma: 2 + 3. Se introducen a la pila los dos valores (2 y 3), y con la directiva
“add” del lenguaje, toma esos dos valores de la pila y los suma, los saca y apila el valor
del resultado.

Para las estructuras de control (if, while, for, etc.) se hace uso de los saltos y etiquetas,
como en el lenguaje ensamblador, validando cada una de las condiciones y dando los
saltos respectivos (como el goto de código de tres direcciones).

Código Intermedio de Java

El lenguaje de programación Java, utiliza un código intermedio de máquina virtual,


llamado Java bytecode. Éste código lo genera el compilador de Java, que posteriormente
será interpretado.

El lenguaje Bytecode, es un lenguaje que se compone de instrucciones que tienen un


tamaño de un Byte, esto le da su nombre al código. Este código se compone de un
código de operación que va desde el cero hasta el doscientos cincuenta y cinco (0 - 255)
y dicho código va seguido de parámetros como las direcciones de memoria, nombres de
los registros, etc. (como el código ensamblador).

7
FORMAS DE CODIGO INTERMEDIO

A continuación se muestra una traducción de código Java a Código Bytecode de


ejemplo:

public class X {
public static void main(String[] args) {
add(1, 2);
}
public static int add(int a, int b) {
return a+b;
}
}

Traducción:

public static void main(java.lang.String[]);


Code:
0: iconst_1
1: iconst_2
//Method add:(II)I
2: invokestatic #2;
5: pop
6: return
public static int add(int,int);
Code:
0: iload_0
1: iload_1
2: iadd
3: ireturn
Ejemplo de traducción a Bytecode de Java.
Fuente: http://www.efn.uncor.edu/escuelas/computacion//files/Introducci%C3%B3n%20a%20JOP.pdf

Cabe aclarar que Java es un lenguaje que es compilado e interpretado. Al inicio, se


compila normalmente, pero no se genera directamente un código máquina, sino un
código de un nivel más abstracto para luego ser interpretado por un intérprete, que suele
llamarse Máquina Virtual de Java (Java Virtual Machine). Éste proceso es llamado
generación de código intermedio de máquina virtual, que es otro tipo de código
intermedio, y es utilizado por muchos compiladores de distintos lenguajes como Pearl,
PHP o Ruby.

En realidad lo que hace este proceso es una pre-compilación para luego realizar una
interpretación con intérpretes llamados Just in time, que van procesando todas las líneas
de código conforme las va leyendo para una mejor ejecución (más óptima).

8
FORMAS DE CODIGO INTERMEDIO

CONCLUSIONES

Existe una diversidad de códigos intermedios, la mayoría de ellos diseñados por


el mismo creador del compilador para determinado lenguaje, y varía su
complejidad ya que existen de tres tipos: de alto nivel, de medio nivel y de bajo
nivel.

Existen casi tantos códigos intermedios como compiladores de lenguajes. Esto


porque quien diseña un compilador puede crear su propio código intermedio que
le resulte agradable y sencillo para el objetivo final de un compilador: generar
un código de menor nivel que el código fuente original.

No por nada el código de tres direcciones es uno de los más utilizados y más
famosos, dado que es muy práctico y sencillo de aprender, y está basado en un
principio sencillo y concreto: cada sentencia debe de apuntar a tres direcciones
de memoria o menos. Además es muy estándar y no es tan complejo como
varios lenguajes intermedios que son muy parecidos al código ensamblador que
puede resultar muy complejo y engorroso para la mayoría de programadores
modernos.

Existe un tipo de código intermedio muy interesante, que es el código


intermedio de máquina virtual. Este es un código que se genera para luego ser
interpretado por alguna otra aplicación, comúnmente llamada máquina virtual.
Java es un lenguaje que utiliza este tipo de compilación, pero también hay otros
lenguajes como Ruby o PHP que llevan este mismo proceso.

El árbol sintáctico abstracto es una representación intermedia de código que


abstrae las estructuras esenciales de cualquier lenguaje, pero no se puede aplicar
una optimización en él, así que en este caso es recomendable utilizar código
como el de tres direcciones que de hecho sí se puede optimizar.

9
FORMAS DE CODIGO INTERMEDIO

RECOMENDACIONES

Al lector de este documento que se haya interesado por los códigos intermedios,
le recomiendo documentarse sobre el código intermedio de máquina virtual.

Obtener una más amplia información acerca del código Bytecode de Java e
investigar acerca de el código-P que en su tiempo utilizaron los compiladores de
pascal en los años 70’s y parte de los 80’s.

En este documento se explico la estructura de los árboles sintácticos abstractos,


pero dejo como recomendación investigar acerca de los grafos dirigidos
acíclicos, que se basan prácticamente en la misma estructura pero tienen una
diferencia con los ASTs.

Recomiendo también el estudio del código intermedio común o código CIL, que
fue originalmente creado por Microsoft Corporation (por eso también llamado
MSIL), pero que fue posteriormente estandarizado y ahora utilizado por el
proyecto MONO de software libre que contiene compiladores de varios
lenguajes de programación, entre ellos C#.

10
FORMAS DE CODIGO INTERMEDIO

FUENTES BILIOGRAFICAS

Alfred V. Aho, Ravi Sethi, Jeffrey D Ullman


Compiladores, principios técnicas y herramientas
1998, Editorial Pearson
(Capítulo VIII)

Kenneth C. Louden
Construccion de compiladores: Principios y prácticas
2004, Editorial Thompson

Fuentes Web:

http://www.di.uniovi.es/procesadores/Apuntes/GeneracionCodigo/trans.LRI.pdf
http://ccia.ei.uvigo.es/docencia/PL/T8.pdf
http://rua.ua.es/dspace/bitstream/10045/3848/1/cil-para-pl.pdf
http://www.uco.es/~ma1fegan/pl/practicas/ANTLR/Arboles-de-sintaxis-abstracta.pdf
http://www.dsic.upv.es/~jsilva/uned/compiladores/Apuntes06.pdf

11