• No se han encontrado resultados

TEMA 4: INTERPRETACIÓN, COMPILACIÓN

N/A
N/A
Protected

Academic year: 2022

Share "TEMA 4: INTERPRETACIÓN, COMPILACIÓN"

Copied!
19
0
0

Texto completo

(1)

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?

En este punto de procesamiento, 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 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 final del mismo. 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

(2)

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 --> 0 b --> 3 z --> 0 b --> INDEF

¿QUÉ ES LA COMPILACIÓN DE UN LENGUAJE?

Compilar un lenguaje significa compilar cada sentencia del lenguaje. Compilar una sentencia de un lenguaje es traducirla a otro lenguaje preservando en lo posible el significado de la sentencia original. 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 dispone de intérprete. Un ejemplo de lenguaje compilado es Java. El lenguaje destino es un lenguaje ensamblador interpretable (después del ensamblado) en una máquina sin registros (con pila) 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 DEF se corresponde con una memorización en tiempo de compilación de la variable y su correspondiente definición; (3) si la instrucción EVAL está definida (no es una evaluación cíclica) entonces dicha instrucción se corresponderá con una secuencia ordenada de asignaciones para el cierre transitivo de las variables usadas en la definición de la variable evaluada seguida de una impresión por pantalla del valor asociado a ésta última. Si no es así, la instrucción EVAL se correspondería con una impresión por pantalla de valor indefinido para la variable evaluada.

Ejemplo 3: Compilación en Java de programa LF en Ejemplo 1

import java.io.*;

public class _Programa2

(3)

{

public static void main(String[] args) { int b,x,a,z,y;

a=-1;

b=a+1;

System.out.println("(Compilador) b ---> "+b);

a=2;

b=a+1;

System.out.println("(Compilador) b ---> "+b);

y=0;

z=2*y;

System.out.println("(Compilador) z ---> "+z);

System.out.println("(Compilador) b ---> INDEF");

} }

¿CÓMO SE DISEÑA Y CONSTRUYE UN INTÉRPRETE?

La construcción de un intérprete es fuertemente dependiente de la semántica del lenguaje a interpretar. 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. 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 el intérprete.

Ejemplo 3.a: Diseño de Intérprete para el lenguaje LF DISEÑO

--- OBJETIVO:

Intérprete para lenguaje LF.

LF es un lenguaje de programación secuencial.

Las instrucciones se ejecutan una a una desde el comienzo del programa hasta el final del mismo.

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

(4)

indefinido.

Ejecutar una instrucción EVAL x significa evaluar el valor de la expresión asociada a x mostrando por pantalla el

resultado.

DECISIONES DE DISEÑO:

(1) Cada localización del programa tiene estado asociado.

estado = conjunto de definiciones.

VARIABLES x, y, z, a, b;

INSTRUCCIONES

a DEF -1; estado=[(x,0),(y,0),(z,0),(a,-1),(b,0)]

b DEF (a+1); estado=[(x,0),(y,0),(z,0),(a,-1),(b,(a+1))]

EVAL b; estado=[(x,0),(y,0),(z,0),(a,-1),(b,(a+1))]

a DEF 2; estado=[(x,0),(y,0),(z,0),(a,2),(b,(a+1))]

EVAL b; estado=[(x,0),(y,0),(z,0),(a,2),(b,(a+1))]

b DEF b + 1; estado=[(x,0),(y,0),(z,0),(a,2),(b,b+1)]

z DEF 2*y; estado=[(x,0),(y,0),(z,2*y),(a,2),(b,b+1)]

EVAL z; estado=[(x,0),(y,0),(z,2*y),(a,2),(b,b+1)]

EVAL b; estado=[(x,0),(y,0),(z,2*y),(a,2),(b,b+1)]

(2) Interpretar(programa) = interpretar secuencialmente sus instrucciones.

(a) Interpretar(definicion) = actualizar estado con definicion.

variables : #(VARIABLES

(VAR {actualizar estado con una definicion

por defecto para VAR})*)

definicion : #(DEF VAR expr)

