Interpretación y compilación - Departamento de Lenguajes y

Anuncio
Departamento de Lenguajes y Sistemas Informáticos
Procesadores de Lenguajes
TEMA 4: INTERPRETACIÓN, COMPILACIÓN
OBJETIVO
Dar respuesta a las siguientes preguntas:
¿Qué es la interpretación de un lenguaje?
¿Qué es la compilación de un lenguaje?
¿Cómo se diseña y construye un intérprete?
¿Cómo se diseña y construye un compilador?
El reconocimiento del lenguaje ya no es el problema. Se supone que el análisis léxico-sintáctico
y el análisis semántico han resuelto el problema del reconocimiento. El problema ahora es ¿qué
hacer con las sentencias del lenguaje?
¿QUÉ ES LA INTERPRETACIÓN DE UN LENGUAJE?
Interpretar un lenguaje significa ejecutar directamente sus sentencias.
Supongamos el lenguaje LF definido en el Tema 1, Teoría. Para interpretar LF tenemos que
definir qué es la ejecución de un programa LF. LF es un lenguaje de programación secuencial.
Las instrucciones se ejecutan una a una desde el comienzo del programa hasta el fin. LF tiene la
capacidad de expresar variables enteras y dos tipos de instrucciones: (a) definición de variables
con expresión entera (DEF) y (b) evaluación de variables (EVAL).
No se acepta el uso de variables sin declarar. La declaración de variables asocia valor 0 por
defecto a éstas.
Ejecutar una instrucción DEF significa vincular una expresión a una variable para una eventual
evaluación. Toda definición de una variable solapará sus definiciones anteriores en el programa.
Toda variable definida sobre sí misma se le asocia un valor indefinido.
Ejecutar una instrucción EVAL x significa evaluar el valor de la expresión asociada a x mostrando
por pantalla el resultado.
A continuación mostramos un programa LF y su interpretación.
Ejemplo 1: Programa LF
VARIABLES
x, y, z, a, b;
INSTRUCCIONES
1
Departamento de Lenguajes y Sistemas Informáticos
Procesadores de Lenguajes
a DEF -1;
b DEF (a+1);
EVAL b;
a DEF 2;
EVAL b;
b DEF b + 1;
z DEF 2*y;
EVAL z;
EVAL b;
Ejemplo 2: Interpretación Programa LF
b
b
z
b
-->
-->
-->
-->
0
3
0
INDEF
¿QUÉ ES LA COMPILACIÓN DE UN LENGUAJE?
Compilar un lenguaje significa ser capaz de compilar cada sentencia del lenguaje. Compilar una
sentencia es traducirla a otro lenguaje preservando en lo posible el significado de la sentencia. A
diferencia de la interpretación, la compilación involucra dos lenguajes: el lenguaje fuente y el
lenguaje destino. El lenguaje fuente suele ser un lenguaje expresivo para el que no se dispone
de intérprete. El lenguaje destino suele ser un lenguaje menos expresivo pero tiene intérprete.
Un ejemplo de lenguaje compilado es Java. El lenguaje destino es un lenguaje ensamblador
interpretable (después del ensamblado) en la llamada máquina virtual de Java.
La construcción de un compilador obliga a especificar correspondencias entre los recursos
expresivos del lenguaje fuente y los recursos expresivos del lenguaje destino. Las principales
correspondencias en nuestro problema son: (1) el programa LF se corresponde con una clase
Java con main, (2) la instrucción EVAL se corresponde con la definición de un método (sin
parámetros) y la correspondiente llamada a dicho método en el main para presentar por pantalla
el resultado, (3) la instrucción DEF se corresponde con la definición de un método (sin
parámetros);
Ejemplo 3: Compilación en Java de programa LF en Ejemplo 1
import java.io.*;
public class _Programa
{
private static int a1(){
return -1;
}
private static int b1(){
return a1()+1;
2
Departamento de Lenguajes y Sistemas Informáticos
Procesadores de Lenguajes
}
private static int a2(){
return 2;
}
private static int b2(){
return a2()+1;
}
private static int y3(){
return 0;
}
private static int z3(){
return 2*y3();
}
private static int b4(){
return b4()+1;
}
public static void main(String[] args) {
System.out.println(b1());
System.out.println(b2());
System.out.println(z3());
System.out.println(b4());
}
}
¿CÓMO SE DISEÑA Y CONSTRUYE UN INTÉRPRETE?
No hay una respuesta general a esta pregunta. La construcción del intérprete es fuertemente
dependiente de la semántica del lenguaje. El paso clave del diseño es clarificar la semántica del
lenguaje. Una forma de clarificar esta semántica es poner sentencias de ejemplos y decir qué
significan (ver Ejemplos 1 y 2). Por ejemplo, la ejecución mostrada en el Ejemplo 2 facilita la
comprensión de LF. Las dos primeras evaluaciones de b producen resultados diferentes sin
cambiar su definición. Esto se debe a que las expresiones se asocian a las variables justo en el
momento de su evaluación no en el momento de su definición (late binding). En nuestro
programa de ejemplo (ver Ejemplo 1), la variable a modifica su definición a lo largo del programa
y la definición de b depende de la definición de a. Esto hace que la evaluación de b pueda ser
distinta sin modificar su definición.
Una vez comprendida la semántica de LF, diseñamos un intérprete. Para evaluar una variable es
necesario almacenar su definición. Dado que la evaluación no se produce hasta alcanzar una
instrucción EVAL, estas definiciones se almacenan en una tabla para recuperarlas en el
momento necesario (atributo heredado y sintetizado).
A continuación se muestra un intérprete para LF en forma de tree-parser.
Ejemplo 3: Intérprete para el lenguaje LF
header{
import java.util.*;
import antlr.*;
}
3
Departamento de Lenguajes y Sistemas Informáticos
Procesadores de Lenguajes
class Anasint2 extends TreeParser;
options
{
importVocab = Anasint;
}
tokens{
INDEF;
}
{
Hashtable<String, AST> variables = new Hashtable<String, AST>();
ASTFactory factory = new ASTFactory();
public void imprimir(){
System.out.println(variables);
}
public void declarar(String var){
AST n = nodo("0",NUMERO);
variables.put(var,n);
}
public AST nodo(String texto, int tipo){
AST resultado = new CommonAST();
resultado.setType(tipo);
resultado.setText(texto);
return resultado;
}
public int convertir(AST numero){
return (new Integer(numero.getText())).intValue();
}
public void almacenar(String var, AST expr){
variables.put(var,expr);
}
public boolean ocurrencia(String var, AST expr){
switch(expr.getType()){
case NUMERO: return false;
case VAR: return expr.getText().equals(var);
case MAS:
case POR: return ocurrencia(var, expr.getFirstChild())
|| ocurrencia(var,
expr.getFirstChild().getNextSibling());
case MENOS: if (expr.getFirstChild().getNextSibling()==null)
return ocurrencia(var, expr.getFirstChild());
else
return ocurrencia(var, expr.getFirstChild())
|| ocurrencia(var,
expr.getFirstChild().getNextSibling());
}
return false;
}
public AST evaluar(AST expr){
AST resultado = null;
4
Departamento de Lenguajes y Sistemas Informáticos
Procesadores de Lenguajes
AST a,b;
switch(expr.getType()){
case NUMERO: resultado = factory.dupTree(expr);
break;
case VAR: resultado = evaluar(variables.get(expr.getText()));
break;
case MAS: a = evaluar(expr.getFirstChild());
b = evaluar(expr.getFirstChild().getNextSibling());
if (a.getType()==INDEF || b.getType()==INDEF)
resultado = nodo("INDEF",INDEF);
else
resultado = nodo((new Integer(convertir(a) +
convertir(b))).toString(),NUMERO);
break;
case POR: a = evaluar(expr.getFirstChild());
b = evaluar(expr.getFirstChild().getNextSibling());
if (a.getType()==INDEF || b.getType()==INDEF)
resultado = nodo("INDEF",INDEF);
else
resultado = nodo((new Integer(convertir(a) *
convertir(b))).toString(),NUMERO);
break;
case MENOS: if (expr.getFirstChild().getNextSibling()==null){
a = evaluar(expr.getFirstChild());
if (a.getType()==INDEF)
resultado = nodo("INDEF",INDEF);
else
resultado =
nodo((new Integer(-1*convertir(a))).toString(),NUMERO);
}
else{
a = evaluar(expr.getFirstChild());
b = evaluar(expr.getFirstChild().getNextSibling());
if (a.getType()==INDEF || b.getType()==INDEF)
resultado = nodo("INDEF",INDEF);
else
resultado =
nodo((new Integer(convertir(a) - convertir(b))).toString(),NUMERO);
}
break;
case INDEF: resultado = factory.dupTree(expr);
break;
}
return resultado;
}
}
programa : #(PROGRAMA variables instrucciones )
;
variables : #(VARIABLES (v:VAR {declarar(#v.getText());})*)
;
instrucciones : #(INSTRUCCIONES (definicion|evaluacion)*)
;
definicion : #(DEF v:VAR e:expr) {if (!ocurrencia(#v.getText(),#e))
almacenar(#v.getText(),#e);
else{
AST n = new CommonAST();
n.setType(INDEF);
n.setText("INDEF");
5
Departamento de Lenguajes y Sistemas Informáticos
Procesadores de Lenguajes
almacenar(#v.getText(),n);}
}
;
evaluacion : #(EVAL v:VAR) {System.out.println(#v.getText()+" -->
"+evaluar(#v).toStringTree());}
;
expr : #(MAS expr expr)
| #(POR expr expr)
| NUMERO
| VAR
| (#(MENOS expr expr)) => #(MENOS expr expr)
| #(MENOS expr)
;
¿CÓMO SE DISEÑA Y CONSTRUYE UN COMPILADOR?
Tampoco hay respuesta general a esta pregunta. La construcción del compilador es fuertemente
dependiente de los lenguajes involucrados (lenguaje fuente y lenguaje destino). Los pasos
claves del diseño son (1) clarificar las semánticas del lenguaje fuente y del lenguaje destino y (2)
clarificar las correspondencias entre ambos lenguajes.
Tal y como ya hemos visto, el enlace dinámico de expresiones a variables constituye el principal
problema a la hora de traducir LF a Java (late binding). Las variables Java son imperativas: el
enlace del valor a la variable se produce al ejecutar una asignación y se mantiene hasta que otra
asignación la solape. Por tanto, no podemos usar asignaciones imperativas para implementar
definiciones LF.
Hemos visto que las principales correspondencias entre LF y Java son: (1) el programa LF se
corresponde con una clase Java con main, (2) la instrucción EVAL se corresponde con la
definición de un método (sin parámetros) y la correspondiente llamada a dicho método en el
main para presentar por pantalla el resultado, (3) la instrucción DEF se corresponde con la
definición de un método (sin parámetros);
Una forma de resolver el problema del enlace variable/expresión es codificar cada evaluación
con la declaración de un método. El cuerpo del método implementará el enlace dinámico. Por
ejemplo, los métodos b1() y b2() en el Ejemplo 3 implementan las dos primeras evaluaciones
de b en el programa LF del Ejemplo 2. Cada evaluación además generará una llamada al
método correspondiente en el main.
El siguiente ejemplo muestra el compilador de LF a Java.
Ejemplo 4: Compilador de LF
header{
import java.util.*;
import antlr.*;
import java.io.*;
}
6
Departamento de Lenguajes y Sistemas Informáticos
Procesadores de Lenguajes
class Anasint3 extends TreeParser; //Compilador
options
{
importVocab = Anasint;
}
{
FileWriter fichero;
private void open_file(){
try{
fichero = new FileWriter("_Programa.java");
}catch(IOException e)
{System.out.println("open_file (exception): "+e.toString());}
}
private void close_file(){
try{
fichero.close();
}catch(IOException e)
{System.out.println("close_file (exception): "+e.toString());}
}
//////////////////
int espacios = 0;
private void gencode_espacios(){
try{
for (int i = 1; i<=espacios;i++)
fichero.write("
");
}catch(IOException e)
{System.out.println("gencode_espacios (exception):
"+e.toString());}
}
/////////////////
ASTFactory factory = new ASTFactory();
Hashtable<String, AST> vars = new Hashtable<String, AST>();
Hashtable<String, AST> vars2 = new Hashtable<String, AST>();
List<String> evals_pend = new LinkedList<String>();
Integer num_evals = new Integer(0);
public void declarar_variable(String var){
AST nodo = new CommonAST();
nodo.setType(NUMERO);
nodo.setText("0");
vars.put(var,nodo);
}
public void definir_variable(String var, AST expr){
vars.put(var,expr);
}
7
Departamento de Lenguajes y Sistemas Informáticos
Procesadores de Lenguajes
public void evaluar_variable(String var){
Integer i;
String aux = new String(var);
num_evals++;
Enumeration<String> v = vars.keys();
while (v.hasMoreElements()){
String key = v.nextElement();
if (cierre(var).contains(key)){ //Se calcula el cierre de var.
// se calcula una variable indexada para var.
aux = key+num_evals.toString();
// se sustituye en la expresión de la definición cada
// variable original por una nueva variable indexada
// por el número de evaluaciones
vars2.put(aux,sustitucion_expr(vars.get(key)));
}
}
try{ // A cada variable presente en la definición de la variable
// evaluada (las incluidas en vars2) se genera un método.
v = vars2.keys();
while (v.hasMoreElements()){
String key = v.nextElement();
gencode_espacios();
fichero.write("private static int "+key+"(){\n");
espacios++;
gencode_espacios();
fichero.write("return "+gencode_exp(vars2.get(key))+";\n");
espacios--;
gencode_espacios();
fichero.write("}\n");
}
}catch(IOException e){}
vars2.clear();
evals_pend.add(var+num_evals.toString()); //Queda pendiente generar
// llamada en el main.
}
public Set<String> cierre(String var){
Set<String> resultado = new HashSet<String>();
int size_ant = 0;
int size_act = 0;
resultado.add(var);
size_act = resultado.size();
while (size_ant!=size_act){
resultado.addAll(calcular_variables(vars.get(var)));
size_ant = size_act;
size_act = resultado.size();
}
return resultado;
}
public Set<String> calcular_variables(AST expr){
Set<String> aux = new HashSet<String>();
switch(expr.getType()){
case NUMERO: break;
case VAR: aux.add(expr.getText()); break;
8
Departamento de Lenguajes y Sistemas Informáticos
Procesadores de Lenguajes
case MAS:
case POR:
aux.addAll(calcular_variables(expr.getFirstChild()));
aux.addAll(calcular_variables(expr.getFirstChild().getNextSibling()));
break;
case MENOS: aux.addAll(calcular_variables(expr.getFirstChild()));
if (expr.getFirstChild().getNextSibling()!=null)
aux.addAll(calcular_variables(expr.getFirstChild().getNextSibling()));
break;
default: return null;
}
return aux;
}
public AST sustitucion_expr(AST expr){
AST nodo;
String aux;
switch(expr.getType()){
case NUMERO: return factory.dupTree(expr);
case VAR: aux = expr.getText();
nodo = new CommonAST();
aux = aux + num_evals.toString();
nodo.setType(VAR);
nodo.setText(aux);
return nodo;
case MAS: nodo = new CommonAST();
nodo.setType(MAS);
nodo.setText("+");
nodo.setFirstChild(sustitucion_expr(expr.getFirstChild()));
nodo.getFirstChild().setNextSibling(
sustitucion_expr(expr.getFirstChild().getNextSibling()));
return nodo;
case POR: nodo = new CommonAST();
nodo.setType(POR);
nodo.setText("*");
nodo.setFirstChild(sustitucion_expr(expr.getFirstChild()));
nodo.getFirstChild().setNextSibling(
sustitucion_expr(expr.getFirstChild().getNextSibling()));
return nodo;
case MENOS: nodo = new CommonAST();
nodo.setType(MENOS);
nodo.setText("-");
nodo.setFirstChild(
sustitucion_expr(expr.getFirstChild()));
if (expr.getFirstChild().getNextSibling()!=null)
nodo.getFirstChild().setNextSibling(
sustitucion_expr(expr.getFirstChild().getNextSibling()));
return nodo;
default: return null;
}
}
private void gencode_begin_class(){
try{
gencode_espacios();
fichero.write("import java.io.*;\n");
gencode_espacios();
9
Departamento de Lenguajes y Sistemas Informáticos
Procesadores de Lenguajes
fichero.write("public class _Programa"+"\n");
gencode_espacios();
fichero.write("{\n");
espacios++;
}catch(IOException e){}
}
private void gencode_main(){
try{
gencode_espacios();
fichero.write("public static void main(String[] args) {\n");
espacios++;
Iterator<String> it = evals_pend.listIterator();
while (it.hasNext()){
gencode_espacios();
fichero.write("System.out.println("+it.next()+"());\n");
}
espacios--;
gencode_espacios();
fichero.write("}\n");
}catch(IOException e){}
}
private void gencode_end_class(){
try{
espacios--;
gencode_espacios();
fichero.write("}");
}catch(IOException e){}
}
public String gencode_exp(AST expr){
switch(expr.getType()){
case NUMERO: return expr.getText();
case VAR: return expr.getText()+"()";
case MAS: return gencode_exp(expr.getFirstChild())+"+"
+gencode_exp(expr.getFirstChild().getNextSibling());
case POR: return gencode_exp(expr.getFirstChild())+"*"
+gencode_exp(expr.getFirstChild().getNextSibling());
case MENOS: if (expr.getFirstChild().getNextSibling()!=null)
return gencode_exp(expr.getFirstChild())+"-"
+gencode_exp(expr.getFirstChild().getNextSibling());
else
return "-"+gencode_exp(expr.getFirstChild());
default: return null;
}
}
}
programa : {open_file(); gencode_begin_class();}
#(PROGRAMA variables instrucciones)
{gencode_main(); gencode_end_class(); close_file();}
;
10
Departamento de Lenguajes y Sistemas Informáticos
Procesadores de Lenguajes
variables : #(VARIABLES (v:VAR {declarar_variable(v.getText());})*)
;
instrucciones : #(INSTRUCCIONES (definicion|evaluacion)*)
;
definicion : #(DEF v:VAR e:expr) {definir_variable(v.getText(),e);}
;
evaluacion : #(EVAL v:VAR) {evaluar_variable(v.getText());}
;
expr : #(MAS expr expr)
| #(POR expr expr)
| NUMERO
| VAR
| (#(MENOS expr expr)) => #(MENOS expr expr)
| #(MENOS expr)
;
11
Departamento de Lenguajes y Sistemas Informáticos
Procesadores de Lenguajes
GENERACIÓN DE CÓDIGO PARA LA MÁQUINA VIRTUAL DE JAVA
LA MÁQUINA VIRTUAL DE JAVA
El formato de ficheros “class”.
La máquina virtual Java (JVM) ejecuta un código particular que es independiente de
arquitecturas concretas y sistemas operativos. Los ficheros ensamblados para dicha máquina
tienen extensión “class”.
Tipos de Datos en JVM.
JVM opera sobre dos clases de tipos: los tipos predefinidos y el tipo referencia.
Los tipos primitivos son:
byte (1 byte),
short (2 bytes),
int (4 bytes),
long (8 bytes),
char (2 bytes),
float (4 bytes),
double (8 bytes),
boolean (valores representados como valores tipo int).
dirección de retorno (tipo interno de la máquina, no programable. Sus valores son apuntadores a
código JVM).
Los tipos reference (referencia) son:
tipo clase (referencias a instancias de clase creadas dinámicamente)
tipo array (referencias a instancias de arrays creadas dinámicamente)
tipo interfaz (referencias a instancias de interfaces creadas dinámicamente)
Existe un valor especial null (para cualquiera de los 3 tipos) para referirnos a la ausencia de
objeto.
Runtime JVM.
JVM es una máquina que simula concurrencia real a través de un conjunto de tareas o hilos de
control. Cada tarea representa una secuencia de instrucciones en ejecución. JVM cuenta con un
contador de programa y una pila para cada tarea y con un montón y una zona de código común
a todas las tareas.
En tiempo de ejecución, la pila puede contener múltiples frames. Sólo el frame situado en la
parte superior marcará el método activo en cada instante de ejecución de la tarea.
Cada frame contiene (a) una referencia al objeto actual (sólo si es instanciable la clase), (b)
parámetros, (c) array de variables locales del método y una pila de operandos.
12
Departamento de Lenguajes y Sistemas Informáticos
Procesadores de Lenguajes
El array de variables locales está destinado a almacenar la dirección del objeto, los parámetros
del método y las variables locales del método. Cada variable local puede almacenar un valor de
tipo boolean, byte, short, int, float o referencia. Los valores de tipo long o double necesitan dos
variables locales. JVM no requiere alineado de ningún tipo.
La pila de operandos está destinada a almacenar resultados parciales y preparación de llamadas
a métodos y obtención de resultados. Por ejemplo, para sumar dos enteros es necesario
cargarlos previamente en la pila y luego ejecutar la instrucción suma. La operación consumirá los
operandos y los sustituirá por el resultado.
JVM
Tarea k
Frame k.1
Variables locales
Pila operandos
El mónton es una zona de memoria destinada a almacenar instancias de clases, arrays e
interfaces.
La zona de código almacena constantes, estructuras de clases (atributos y métodos) y código de
métodos.
Juego de Instrucciones para JVM
En esta sección presentaremos un resumen extendido del juego de instrucciones existentes para
la máquina virtual de Java. Es importante señalar que JVM es una máquina basada en el uso de
pila y las descripciones de las instrucciones usarán la notación (estado antes ==> estado
después) haciendo referencia al estado de la pila antes y después de la ejecución de la
instrucción. Las definiciones harán uso de dos vocablos con un significado preciso: cargar, para
mover datos desde las variables a la pila, y almacenar, para mover los datos desde la pila hacia
las variables.
JVM define un juego de instrucciones para cada tipo predefinido. Las instrucciones sobre enteros
presentan código de operación prefijado con i, l para long, s para short, b para byte, c para char,
f para float, d para double y a para reference.
Según la funcionalidad, las instrucciones JVM se clasifican en:
Carga y almacenamiento desde variables locales a pila de operandos.
Instrucciones aritméticas
Instrucciones de conversión de tipos
Instrucción de gestión de la pila de operandos
Instrucciones de transferencia de control
13
Departamento de Lenguajes y Sistemas Informáticos
Procesadores de Lenguajes
Instrucciones de llamada a método y retorno
Instrucciones dedicadas a elevar excepción y monitorización
A continuación mostramos un resumen de la mayoría de estas instrucciones:
Tipo Referencia
Carga una referencia desde un array de referencias.
aaload
(..., referencia_array, indice => ..., valor)
Almacenar una referencia en un array de referencias.
aastore
(..., referencia_array, indice, valor => ...)
Cargar referencia nula.
aconst_null
(...,=> ..., null)
Cargar referencia desde variable local indexada por indice.
aload indice
(...,=> ..., referencia)
Crea en el montón un nuevo array de referencias. indice1 + indice2 constituye un índice a una
referencia simbólica array, clase o interfaz en la tabla de constantes.
anewarray indice1 indice2
(...,numero de componentes => ..., referencia_array).
Devuelve referencia desde método.
areturn
(..., referencia =>)
Almacenar referencia en variable local.
astore indice
(..., referencia => ...)
Comparación de dos referencias. indice1+indice2 representa el offset al que se salta si la
comparación es cierta. Si la comparación falla, el programa sigue por la instrucción siguiente a la
que indica el contador de programa.
if_acmpeq indice1 indice2
if_acmpne indice1 indice2
if_acmpgt indice1 indice2
if_acmplt indice1 indice2
if_acmpge indice1 indice2
if_acmple indice1 indice2
(..., valor1, valor2 => ...)
(..., valor1, valor2 => ...)
(..., valor1, valor2 => ...)
(..., valor1, valor2 => ...)
(..., valor1, valor2 => ...)
(..., valor1, valor2 => ...)
14
Departamento de Lenguajes y Sistemas Informáticos
Procesadores de Lenguajes
Tipo Byte
Carga un byte o boolean desde un array.
baload
(..., referencia, indice => ..., valor)
Almacena un byte o boolean a un array.
bstore
(..., referencia, indice, valor => ...)
Carga byte.
bipush byte
(...,=> ..., valor)
Tipo Char
Carga char desde array
caload
(...,referencia, indice => ..., valor)
Almacenar en una array de caracteres.
castore
(..., referencia, indice, valor => ...)
Tipo Double
Convierte double a float
d2f
(..., valor => ..., resultado)
Convierte double a int
d2i
(..., valor => ..., resultado)
Convierte double a long
d2l
(..., valor => ..., resultado)
Suma para el tipo double
dadd
(..., valor1, valor2 => ..., resultado)
Carga double desde array
daload
(...,referencia, indice => ..., valor)
Almacenar en una array de tipo double.
dastore
(..., referencia, indice, valor => ...)
Comparación tipo double. El resultado es 1/0/-1 si valor1 es mayor/igual/menor que valor2. (ver
nota aclaratoria sobre el uso de NaN)
dcmpg
(..., valor1, valor2 => ..., resultado)
dcmpl
(..., valor1, valor2 => ..., resultado)
Divide para el tipo double
ddiv
(..., valor1, valor2 => ..., resultado)
15
Departamento de Lenguajes y Sistemas Informáticos
Procesadores de Lenguajes
Carga double desde variable local
dload indice
(..., => ..., valor)
Multiplica para el tipo double
dmul
(..., valor1, valor2 => ..., resultado)
Cambio signo para el tipo double
dneg
(..., valor => ..., resultado)
Devuelve double desde un método.
dreturn
(..., valor =>)
Almacenar double en una variable local.
dstore indice
(...,valor => ...)
Tipo Float
Similar a las instrucciones del tipo double.
Tipo int
Convierte int a byte
i2b
(..., valor => ..., resultado)
Convierte int a char
i2c
(..., valor => ..., resultado)
Convierte int a double
i2d
(..., valor => ..., resultado)
Convierte int a float
i2f
(..., valor => ..., resultado)
Convierte int a long
i2l
(..., valor => ..., resultado)
Convierte int a short
i2s
(..., valor => ..., resultado)
Suma para el tipo int
iadd
(..., valor1, valor2 => ..., resultado)
Carga int desde array
iaload
(...,referencia, indice => ..., valor)
Almacenar en una array de tipo int.
iastore
(..., referencia, indice, valor => ...)
16
Departamento de Lenguajes y Sistemas Informáticos
Procesadores de Lenguajes
Almacenar una constante tipo int desde el slot n de constantes.
iconst_<n>
(..., => ...,<n>)
Devuelve int desde un método.
ireturn
(..., referencia, indice, valor => ...)
Comparación de dos enteros. indice1+indice2 representa el offset al que se salta si la
comparación es cierta. Si la comparación falla, el programa sigue por la instrucción siguiente a la
que indica el contador de programa.
if_icmpeq indice1 indice2
if_icmpne indice1 indice2
if_icmpgt indice1 indice2
if_icmplt indice1 indice2
if_icmpge indice1 indice2
if_icmple indice1 indice2
(..., valor1, valor2 => ...)
(..., valor1, valor2 => ...)
(..., valor1, valor2 => ...)
(..., valor1, valor2 => ...)
(..., valor1, valor2 => ...)
(..., valor1, valor2 => ...)
Incrementa la variable local referenciada ubicada en el slot indice con el valor de la constante
constante
iinc indice constante
(...=> ...)
Carga int desde variable local
iload indice
(..., => ..., valor)
Almacenar int en una variable local.
istore indice
(...,valor => ...)
Tipo Long
Similar a las instrucciones para el tipo int.
Manipulación de la pila
Duplica la cima de la pila.
dup
(...,valor => ..., valor, valor)
Instrucciones asociadas a objetos:
Carga campo desde un objeto. indice1+ indice2 identifica una referencia simbólica al atributo en
la tabla de constantes.
getfield indice1, indice2
(...,referencia => ..., valor)
Establece el valor de un campo de objeto. indice1+ indice2 identifica una referencia simbólica al
atributo en la tabla de constantes.
putfield indice1, indice2
(...,referencia, valor => ...)
17
Departamento de Lenguajes y Sistemas Informáticos
Procesadores de Lenguajes
Invoca un método desde un objeto. indice1+ indice2 identifica una referencia simbólica al método
en la tabla de constantes.
invokevirtual indice1, indice2
(...,referencia, arg1, ..., argN => ...)
Instrucciones asociadas a clases:
Carga campo de clase (estático). indice1+ indice2 identifica una referencia simbólica a la clase
en la tabla de constantes.
getstatic indice1, indice2
(..., => ..., valor)
Establece el valor de un campo de clase (estático). indice1+ indice2 identifica una referencia
simbólica a la clase en la tabla de constantes.
putstatic indice1, indice2
(..., valor => ...)
Invoca un método de clase (estático). indice1+ indice2 identifica una referencia simbólica al
método de clase en la tabla de constantes.
invokestatic indice1, indice2
(..., arg1, ...argN => ..., valor)
Salto incondicional:
Salto incondicional. indice1+ indice2 representa un offset para el contador de programa.
goto indice1, indice2
(..., => ..., valor)
Creación de objetos:
Crea un objeto. indice1+ indice2 representa un índice para referenciar la clase dentro de la zona
de código.
new indice1, indice2
(..., => ..., referencia)
Jamaica. Ensamblador para JVM
Jamaica es un ensamblador para JVM con la capacidad programar clases al estilo Java, de usar
etiquetas simbólicas y de nombrar variables y atributos en vez de utilizar índices. Además,
Jamaica cuenta con un conjunto de macros para simplificar la programación. Para más
información, consultar http://judoscript.org/articles/jamaica.html
A continuación se muestran dos ejemplos de código Jamaica, uno sin macros y otro con macros:
18
Departamento de Lenguajes y Sistemas Informáticos
Procesadores de Lenguajes
public class C
{
int contador;
public C( ){
iconst_0
putfield count int
return
}
public void inc(int c) {
aload_0
getfield contador int
iload c
iadd
putfield contador int
return
}
public void imprimir( ) throws IOException {
getstatic System.out PrintStream
aload this
invokevirtual PrintStream.println(Object) void
return
}
}
public class C
{
int contador;
public C( ){
%set contador = 0
}
public void inc(int c) {
%load contador
%load c
iadd
putfield contador int
}
public void imprimir( ) throws IOException {
%println this
}
}
Reglas léxicas en Jamaica
Los ficheros Jamaica aceptan comentarios de línea y comentarios multilínea al estilo Java.
Los identificadores en Jamaica siguen las mismas reglas léxicas que los identificadores Java.
Los mnemónicos de las operaciones son palabras reservadas.
Los tipos primitivos, nombres de clases e interfaces y nombres de arrays son los mismos que los
nombres de tipos en Java.
Todas las macros comienzan con %
19
Departamento de Lenguajes y Sistemas Informáticos
Procesadores de Lenguajes
Los bytecodes y macros no terminan con ‘;’
Clases e Interfaces en Jamaica
Las clases e interfaces Jamaica se declaran con la misma sintaxis que Java. Jamaica define la
importación por defecto de los paquetes java.lang.*, java.io.*, java.util.*
Ejemplo:
package A;
public class Elemento
{
private int e;
public Elemento()
{
aload this
invokespecial super()void
return
}
public int consultar()
{
aload this
getfield e int
ireturn
}
public void modificar(int i)
{
aload this
iload i
putfield e int
return
}
}
Juego de Instrucciones de Jamaica
Carga de constantes
Las constantes pueden ser de tipo int, long, float, string y null.
La instrucción aconst_null carga la constante null.
Las instrucciones bipush numero y sipush numero cargan valores enteros de 1 o 2 bytes
respectivamente.
Las instrucciones iconst_m1, iconst_0, ..., iconst_5 carga las constantes int –1,0,..,5
respectivamente.
20
Departamento de Lenguajes y Sistemas Informáticos
Procesadores de Lenguajes
Las instrucciones lconst_0 y lconst_1 carga las constantes long 0 y 1 respectivamente.
Las instrucciones fconst_0, ..., fconst_2 carga las constantes float 0.,..,2. respectivamente.
Las instrucciones dconst_0 y dconst_1 carga las constantes double 0 y 1 respectivamente.
Sin embargo Jamaica dispone de lcd como instrucción general de carga de constantes:
ldc 129834
ldc “pepe”
ldc 5.5
Variables
La instrucción <tipo>load carga un valor de una variable a la pila donde tipo = i, l, f , d, a (int,
long, float, double y reference respectvamente)
La instrucción <tipo>store carga un valor desde la pila a una variable donde tipo = i, l, f , d, a
(int, long, float, double y reference respectvamente)
.
Sinónimos: aload_0 y aload this
Ejemplo : intercambio de valores entre dos variables enteras.
iload x
istore tmp
iload y
istore x
iload tmp
istore y
Formaciones
La instrucción <tipo>aload carga un valor de una componente de un formación a la pila donde
tipo = i, l, f , d, a (int, long, float, double y reference respectvamente)
La instrucción <tipo>astore carga un valor desde la pila a una componente de una formación
donde tipo = i, l, f , d, a (int, long, float, double y reference respectvamente)
.
Ejemplo : almacenar el valor de una variable x en la componente 1 de un vector llamado
formacion.
aload formacion
iconst_1
iload x
iastore
21
Departamento de Lenguajes y Sistemas Informáticos
Procesadores de Lenguajes
Atributos
La instrucción getfield [nombre_clase.]nombre_atributo tipo carga en la pila el valor del
atributo atributo desde una instancia de clase.
La instrucción getstatic [nombre_clase.]nombre_atributo tipo carga en la pila el valor del
atributo atributo desde una clase no instanciable.
La instrucción putfield [nombre_clase.]nombre_atributo tipo carga el valor de la cima de la
pila en el atributo atributo desde una instancia de clase.
La instrucción putstatic [nombre_clase.]nombre_atributo tipo carga el valor de la cima de la
pila en el atributo atributo desde una clase no instanciable.
Método
La instrucción invokevirtual [nombre_clase.]nombre_metodo signatura llamada a un método
desde una instancia de clase donde signatura = ( [Tipo (, Tipo)*]) Tipo.
La instrucción invokestatic [nombre_clase.]nombre_metodo signatura llamada a un método
desde una clase no instanciable donde signatura = ( [Tipo (, Tipo)*]) Tipo.
La instrucción invokespecial [nombre_clase.]nombre_metodo signatura llamada a un
método constructor donde signatura = ( [Tipo (, Tipo)*]) Tipo.
Creación de objetos
La instrucción new nombre_clase crea una instancia de la clase nombrada en la cima de la
pila.
Instrucciones aritméticas y lógicas
Las instrucciones <tipo>add <tipo>sub <tipo>mul <tipo>div suman, restan, mutiplican
y dividen dos operados de la pila dejando el resultado en la pila.
Las instrucciones <tipo>and <tipo>or <tipo>xor realizan un and, or xor de dos operados de
la pila dejando el resultado en la pila.
Aunque JVM contemplan la existencia del tipo boolean, sus valores se representan con enteros
(0 para el valor falso y 1 para el valor cierto).
La instrucción iinc variable incremento incrementa una variable sin usar la pila.
22
Departamento de Lenguajes y Sistemas Informáticos
Procesadores de Lenguajes
Manipulaciones de la pila
La instrucción pop se utiliza para desapilar y la instrucción dup para duplicar la cima.
Saltos incondicionales
La instrucción goto etiqueta se utiliza para pasar el control a la instrucción referenciada por
etiqueta.
La instrucción return se utiliza para finalizar procedimientos.
La instrucción <tipo>return se utiliza para finalizar funciones.
Saltos condicionales
La instrucción if_icmp<op> etiqueta se utiliza para comparar dos enteros en la pila y saltar a
etiqueta en consecuencia donde op = eq (igual), ne (distinto), lt (menor), le (menor o igual),
gt (mayor) o ge (mayor o igual).
La instrucción if_acmpeq etiqueta se utiliza para saber si son iguales dos referencias en la pila
y saltar a etiqueta en consecuencia.
La instrucción if_acmpne etiqueta se utiliza para saber si son iguales dos referencias en la pila
y saltar a etiqueta en consecuencia.
La instrucción if_null etiqueta se utiliza para saber es nula la referencia en la pila.
La instrucción lcmp compara dos long en la pila y deja el resultado en la pila. Las instrucciones
fcmpl/fcmpg compara si un float es menor/mayor que otro en la pila y deja el resultado en la
pila.Las instrucciones dcmpl/dcmpg compara si un double es menor/mayor que otro en la pila y
deja el resultado en la pila. Entonces se utiliza la instrucción if<op> etiqueta para saltar a
etiqueta en consecuencia donde op = eq, ne, lt, le, gt y ge.
Macros
Las siguientes instrucciones son propias de Jamaica y no existen en el juego original de
instrucciones JVM:
23
Departamento de Lenguajes y Sistemas Informáticos
Procesadores de Lenguajes
(%println | %print | %flush) [parametro (, parametro)*] donde parametro =
constante|nombre([parametro])*
Impresión en la salida estándar (out). Estas macros pueden tener un número variable de
argumentos como en Java.
%load parametro
Cargar constante, variable, atributo o componente de formación en la pila.
%set parametro = parametro
Almacenamiento de constante, variable, atributo o componente de formación en un atributo,
variable o componente de formación.
%object nombre_clase
Crear objeto.
Compilando para la JVM en Ensamblador Jamaica
Los siguientes ejemplos muestran la compilación en Jamaica de distintos fragmentos de un
programa en un lenguaje de programación de alto nivel.
Constantes, variables y estructuras de control
Fragmento programa en
lenguaje de alto nivel
entero i; //X
i=1; // A
mientras (i<=10) hacer // B
si (i<5) entonces
// C
escribir(i); // D
finsi
i:=i+1; // E
finmientras
Traducción Jamaica (bajo nivel)
int i;
//X
bipush 1 // A
istore i // A
goto bucle_1_test // B
bucle_1_cuerpo:
iload i
// C
bipush 5 // C
if_icmplt si_2_cuerpo // C
goto fin_si_2_cuerpo
// C
si_2_cuerpo:
// D
%println i // D
goto fin_si_2_cuerpo // D
fin_si_2_cuerpo: // D
iinc i 1
// E
bucle_1_test:
// B
iload i
// B
bipush 10
// B
if_icmple bucle_1_cuerpo// B
24
Departamento de Lenguajes y Sistemas Informáticos
Procesadores de Lenguajes
Asignaciones y expresiones aritméticas
Fragmento programa
en lenguaje de
alto nivel
entero x;
entero y;
x = 2 * x + y;
Traducción Jamaica (bajo nivel)
int x;
int y;
bipush 2
iload x
imul
iload y
iadd
istore x
Fragmento programa en
lenguaje de alto nivel
real x;
real y;
x = 2 * x + y;
//Parte
//Parte
//Parte
//Parte
//Parte
//Parte
izq.
izq.
izq.
izq.
izq.
der.
asignación
asignación
asignación
asignación
asignación
asignación
Traducción Jamaica (bajo nivel)
float x;
float y;
ldc 2.0
fload x
fmul
fload y
fadd
fstore x
25
Departamento de Lenguajes y Sistemas Informáticos
Procesadores de Lenguajes
Clases instanciables, objetos, atributos y métodos
Fragmento programa en lenguaje de
alto nivel
inst clase Elemento
{
oculto entero e;
Traducción Jamaica (bajo nivel)
public class Elemento
{
private int e;
consultar() dev entero
{
dev objeto.e;
}
modificar(entero i)
{
objeto.e:=i;
}
public Elemento()
{
aload this
invokespecial super()void
return
}
public int consultar()
{
aload this
getfield e int
ireturn
}
public void modificar(int i)
{
aload this
iload i
putfield e int
return
}
}
}
26
Departamento de Lenguajes y Sistemas Informáticos
Procesadores de Lenguajes
Fragmento programa en
lenguaje de alto nivel
clase Programa2
{
inicio(){
Elemento e;
Pila p;
entero i;
entero tmp;
Traducción Jamaica (bajo nivel)
public class Programa2
{
public static void
main(String[] args) {
Elemento e;
Pila p;
int i;
int tmp;
e := crear(Elemento)
i := 1
mientras (i<=10) hacer
e.modificar(i);
p.apilar(e);
tmp := e.consultar();
escribir(tmp);
i:=i+1;
finmientras
}
%object Elemento
astore e
bipush 1
istore i
goto bucle_1_test
bucle_1_cuerpo:
aload e
iload i
invokevirtual
Elemento.modificar(int)void
aload e
invokevirtual
Elemento.consultar()int
istore tmp
%println tmp
iinc i 1
bucle_1_test:
iload i
bipush 10
if_icmple bucle_1_cuerpo
}
}
}
27
Departamento de Lenguajes y Sistemas Informáticos
Procesadores de Lenguajes
Clases no instanciables
Fragmento programa en lenguaje Traducción Jamaica (bajo nivel)
de alto nivel
clase Elemento
public class Elemento
{
{
oculto entero e;
private static int e;
consultar() dev entero
{
dev e;
}
modificar(entero i)
{
e:=i;
}
}
clase Programa
{
inicio(){
entero i;
entero tmp;
public static int consultar(){
getstatic e int
ireturn
}
public static void
modificar(int i){
iload i
putstatic e int
return
}
}
public class Programa
{
public static
void main(String[] args) {
i := 1
mientras (i<=10) hacer
Elemento.modificar(i);
tmp := Elemento.consultar();
escribir(tmp);
i:=i+1;
finmientras
}
int i;
int tmp;
bipush 1
istore i
goto bucle_1_test
bucle_1_cuerpo:
iload i
invokestatic
Elemento.modificar(int)void
invokestatic
Elemento.consultar()int
istore tmp
%println tmp
iinc i 1
bucle_1_test:
iload i
bipush 10
if_icmple bucle_1_cuerpo
}
}
}
28
Departamento de Lenguajes y Sistemas Informáticos
Procesadores de Lenguajes
Formaciones
Fragmento programa en lenguaje Traducción Jamaica (bajo nivel)
de alto nivel
inst clase Pila2
public class Pila2{
{
private Elemento almacen[];
oculto formacion 100
private int cima;
Elemento almacen;
oculto entero cima;
public Pila2() {
aload this
iniciar(){
invokespecial super()void
cima := -1;
aload this
}
bipush 100
anewarray Elemento
apilar(Elemento e){
putfield almacen Elemento[]
cima := cima + 1;
return
componente(almacen,
}
cima):= e;
}
public void iniciar(){
aload this
desapilar() dev Elemento{
iconst_m1
Elemento tmp;
putfield cima int
tmp :=
return
componente(almacen,cima);
}
cima := cima - 1;
public Elemento desapilar(){
dev tmp
Elemento tmp;
}
aload this
}
getfield almacen Elemento[]
aload this
getfield cima int
aaload
astore tmp
aload this
aload this
getfield cima int
ldc 1
isub
putfield cima int
aload tmp
areturn
}
public void apilar(Elemento e){
aload this
aload this
getfield cima int
ldc 1
iadd
putfield cima int
aload this
getfield almacen Elemento[]
aload this
getfield cima int
aload e
aastore
return
}
}
29
Departamento de Lenguajes y Sistemas Informáticos
Procesadores de Lenguajes
Módulos (I)
Fragmento programa en
lenguaje de alto nivel
modulo B
Traducción Jamaica (bajo nivel)
importacion:
public class Elemento
{
private int e;
exportacion:
package B;
inst clase Elemento
{
oculto entero e;
%default_constructor <public>
public int consultar(){
aload this
getfield e int
ireturn
}
public void modificar(int i){
aload this
iload i
putfield e int
return
}
consultar() dev entero
{
dev objeto.e;
}
modificar(entero i)
{
objeto.e:=i;
}
}
}
implementacion:
30
Departamento de Lenguajes y Sistemas Informáticos
Procesadores de Lenguajes
Fragmento programa en
lenguaje de alto nivel
modulo A
Traducción Jamaica (bajo nivel)
importacion: B
import B.Elemento;
exportacion:
public class Programa4{
package A;
clase Programa4
{
inicio(){
Elemento e;
Pila p;
entero i;
entero tmp;
public static void
main(String[] args) {
Elemento e;
Pila p;
int i;
int tmp;
%object Elemento
astore e
e := crear(Elemento)
i := 1
mientras (i<=10) hacer
e.modificar(i);
p.apilar(e);
tmp := e.consultar();
escribir(tmp);
i:=i+1;
finmientras
}
bipush 1
istore i
goto bucle_1_test
bucle_1_cuerpo:
aload e
iload i
invokevirtual
Elemento.modificar(int)void
aload e
invokevirtual
Elemento.consultar()int
istore tmp
%println tmp
iinc i 1
bucle_1_test:
iload i
bipush 10
if_icmple bucle_1_cuerpo
}
}
implementacion:
}
31
Departamento de Lenguajes y Sistemas Informáticos
Procesadores de Lenguajes
Módulos (II)
Fragmento programa en
lenguaje de alto nivel
modulo A
Traducción Jamaica (bajo nivel)
importacion:
public class Programa5{
package A;
exportacion:
public static void
main(String[] args) {
A.Elemento e;
Pila p;
int i;
int tmp;
clase Programa5
{
inicio(){
Elemento e;
Pila p;
entero i;
entero tmp;
%object A.Elemento
astore e
e := crear(Elemento)
bipush 1
istore i
goto bucle_1_test
bucle_1_cuerpo:
aload e
iload i
invokevirtual
A.Elemento.modificar(int)void
aload e
invokevirtual
A.Elemento.consultar()int
istore tmp
%println tmp
iinc i 1
bucle_1_test:
iload i
bipush 10
if_icmple bucle_1_cuerpo
}
i := 1
mientras (i<=10) hacer
e.modificar(i);
p.apilar(e);
tmp := e.consultar();
escribir(tmp);
i:=i+1;
finmientras
}
}
implementacion:
inst clase Elemento
{
oculto entero e;
consultar() dev entero
{
dev objeto.e;
}
modificar(entero i)
{
objeto.e:=i;
}
}
}
32
Departamento de Lenguajes y Sistemas Informáticos
Procesadores de Lenguajes
Fragmento programa en
lenguaje de alto nivel
Traducción Jamaica (bajo nivel)
package A;
class Elemento
{
private int e;
%default_constructor <public>
private int consultar(){
aload this
getfield e int
ireturn
}
private void modificar(int i){
aload this
iload i
putfield e int
return
}
}
33
Descargar