Compiladores. Guía 11 1 Facultad: Ingeniería Escuela: Computación Asignatura: Compiladores Tema: INTÉRPRETE ENSAMPOCO/3 Contenido En esta práctica se desarrollará un intérprete de lenguaje ENSAMPOCO/3 para completar el desarrollo de un compilador. Objetivos Específicos Aplicar y lenguaje. definir los conceptos de intérprete de un Material y Equipo Guía de Laboratorio Nº 11. Computadora con DevC++ Introducción Teórica Intérpretes Guía 3 Los intérpretes son programas que simplemente ejecutan las instrucciones que encuentran en el texto fuente. En muchos Guía 4 casos coexisten en memoria el programa fuente y el programa intérprete. Nótese que en este caso todo se hace en tiempo de ejecución. fía Algunos de los lenguajes comúnmente interpretados son el BASIC, LOGO, PROLOG, SMALLTALK, APL, PHP y LISP. Funcionamiento de un intérprete Evidentemente la ejecución de un programa compilado será más rápida que la del mismo programa interpretado. Sin embargo los intérpretes son más interactivos y facilitan la puesta a punto de programas. 2 Compiladores. Guía 11 Algunos lenguajes de programación tan sólo pueden ser interpretados debido a sus características, por ejemplo algunos lenguajes funcionales y lenguajes orientados a objeto. En estos casos existen intérpretes con compiladores incrementales que pueden recompilar los módulos modificados en tiempo de ejecución. Los intérpretes se pueden clasificar desde el punto de vista de su estructura en varios tipos: intérpretes puros, intérpretes avanzados o normales, e intérpretes incrementales. Intérpretes puros Los intérpretes puros son los que analizan una sentencia y la ejecutan, y así sucesivamente todo el programa fuente. Fueron los intérpretes desarrollados en la primera generación de ordenadores, pues permitían la ejecución de largos programas con ordenadores de memoria muy reducida, ya que sólo debían contener en memoria el intérprete y la sentencia a analizar y ejecutar. El principal problema de este tipo de intérpretes es que si a mitad del programa fuente se producen errores, se debe de volver a comenzar el proceso. En la figura se representa el esquema general de un intérprete puro, donde se puede observar que el lenguaje fuente se traduce a una representación interna (texto o binaria) que puede ser almacenada en memoria o en disco. Esta representación interna tiene todas las instrucciones numeradas o colocadas consecutivamente en estructuras de tamaño fijo (por ejemplo un array o posiciones consecutivas de memoria, o un fichero binario de estructuras de tamaño fijo). Mientras se realiza este paso se puede construir la tabla de etiquetas, que es una tablas que contiene una estructura donde están todas las etiquetas y su posición en el programa fuente (las etiquetas se utilizan tanto en las instrucciones de salto como en las llamadas a procedimientos y funciones). Una vez que este proceso ha finalizado, comienza la ejecución por la primera instrucción del código, que se envía al evaluador de instrucciones, éste la ejecuta (recibiendo datos si es necesario o enviando un mensaje de error). El evaluador de instrucciones también determina la instrucción siguiente a ejecutar, en algunos casos previa consulta a la tabla de etiquetas. En el caso de que no haya saltos (GOTO) o llamadas a procedimientos o funciones se ejecuta la siguiente instrucción a la instrucción en curso. Compiladores. Guía 11 3 El evaluador de instrucciones puede utilizar dos métodos de evaluación. El método clásico es la evaluación voraz o ansiosa, donde se evalúan las expresiones completamente. Otro método es la evaluación perezosa, evaluándose sólo la parte necesaria de la expresión (el resto no se evalúa). Esquema general de un intérprete puro Intérpretes avanzados Los intérpretes avanzados o normales incorporan un paso previo de análisis de todo el programa fuente. Generando posteriormente un lenguaje intermedio que es ejecutado por ellos mismos. De esta forma en caso de errores sintácticos no pasan de la fase de análisis. Un ejemplo de intérprete avanzado es el que utiliza el lenguaje Java [JAVAi]. Así un programa en lenguaje java (con la extensión .java) se compila y produce uno o varios ficheros con la extensión .class, estos ficheros están en un formato binario denominado bytecode independiente de plataforma, que se interpreta posteriormente. Esto permite que el bytecode se ejecute en cualquier sistema operativo que disponga de un intérprete de bytecode. Dado que la mayor parte de los navegadores de Internet llevan inmerso un intérprete de bytecode, esto ha permitido al lenguaje Java ser uno de los más utilizados en aplicaciones que usen Internet. 4 Compiladores. Guía 11 Esquema general de un intérprete avanzado Las aplicaciones de los intérpretes avanzados son: • Emulación de arquitecturas de ordenadores. • Método para facilitar la portabilidad de aplicaciones entre distintas arquitecturas de ordenadores. • Fase de prueba de lenguajes y arquitecturas de máquinas abstractas, antes de construir los traductores a código objeto de una arquitectura determinada. Intérpretes incrementales Algunos lenguajes no se pueden compilar, debido a que entre sus características pueden manejar objetos o funciones que no son conocidos en tiempo de compilación, ya que son creados en ejecución. Para este tipo de lenguajes existen los intérpretes incrementales, que permiten compilar los módulos completamente definidos, y recompilar en tiempo de ejecución los nuevos módulos. Están a medio camino entre los intérpretes puros y los intérpretes avanzados o normales. Los intérpretes incrementales tienen gran interés en los lenguajes que permiten no definir los problemas completamente en tiempo de compilación. En estos casos se utilizan evaluadores parciales (partial evaluators) que toman como entrada el programa fuente junto con algunos datos (pero no todos), realizándose los cálculos que se pueden hacer con dicho subconjunto de datos, y produciendo una salida que contiene un residuo del programa fuente que se ha introducido. Compiladores. Guía 11 5 Algunos fabricantes de software denominan a los intérpretes incrementales como compiladores incrementales, debido a que la palabra intérprete está desprestigiada, y prefieren acogerse a la parte del intérprete avanzado. Procedimiento Considerando la clase “Interprete”, desarrolle un proyecto en C++ que solicite el nombre del archivo en ENSAMPOCO/3 y a Guía 3 continuación ejecute el interprete del archivo. InterpreteEnsampoco.h Guía 4 #ifndef INTERPRETE_H #define INTERPRETE_H fía #include <stdio.h> #include <iostream> #include <string.h> #include <math.h> #include <stdlib.h> #define TAM_MAX_INS 20 using namespace std; struct pila { char valor; struct pila *sucesor; }; typedef struct pila ELEMENTO; typedef ELEMENTO *PUNTERO; class Interprete{ FILE *entrada; //Descriptor del fichero de entrada bool esCodigo; int memoria[26]; PUNTERO cabezaPila; char instruccion[TAM_MAX_INS]; char parametro; void analizarInstruccion(); void interpretarInstruccion(); void interpretarInstruccionDeCaracter(); void interpretarInstruccionDeFlotante(); void interpretarInstruccionDeInt(); void iabs(); void add(); void code(); void data(); void div(); void end(); 6 Compiladores. Guía 11 void iexp(); void i2tof(); void itof(); void load(); void mod(); void mul(); void neg(); void output(); void input(); void pusha(); void pushc(); void isin(); void store(); void itan(); int pop(); void push(int valor); void error(int num); public: Interprete (char *unNombreFicheroEntrada); ~Interprete (void); }; Interprete::Interprete (char *unNombreFicheroEntrada){ int i; if ((entrada = fopen(unNombreFicheroEntrada,"r")) == NULL ) error(0); for(i= 0; i<26; i++) memoria[i] = 0;//inicialización a cero while (!feof(entrada)){ analizarInstruccion(); if (instruccion[0] != '\0') interpretarInstruccion(); else{ cout<<"FIN\n"; return; } } } /**********************************************************/ Interprete::~Interprete (void){ fclose(entrada); } /**********************************************************/ void Interprete::analizarInstruccion(){ int n; if ((instruccion[0] = fgetc(entrada)) != EOF){ n = 1; while (((instruccion[n] = fgetc(entrada)) != ' ') && (instruccion[n] != '\n')) Compiladores. Guía 11 7 n++; if (instruccion[n] != '\n'){ while (((parametro = fgetc(entrada)) == ' ') && (parametro != '\n')) ; instruccion[n] = '\0'; if (parametro != '\n'){ while ((n = fgetc(entrada)) != '\n'); } } else{ instruccion[n] = '\0'; } } else instruccion[0]='\0'; } void Interprete::interpretarInstruccion() { switch (instruccion[0]) { case 'C': interpretarInstruccionDeCaracter(); break; case '.': if (strcmp (instruccion,".DATA") == 0) data(); else if (strcmp (instruccion,".CODE") == 0) code(); else error(1); break; case 'E': if (strcmp (instruccion,"END") == 0) end(); else error(1); break; case 'F': interpretarInstruccionDeFlotante(); break; case 'I':if (strcmp(instruccion,"ITOF") == 0) itof(); else if (strcmp(instruccion,"I2TOF") == 0) i2tof(); else interpretarInstruccionDeInt(); break; default : error(1); break; } return; } void Interprete::interpretarInstruccionDeCaracter() { if(!esCodigo) return; switch(instruccion[1]){ case 'I': if (strcmp(instruccion,"CINPUT") == 0) input(); 8 Compiladores. Guía 11 else error(1); break; case 'L': if (strcmp(instruccion,"CLOAD") == 0) load(); else error(1); break; case 'O': if (strcmp(instruccion,"COUTPUT") == 0) output(); else error(1); break; case 'P': if (strcmp(instruccion,"CPUSHA") == 0) pusha(); else error(1); break; case 'S': if (strcmp(instruccion,"CSTORE") == 0) store(); else error(1); break; default: error(1); break; } } void Interprete::interpretarInstruccionDeFlotante(){ if(!esCodigo) return; switch(instruccion[1]){ case 'A': if(strcmp(instruccion,"FABS")==0) iabs(); else if (strcmp(instruccion,"FADD") == 0) add(); else error(1); break; case 'D': if (strcmp(instruccion,"FDIV") == 0) div(); else error(1); break; case 'E': if (strcmp(instruccion,"FEXP") == 0) iexp(); else error(1); break; case 'I': if (strcmp(instruccion,"FINPUT") == 0) input(); else error(1); break; case 'L': if (strcmp(instruccion,"FLOAD") == 0) load(); else error(1); Compiladores. Guía 11 9 break; case 'M': if (strcmp(instruccion,"FMUL") == 0) mul(); else if (strcmp(instruccion,"FMOD") == 0) mod(); else error(1); break; case 'N': if (strcmp(instruccion,"FNEG") == 0) neg(); else error(1); break; case 'O': if (strcmp(instruccion,"FOUTPUT") == 0) output(); else error(1); break; case 'P': if (strcmp(instruccion,"FPUSHA") == 0) pusha(); else error(1); break; case 'S': if (strcmp(instruccion,"FSIN") == 0) isin(); else if (strcmp(instruccion,"FSTORE") == 0) store(); else error(1); break; case 'T': if (strcmp(instruccion,"FTAN") == 0) itan(); else error(1); break; default: error(1); break; } } void Interprete::interpretarInstruccionDeInt() { if(!esCodigo) return; switch(instruccion[1]){ case 'A': if (strcmp(instruccion,"IABS") == 0) iabs(); else if(strcmp(instruccion,"IADD")==0) add(); else error(1); break; case 'D': if (strcmp(instruccion,"IDIV") == 0) div(); else error(1); break; case 'E': if (strcmp(instruccion,"IEXP") == 0) iexp(); 10 Compiladores. Guía 11 else error(1); break; case 'I': if (strcmp(instruccion,"IINPUT") == 0) input(); else error(1); break; case 'L': if (strcmp(instruccion,"ILOAD") == 0) load(); else error(1); break; case 'M': if (strcmp(instruccion,"IMUL") == 0) mul(); else if (strcmp(instruccion,"IMOD") == 0) mod(); else error(1); break; case 'N': if (strcmp(instruccion,"INEG") == 0) neg(); else error(1); break; case 'O': if (strcmp(instruccion,"IOUTPUT") == 0) output(); else error(1); break; case 'P':if (strcmp(instruccion,"IPUSHC") == 0) pushc(); else if (strcmp(instruccion,"IPUSHA") == 0) pusha(); else error(1); break; case 'S': if (strcmp(instruccion,"ISIN")==0) isin(); else if (strcmp(instruccion,"ISTORE") == 0) store(); else error(1); break; case 'T': if(strcmp(instruccion,"ITAN")==0) itan(); else error(1); break; default: error(1); break; } } //**** INSTRUCCIONES DE ENSAMPOCO ***** void Interprete::iabs(){ push(abs(pop())); } void Interprete::add() { Compiladores. Guía 11 11 PUNTERO p; p = cabezaPila; cabezaPila = cabezaPila->sucesor; cabezaPila->valor = cabezaPila->valor+p->valor; free((PUNTERO)p); } void Interprete::code() { esCodigo=true; } void Interprete::data(){ esCodigo=false; } void Interprete::div() { PUNTERO p; p = cabezaPila; cabezaPila = cabezaPila->sucesor; cabezaPila->valor = (int) (cabezaPila->valor / p->valor); free((PUNTERO)p); } void Interprete::end() { return; } void Interprete::iexp() { double a = pop(); double b = pop(); //System.out.println(b+" ^ "+a+"= "+ Math.pow(b, a)); push((int)pow(b, a)); } void Interprete::i2tof() { //de momento nada la conversion de entero a float es //automatica en c++ } void Interprete::input() { int valor; cout<<"Entrada "<<parametro<<" > "; cin>>valor; memoria[parametro - 'a'] = valor; } void Interprete::itof(){ //de momento nada la conversion de entero a float es //automatica en c++ } void Interprete::load() { cabezaPila->valor = memoria[cabezaPila->valor-'a']; } void Interprete::mod() { 12 Compiladores. Guía 11 PUNTERO p; p = cabezaPila; cabezaPila = cabezaPila->sucesor; cabezaPila->valor = (( (int)cabezaPila->valor) % ( (int) p->valor) ); free((PUNTERO)p); } void Interprete::mul() { PUNTERO p; p = cabezaPila; cabezaPila = cabezaPila->sucesor; cabezaPila->valor = cabezaPila->valor*p->valor; free((PUNTERO)p); } void Interprete::neg() { cabezaPila->valor = -cabezaPila->valor; } void Interprete::output() { cout<<"Salida "<<parametro<<" > "<< memoria[parametro'a']<<endl; system("pause"); } void Interprete::pusha() { PUNTERO p; p = (PUNTERO) malloc (sizeof(ELEMENTO)); p->valor = parametro; p->sucesor = cabezaPila; cabezaPila = p; } void Interprete::pushc() { PUNTERO p; p = (PUNTERO) malloc (sizeof(ELEMENTO)); p->valor = parametro-'0'; p->sucesor = cabezaPila; cabezaPila = p; } void Interprete::isin(){ push((int)sin(pop())); } void Interprete::store() { PUNTERO p,q; p = cabezaPila; q = cabezaPila->sucesor; if (q->sucesor!=NULL) cabezaPila = q->sucesor; else cabezaPila=NULL; memoria[(q->valor)-'a'] = p->valor; free((PUNTERO)p); Compiladores. Guía 11 13 free((PUNTERO)q); } void Interprete::itan(){ push((int)tan(pop())); } //********* FIN DE LAS INSTRUCCIONES DE ENSAMPOCO ******* int Interprete::pop() { int valor = cabezaPila->valor; cabezaPila = cabezaPila->sucesor; return valor; } void Interprete::push(int valor) { PUNTERO nuevo;// = new PUNTERO(); nuevo->valor = valor; nuevo->sucesor = cabezaPila; cabezaPila = nuevo; } void Interprete::error(int num){ // Manejo de errores cout<<"Error "<<num; switch (num){ case 0: cout<<": No encuentra el fichero fuente"<<endl; break; case 1: cout<<": Instrucción desconocida"<<endl;break; case 2: cout<<": PUSHA sin variable"<<endl;break; case 3: cout<<": PUSHC sin constante"<<endl;break; case 4: cout<<": INPUT sin variable"<<endl;break; case 5: cout<<": OUTPUT sin vaariable"<<endl;break; default: cout<<": No documentado"<<endl; break; } } #endif Análisis de resultados Teniendo en cuenta el siguiente código en lenguaje ensampoco/3 ejecute el intérprete implementado anteriormente y describa su funcionamiento. Prueba.poc .DATA INT a INT b FLOAT c FLOAT d 14 Compiladores. Guía 11 .CODE IINPUT a IINPUT b FPUSHA c IPUSHA a ILOAD IPUSHA b ILOAD IADD ITOF FSTORE FPUSHA d IPUSHA a ILOAD IPUSHA b ILOAD INEG IADD ITOF FSTORE FOUTPUT c FOUTPUT d END Responda las siguientes cuestiones: Describa el algoritmo de la clase intérprete. Clasifique y distinga, ¿qué tipo de intérprete es el implementado para ENSAMPOCO/3? Explique, en el caso de ENSAMPOCO/0 que no acepta tipos, ¿cuáles cambios consideraría usted que deberían ser hechos a este intérprete, para interpretar ENSAMPOCO/0? Investigación complementaria Investigue que es un traductor, documente las partes que lo componen y la actividad principal. Además haga un cuadro comparativo entre compiladores, traductores e intérpretes. Bibliografía Cueva, J. lenguaje. (1998). Conceptos Universidad básicos de de procesadores de Oviedo, España. Compiladores. Guía 11 15 Hoja de cotejo: Guía 11: Intérprete de Ensampoco/3 Docente: Tema: Presentación del programa 11 1 Máquina No: Máquina No: Alumno: Máquina No: GL: Alumno: Docente: GL: Docente: GL: Fecha: a EVALUACION % CONOCIMIENTO Del 20 al 30% APLICACIÓN DEL CONOCIMIENTO Del 40% al 60% 1-4 5-7 8-10 Conocimie nto deficient e de los fundament os teóricos Conocimiento y explicación incompleta de los fundamentos teóricos Conocimiento completo y explicación clara de los fundamentos teóricos No tiene actitud proactiva . Actitud propositiva y con propuestas no aplicables al contenido de la guía. Tiene actitud proactiva y sus propuestas son concretas. ACTITUD Del 15% al 30% TOTAL 100% Nota