{actualizar estado con una definicion expr para VAR}

(b) Interpretar(evaluacion) = calcular el valor de la variable según la semántica de LF e imprimirlo por pantalla.

La semántica de la instrucción de evaluacion es la siguiente:

evaluacion: #(EVAL VAR) {

si (test(VAR)=CICLO) entonces

imprimir_mensaje(VAR,indefinida) si no

valor = calcular_valor_expr(VAR) imprimir_mensaje(VAR,valor) }

Ejemplo de evaluación:

EVAL b; estado=[(x,0),(y,0),(z,0),(a,-1),(b,(a+1))]

(5)

(a) La variable b no es indefinida Su definición en el estado es (a+1)

(b) expandir la definición de variable a en (a+1) produce (-1+1)

reduccir (-1+1) produce 0

se imprimirá por pantalla valor 0 para b

Diseño de test(v):

"testar si una variable v tiene ciclo en su definición"

test(v) devuelve (CICLO/NO CICLO)

d = consultar definición de v en el estado S = extraer el conjunto de variables en d si S es vacio entonces

devolver NO CICLO si no

devolver ciclo([v],S)

ciclo(V,S) devuelve (CICLO/NO CICLO)

si algún elemento en S ocurre en V entonces devolver CICLO

si no

para cada variable z en S

d = consultar definición de z en el estado K = extraer el conjunto de variables en d si (ciclo([V,z],K)=CICLO) entonces

devolver CICLO devolver NO CICLO

Diseño de calcular_valor_expr(expr):

"calcular el valor de una expresión expr"

calcular_valor_expr(expr) devuelve valor:

si expr es igual a #(MAS a:expr b:expr) entonces valor=suma(calcular_valor_expr(a),

calcular_valor_expr(b)) sino

si expr es igual a #(MENOS a:expr b:expr) entonces valor=diferencia(calcular_valor_expr(a),

calcular_valor_expr(b)) sino

si expr es igual #(MENOS a:expr) entonces valor=menosunario(calcular_valor_expr(a)) sino

si expr es igual a #(POR a:expr b:expr) entonces valor=producto(calcular_valor_expr(a),

calcular_valor_expr(b)) sino

(6)

si expr es igual a NUMERO entonces valor=NUMERO

sino

si expr es igual a VAR entonces

d=consultar la definición de VAR en el estado

valor=calcular_valor_expr(d) fsi

...

fsi

Gramática atribuida resultante:

--- estado (global)

programa : #(PROGRAMA variables instrucciones)

variables : #(VARIABLES

(VAR {actualizar estado con una definicion por defecto para VAR})*)

instrucciones : #(INSTRUCCIONES (definicion|evaluacion)*)

definicion : #(DEF VAR expr)

{actualizar estado con una definicion expr para VAR}

evaluacion: #(EVAL VAR) {

si (test(VAR)=CICLO) entonces

imprimir_mensaje(VAR,indefinida) si no

valor = calcular_valor_expr(VAR) imprimir_mensaje(VAR,valor) }

expr : #(MAS expr expr) | #(MENOS expr expr) | #(MENOS expr) | #(POR expr expr) | NUMERO

| VAR

Una vez diseñado el intérprete, tenemos que testarlo con algunos ejemplos paradigmáticos. Si superamos satisfactoriamente éstos tests, procedemos a implementar el intérprete desde la especificación propuesta por el diseño.

Ejemplo 3.b: Implementación de Intérprete para el lenguaje LF //Intérprete de programa LF

//(Implementación de InterpreteDiseño.txt) header{

(7)

import java.util.*;

import antlr.*;

}

class Interprete extends TreeParser;

options {

importVocab = Anasint;

} {

//(1) Cada localización del programa tiene estado asociado.

//estado = conjunto de definiciones.

Map<String,AST> estado = new HashMap<String,AST>();

//Clase ANTLR para copiar árboles de sintaxis abstracta (AST)

ASTFactory factory = new ASTFactory();

//(2.a) Definición por defecto

public AST definicion_por_defecto(String texto, int tipo){

AST resultado = new CommonAST();

resultado.setType(tipo);

resultado.setText(texto);

return resultado;

}

//(2.a) Actualizar estado con una definicion //por defecto para variable var

void declarar_variable(String var){

estado.put(var, definicion_por_defecto("0", NUMERO));

}

//(2.a) Actualizar estado con una definicion expr para var void definir_variable(String var, AST expr){

if (estado.get(var)!=null)

estado.put(var, factory.dupTree(expr));

}

//(2.b) Testar ciclicidad en una variable v boolean test(String v){

try{

AST d = estado.get(v);

Set<String> S = extraer_el_conjunto_de_variables(d);

if (S.isEmpty()) return false;

else {

List<String>aux=new LinkedList<String>();

aux.add(v);

return ciclo(aux,S);

}

}catch(RecognitionException excep)

{System.out.println(excep.toString());return false;}

}

boolean contiene_algun_elemento(List<String> V,Set<String> S){

boolean r = false;

for (String e:S){

(8)

if (V.contains(e)) r=true;

}

return r;

}

//(2.b) Testar ciclicidad en la definición de una variable v boolean ciclo(List<String> V,Set<String> S){

try{

if (contiene_algun_elemento(V,S)) return true;

else

for (String z:S){

AST d = estado.get(z);

Set<String> K = extraer_el_conjunto_de_variables(d);

List<String> aux = new LinkedList<String>();

aux.addAll(V);aux.add(z);

if (ciclo(aux,K)) return true;

}

return false;

}catch(RecognitionException excep)

{System.out.println(excep.toString());return false;}

} }

programa : #(PROGRAMA variables instrucciones) ; 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)

{ //(2.b) Interpretar(evaluacion) = calcular el valor de la variable

// según la semántica de LF e imprimirlo por pantalla.

if (test(v.getText()))

System.out.println("(Intérprete) Evaluación indefinida para la variable:"+v.toString());

else{

Integer r = calcular_valor_expr(v);

System.out.println("(Intérprete) "+v.toString()+

" ---> "+r);

} } ;

expr : #(MAS expr expr)

| (#(MENOS expr expr)) => #(MENOS expr expr) | #(MENOS expr)

| #(POR expr expr) | NUMERO

(9)

| VAR ;

//Función auxiliar usada en test y ciclo.

//Extraer el conjunto de variables de una expresión extraer_el_conjunto_de_variables

returns[Set<String> s = new HashSet<String>()]

{Set<String> aux;} :

#(MAS aux=extraer_el_conjunto_de_variables s=extraer_el_conjunto_de_variables) {s.addAll(aux);}

| #(POR aux=extraer_el_conjunto_de_variables s=extraer_el_conjunto_de_variables) {s.addAll(aux);}

| (#(MENOS . .)) =>

#(MENOS aux=extraer_el_conjunto_de_variables s=extraer_el_conjunto_de_variables) {s.addAll(aux);}

| #(MENOS s=extraer_el_conjunto_de_variables) | NUMERO

| a:VAR {s.add(a.getText());}

;

// Calcular el valor de una expresion expr // valor = calcular_valor_expr(expr)

calcular_valor_expr returns [Integer valor = 0] {Integer aux1,aux2;}:

#(MAS aux1=calcular_valor_expr aux2=calcular_valor_expr) {valor=aux1+aux2;}

| (#(MENOS . .)) =>

#(MENOS aux1=calcular_valor_expr aux2=calcular_valor_expr) {valor=aux1-aux2;}

| #(MENOS aux1=calcular_valor_expr){valor=-aux1;}

| #(POR aux1=calcular_valor_expr aux2=calcular_valor_expr) {valor=aux1*aux2;}

| n:NUMERO {valor=new Integer(n.getText());}

| v:VAR {AST d=estado.get(v.getText());

valor=calcular_valor_expr(d);} ;

¿CÓMO SE DISEÑA Y CONSTRUYE UN COMPILADOR?

Al igual que el intérprete, 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 hemos visto, la asociación del valor de una expresión a una variable constituye el

principal problema a la hora de traducir LF a Java. Las variables Java son imperativas: esta

asociación 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.

(10)

Hemos visto que las principales correspondencias en nuestro problema son: (1) el programa LF se corresponde con una clase Java con main, (2) la instrucción DEF se corresponde con una memorización en tiempo de compilación de la variable y su correspondiente definición; (3) si la instrucción EVAL está definida (no es una evaluación cíclica) entonces dicha instrucción se corresponderá con una secuencia ordenada de asignaciones para el cierre transitivo de las variables usadas en la definición de la variable evaluada seguida de una impresión por pantalla del valor asociado a ésta última. Si no es así, la instrucción EVAL se correspondería con una impresión por pantalla de valor indefinido para la variable evaluada.

El siguiente ejemplo muestra el compilador de LF a Java.

Ejemplo 4a: Diseño de Compilador para el lenguaje LF DISEÑO

--- OBJETIVO:

Compilador de lenguaje LF a lenguaje Java LF es un lenguaje de programación secuencial.

Las instrucciones se ejecutan una a una desde el comienzo del programa hasta el final del mismo.

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.

DECISIONES DE DISEÑO:

La asociación del valor de una expresión a una variable constituye el principal problema a la hora de traducir LF a Java.

Las variables Java son imperativas: esta asociación 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.

La traducción de LF a Java se basa en:

El programa LF se traduce a una clase Java con main,

La instrucción DEF se traduce a una memorización en tiempo de compilación de la variable y su correspondiente

definición;

Si la instrucción EVAL está definida (no es una evaluación cíclica), se traduce a una secuencia ordenada de asignaciones

(11)

para todas las variables usadas en la definición de la variable evaluada seguida de una impresión por pantalla del valor asociado a ésta última.

Si no es así, la instrucción EVAL se traduce a una impresión por pantalla de valor indefinido para la variable evaluada.

1.Definir el esquema de traducción de un programa LF

import java.io.*;

public class _Programa {

public static void main(String[] args) {

generar codigo para declaracion de variables generar codigo para cada instrucción

} }

2.generar codigo para declaracion de variables y

almacenar definiciones por defecto para cada variable.

(necesario para generar código de evaluación)

VARIABLES x,y,z,a,b; ---> int x,y,z,a,b;

estado=[(x,0),(y,0),(z,0),(a,0),(b,0))]

3.generar codigo para cada instrucción

3.1 No se genera código para la definición.

Almacenar cada definición.

(necesario para generar código de evaluación)

a DEF -1; estado=[(x,0),(y,0),(z,0),(a,-1),(b,0))]

b DEF (a+1); estado=[(x,0),(y,0),(z,0),(a,-1),(b,(a+1))]

3.2 generar codigo para evaluación de variables

(a) Las variables con definición cíclica hay que

detectarlas y simular en Java su evaluación en LF .

(b) Las variables con definición no cíclica hay que simular en Java su evaluación en LF. La definición no cíclica obliga a secuenciar las variables de las que depende las variables evaluadas.

si (test(VAR)=CICLO) entonces

generar codigo para evaluación de variable indefinida

si no

generar codigo para evaluación de variable definida

Gramática atribuida resultante:

--- estado (global)

programa : {generar codigo declaracion clase

(12)

generar codigo cabecera main}

#(PROGRAMA variables instrucciones) {generar codigo fin main

generar codigo fin clase}

variables : #(VARIABLES

(VAR {actualizar estado con una definicion por defecto para VAR})*)

{generar codigo declaracion variables}

instrucciones : #(INSTRUCCIONES (definicion|evaluacion)*)

definicion : #(DEF VAR expr)

{actualizar estado con una definicion expr para VAR}

evaluacion: #(EVAL VAR) {

si (test(VAR)=CICLO) entonces

generar codigo para evaluación de variable VAR indefinida

si no

generar codigo para evaluación de variable VAR definida

} expr : #(MAS expr expr) | #(MENOS expr expr) | #(MENOS expr) | #(POR expr expr) | NUMERO

| VAR

--- generar codigo declaracion clase:

escribir en fichero:

import java.io.*;

public class _Programa{

generar codigo cabecera main:

escribir en fichero: public static void main(String[] args) {

generar codigo fin main:

escribir en fichero: }

generar codigo fin clase:

escribir en fichero: }

generar codigo para evaluación de variable VAR indefinida:

escribir en fichero:

System.out.println("(Compilador) "+VAR+" ---> INDEF");

generar codigo para evaluación de variable VAR definida:

dep = calcular las variables de las que depende VAR por niveles

(13)

para cada nivel n en dep (de menor a mayor) para cada variable v en el nivel n

def = consultar definicion de v en estado cod = generar codigo para expresión def escribir en fichero:

System.out.println("(Compilador) "+v+"="+cod+";");

generar codigo para expresión:

si la expresion es un NUMERO entonces el código generado es el propio numero

si la expresion es una VAR entonces el código generado es la propia VAR

si la expresion es una suma entonces el código generado es la composición del

código generado para la subexpresión izquierda, el código + y

código generado para la subexpresión derecha

si la expresión es una resta, producto o división entonces el código generado es

similar al código de la suma salvo el operador aritmético.

(14)

Ejemplo 4b: Implementación de Compilador para el lenguaje LF header{

import java.util.*;

import antlr.*;

import java.io.*;

}

class Compilador extends TreeParser; //Compilador options

{

importVocab = Anasint;

} {

FileWriter fichero;

private void abrir_fichero(){

try{

fichero = new FileWriter("src/_Programa.java");

}catch(IOException e)

{System.out.println("open_file (exception):

"+e.toString());}

}

private void cerrar_fichero(){

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> almacen_definiciones = new Hashtable<String, AST>(); //definiciones

public void actualizar_estado_definicion_por_defecto(String

(15)

var){

AST nodo = new CommonAST();

nodo.setType(NUMERO);

nodo.setText("0");

almacen_definiciones.put(var,nodo);

}

public void actualizar_estado_con_definicion(String var, AST expr){

almacen_definiciones.put(var,factory.dupTree(expr));

}

public void

generar_codigo_evaluacion_variable_indefinida(String var){

try{

gencode_espacios();

fichero.write("System.out.println(\"(Compilador) "+

var+" ---> INDEF\""+");\n");

}catch(IOException e){}

}

public void

calcular_dependencias_por_niveles(Set<String>nivel, List<Set<String>>r){

try{

if (!nivel.isEmpty()){

Set<String>nivel_sig=new HashSet<String>();

for(String v:nivel){

AST def = almacen_definiciones.get(v);

Set<String>aux=

extraer_el_conjunto_de_variables(def);

nivel_sig.addAll(aux);

}

if (!nivel_sig.isEmpty()) r.add(nivel_sig);

calcular_dependencias_por_niveles(nivel_sig,r);

}

}catch(RecognitionException e){}

}

public void

generar_codigo_evaluacion_variable_definida(String var){

Set<String>n=new HashSet<String>();

n.add(var);

List<Set<String>>r=new LinkedList<Set<String>>();

r.add(n);

calcular_dependencias_por_niveles(n,r);

Set<String>visitados=new HashSet<String>();

for (int i=r.size()-1;i>=0;i--)

(16)

for(String v:r.get(i))

if (!visitados.contains(v)){

visitados.add(v);

AST exp = almacen_definiciones.get(v);

String txt_exp = gencode_exp(exp);

gencode_espacios();

try{

fichero.write(v+"="+txt_exp+";\n");

}catch(IOException e){}

}

gencode_evaluar_variable(var);

}

private void generar_codigo_declaracion_clase(){

try{

gencode_espacios();

fichero.write("import java.io.*;\n");

gencode_espacios();

fichero.write("public class _Programa"+"\n");

gencode_espacios();

fichero.write("{\n");

espacios++;

}catch(IOException e){}

}

private void generar_codigo_cabecera_main(){

try{

gencode_espacios();

fichero.write("public static void main(String[] args) {\n");

espacios++;

}catch(IOException e){}

}

private void generar_codigo_fin_main(){

try{

espacios--;

gencode_espacios();

fichero.write("}\n");

}catch(IOException e){}

}

private void generar_codigo_fin_clase(){

try{

espacios--;

gencode_espacios();

fichero.write("}");

}catch(IOException e){}

}

(17)

private void generar_codigo_declaracion_variables(){

try{

Set<String> aux = almacen_definiciones.keySet();

if (aux.size()>0){

gencode_espacios();

fichero.write("int ");

Iterator<String> it = aux.iterator();

while (it.hasNext()){

fichero.write(it.next());

if (it.hasNext()) fichero.write(",");

}

fichero.write(";\n");

}

}catch(IOException e){}

}

public void gencode_evaluar_variable(String var){

try{

gencode_espacios();

fichero.write("System.out.println(\"(Compilador) "+

var+" ---> \""+"+"+var+");\n");

}catch(IOException e){}

}

//Generar código Java para una expresión LF 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;

} }

(18)

// Test de evaluaciones indefinidas.

boolean test(String v){

try{

AST d = almacen_definiciones.get(v);

Set<String> S = extraer_el_conjunto_de_variables(d);

if (S.isEmpty()) return false;

else {

List<String>aux=new LinkedList<String>();

aux.add(v);

return ciclo(aux,S);

}

}catch(RecognitionException excep)

{System.out.println(excep.toString());return false;}

}

boolean contiene_algun_elemento(List<String> V,Set<String> S){

boolean r = false;

for (String e:S){

if (V.contains(e)) r=true;

}

return r;

}

//Testar ciclicidad en la definición de una variable v boolean ciclo(List<String> V,Set<String> S){

try{

if (contiene_algun_elemento(V,S)) return true;

else

for (String z:S){

AST d = almacen_definiciones.get(z);

Set<String> K =

extraer_el_conjunto_de_variables(d);

List<String> aux = new LinkedList<String>();

aux.addAll(V);aux.add(z);

if (ciclo(aux,K)) return true;

}

return false;

}catch(RecognitionException excep)

{System.out.println(excep.toString());return false;}

} }

//Reglas de la gramática atribuida programa : {abrir_fichero();

generar_codigo_declaracion_clase();

generar_codigo_cabecera_main();}

#(PROGRAMA variables instrucciones) {generar_codigo_fin_main();

generar_codigo_fin_clase();

cerrar_fichero();}

; variables :

(19)

#(VARIABLES (v:VAR

{actualizar_estado_definicion_por_defecto(v.getText());})*) {generar_codigo_declaracion_variables();}

;

instrucciones : #(INSTRUCCIONES (definicion|evaluacion)*) ;

definicion : #(DEF v:VAR e:expr)

{actualizar_estado_con_definicion(v.getText(),e);}

;

evaluacion : #(EVAL v:VAR)

{if (test(v.getText()))

generar_codigo_evaluacion_variable_indefinida(v.getText());

else

generar_codigo_evaluacion_variable_definida(v.getText());

}

; expr : #(MAS expr expr) | #(POR expr expr) | NUMERO

| VAR

| (#(MENOS expr expr)) => #(MENOS expr expr) | #(MENOS expr)

;

//Función auxiliar usada en test y ciclo.

//Extraer el conjunto de variables de una expresión extraer_el_conjunto_de_variables

returns[Set<String> s = new HashSet<String>()]

{Set<String> aux;} :

#(MAS aux=extraer_el_conjunto_de_variables s=extraer_el_conjunto_de_variables) {s.addAll(aux);}

| #(POR aux=extraer_el_conjunto_de_variables s=extraer_el_conjunto_de_variables) {s.addAll(aux);}

| (#(MENOS . .)) =>

#(MENOS aux=extraer_el_conjunto_de_variables s=extraer_el_conjunto_de_variables) {s.addAll(aux);}

| #(MENOS s=extraer_el_conjunto_de_variables) | NUMERO

| a:VAR {s.add(a.getText());}

;

Referencias

Documento similar