lunes, 20 de febrero de 2017

Analizador lexico

Analizador Léxico.

Concepto 1:

Un analizador léxico y/o analizador léxico gráfico (en ingles scanner) es la primera fase de un compilador consistente en un programa que recibe como entrada el código fuente de otro programa (secuencia de caracteres) y produce una salida compuesta de tokens (componentes léxicos) o símbolos. Estos tokens sirven para una posterior etapa del proceso de traducción, siendo la entrada para el analizador sintáctico (en inglés parser).

La especificación de un lenguaje de programación a menudo incluye un conjunto de reglas que definen el léxico Estas reglas consisten comúnmente en expresiones regulares que indican el conjunto de posibles secuencias de caracteres que definen un token o lexema.
En algunos lenguajes de programación es necesario establecer patrones para caracteres especiales (como el espacio en blanco) que la gramática pueda reconocer sin que constituya un token en sí.

Concepto 2:

Analizador léxico.

 Se encarga de buscar los componentes léxicos o palabras que componen el programa fuente, según unas reglas o patrones. La entrada del analizador léxico podemos definirla como una secuencia de caracteres.
El analizador léxico tiene que dividir la secuencia de caracteres en palabras con significado propio y después convertirlo a una secuencia de terminales desde el punto de vista del analizador sintáctico, que es la entrada del analizador sintáctico. 

El analizador léxico reconoce las palabras en función de una gramática regular de manera que sus NO TERMINALES se convierten en los elementos de entrada de fases posteriores. En LEX, por ejemplo, esta gramática se expresa mediante expresiones regulares.


Funciones del Analizador Léxico  
  • Convierte el programa fuente en una cadena de tokens 
  • Para reconocer el token usa un patrón, una regla que describe como se forman las cadenas que corresponden a un token.
  • Salta comentarios y espacios en blanco (tabuladores, saltos de línea...) 
  • Tener el registro de la línea del archivo fuente que está siendo analizada
  • Genera mensajes de error léxico, y se recupera del error 
  • Convierte los valores literales al tipo que corresponda
  • Si la entrada debe obedecer a un formato, verifica el formato Ej. Fortran, Cobol 

Ejemplos:



Representación de los componentes de un Analizador Léxico.

En la fase de análisis, los términos componentes léxicos (token), patrón y lexemase emplean con significados específicos. Un analizador léxico, inicialmente lee loslexemas y le asigna un significado propio.

•Componente léxico es la secuencia lógica y coherente de caracteres relativo auna categoría: identificador, palabra reservada, literales (cadena/numérica),operador o carácter de puntuación, además de que un componente léxico puedetener uno o varios lexemas.

• Patrón es una regla que genera la secuencia de caracteres que puede
representar a un determinado componente léxico (expresión regular).

• Lexema es una cadena de caracteres que concuerda con un patrón que describe
un componente léxico (valor de cadena).

Ejemplo de una cadena de código: const pi = 3.1416;

El analizador léxico recoge información sobre los componentes léxicos en susatributos asociados.

Los tokens influyen en las decisiones del análisis sintáctico, ylos atributos, en la traducción de los tokens.

En la práctica los componentes léxicos suelen tener solo un atributo.Para efectos de diagnostico, puede considerarse tanto el lexema para unidentificador como el numero de línea en el que se encontró por primera vez.





Analizador semántico.

Concepto 1:

Se compone de un conjunto de rutinas independientes, llamadas por los analizadores morfológico y sintáctico.
El análisis semántico utiliza como entrada el árbol sintáctico detectado por el análisis sintáctico para comprobar restricciones de tipo y otras limitaciones semánticas y preparar la generación de código.
En compiladores de un solo paso, las llamadas a las rutinas semánticas se realizan directamente desde el analizador sintáctico y son dichas rutinas las que llaman al generador de código. El instrumento más utilizado para conseguirlo es la gramática de atributos.
En compiladores de dos o más pasos, el análisis semántico se realiza independientemente de la generación de código, pasándose información a través de un archivo intermedio, que normalmente contiene información sobre el árbol sintáctico en forma linealizada (para facilitar su manejo y hacer posible su almacenamiento en memoria auxiliar).
En cualquier caso, las rutinas semánticas suelen hacer uso de una pila (la pila semántica) que contiene la información semántica asociada a los operandos (y a veces a los operadores) en forma de registros semánticos.

Concepto 2:

Los programas que se compilan no son solamente cadenas de símbolos sin ningún significado que pueden ser aceptadas como correctas o no por una máquina abstracta. El lenguaje no es más que el vehículo por el cual se intenta transmitir una serie de instrucciones a un procesador para que éste las ejecute produciendo unos resultados. 

Por ello, la tarea del compilador requiere la extracción del contenido semántico incluido en las distintas sentencias del programa.

Por todo ello, se hace necesario dotar al compilador de una serie de rutinas auxiliares que permitan captar todo aquello que no se ha expresado mediante la sintaxis del lenguaje y todo aquello que hace descender a nuestro lenguaje de programación de las alturas de una máquina abstracta hasta el nivel de un computador real. A todas estas rutinas auxiliares se les denomina genéricamente análisis semántico.

El análisis semántico, a diferencia de otras fases, no se realiza claramente diferenciado del resto de las tareas que lleva a cabo el compilador, más bien podría decirse que el análisis semántico completa las dos fases anteriores de análisis lexicográfico y sintáctico incorporando ciertas comprobaciones que no pueden asimilarse al mero reconocimiento de una cadena dentro de un lenguaje.

La fase de análisis semántico revisa el programa fuente para tratar de encontrar errores semánticos y reúne la información sobre los tipos para la fase posterior de generación de código. En ella se utiliza la estructura jerárquica determinada por la fase de análisis sintáctico para identificar los operadores y operandos de expresiones y proposiciones.

Un componente importante del análisis semántico es la Verificación de Tipos. Aquí, el compilador verifica si cada operador tiene operandos permitidos por la especificación del lenguaje fuente.




Ejemplos:

Sea la expresión
    int a,b,c;
    a/(b+c^2)
El árbol sintáctico es:
          /
      ---------
      |       |
      a       +
          ---------
          |       |
          b       ^
              ---------
              |       |
              c       2
De la instrucción declarativa, la tabla de símbolos y el analizador morfológico obtenemos los atributos de los operandos:
          /
      ---------
      |       |
      a       +
     int  ---------
          |       |
          b       ^
         int  ---------
              |       |
              c       2
             int     int
Propagando los atributos obtenemos:
          / int
      ---------
      |       |
      a       + int
     int  ---------
          |       |
          b       ^ int
         int  ---------
              |       |
              c       2
             int     int
Si la expresión hubiera sido
    a/(b+c^-2)
El árbol sintáctico sería el mismo, sustituyendo 2 por -2. Sin embargo, la propagación de atributos sería diferente:
          / real
      ---------
      |       |
      a       + real
     int  ---------
          |       |
          b       ^ real
         int  ---------
              |       |
              c      -2
             int     int
En algún caso podría llegar a producirse error (p.e. si / representara sólo la división entera).
Si la expresión hubiera sido
    int a,b,c,d;
    a/(b+c^d)
El árbol sintáctico sería el mismo, sustituyendo 2 por d. Sin embargo, la propagación de atributos sería incompleta:
          / {int,real}
      ---------
      |       |
      a       + {int,real}
     int  ---------
          |       |
          b       ^ {int,real}
         int  ---------
              |       |
              c       d
             int     int

Segundo ejemplo:

real a;
    int b,c;
    a:=b+c
El árbol sintáctico es:
          :=
      ---------
      |       |
      a       +
    real  ---------
          |       |
          b       c
         int     int
Existen dos conversiones posibles:
          := real                     := real
      ---------                   ---------
      |       |                   |       |
      a       + real              a       + int
    real  ---------             real  ---------
          |       |                   |       |
          b       c                   b       c
         int     int                 int     int

Representación de los componentes.



miércoles, 8 de febrero de 2017

Diseño de compiladores

Programa 1:

#include <iostream>
#include <stdlib.h>

using namespace std;

int main()
{
char c1='C',c2='r',c3='i',c4='s',c5='t',c6='i',c7='a',c8='n';

cout << "TU NOMBRE TIENE 8 CARACTERES\n";
cout<< c1;
cout<< c2;
cout<< c3;
cout<< c4;
cout<< c5;
cout<< c6;
cout<< c7;
cout<< c8;
cout <<endl;
system("PAUSE");
return 0;
}


Segundo programa:
#include <iostream>

using namespace std;

int main()
{
int caracteres=0;
char c1[1],c2[1],c3[1],c4[1],c5[1],c6[1],c7[1],c8[1],c9[1],c10[1]; 

cout << "CUANTOS CARACTERES TIENE TU NOMBRE\n";
cin >> caracteres;
if(caracteres == 4)
{
cout<< "INTRUCUDE TU PRIMER LETRA:\n";
cin >> c1;
cout<< "INTRUCUDE LA SIGUIENTE:\n";
cin >> c2;
cout<< "INTRUCUDE LA SIGUIENTE:\n";
cin >> c3;
cout<< "INTRUCUDE LA SIGUIENTE:\n";
cin >> c4;
}
if(caracteres == 5)
{
cout<< "INTRUCUDE TU PRIMER LETRA";
cin >> c1;
cout<< "INTRUCUDE LA SIGUIENTE:\n";
cin >> c2;
cout<< "INTRUCUDE LA SIGUIENTE:\n";
cin >> c3;
cout<< "INTRUCUDE LA SIGUIENTE:\n";
cin >> c4;
cout<< "INTRUCUDE LA SIGUIENTE:\n";
cin >> c5;
}
if(caracteres == 6)
{
cout<< "INTRUCUDE TU PRIMER LETRA:\n";
cin >> c1;
cout<< "INTRUCUDE LA SIGUIENTE:\n";
cin >> c2;
cout<< "INTRUCUDE LA SIGUIENTE:\n";
cin >> c3;
cout<< "INTRUCUDE LA SIGUIENTE:\n";
cin >> c4;
cout<< "INTRUCUDE LA SIGUIENTE:\n";
cin >> c5;
cout<< "INTRUCUDE LA SIGUIENTE:\n";
cin >> c6;
}
if(caracteres == 7)
{
cout<< "INTRUCUDE TU PRIMER LETRA:\n";
cin >> c1;
cout<< "INTRUCUDE LA SIGUIENTE:\n";
cin >> c2;
cout<< "INTRUCUDE LA SIGUIENTE:\n";
cin >> c3;
cout<< "INTRUCUDE LA SIGUIENTE:\n";
cin >> c4;
cout<< "INTRUCUDE LA SIGUIENTE:\n";
cin >> c5;
cout<< "INTRUCUDE LA SIGUIENTE:\n";
cin >> c6;
cout<< "INTRUCUDE LA SIGUIENTE:\n";
cin >> c7;
}
if(caracteres == 8)
{
cout<< "INTRUCUDE TU PRIMER LETRA:\n";
cin >> c1;
cout<< "INTRUCUDE LA SIGUIENTE:\n";
cin >> c2;
cout<< "INTRUCUDE LA SIGUIENTE:\n";
cin >> c3;
cout<< "INTRUCUDE LA SIGUIENTE:\n";
cin >> c4;
cout<< "INTRUCUDE LA SIGUIENTE:\n";
cin >> c5;
cout<< "INTRUCUDE LA SIGUIENTE:\n";
cin >> c6;
cout<< "INTRUCUDE LA SIGUIENTE:\n";
cin >> c7;
cout<< "INTRUCUDE LA SIGUIENTE:\n";
cin >> c8;
}
if(caracteres == 9)
{
    cout<< "INTRUCUDE TU PRIMER LETRA:\n";
cin >> c1;
cout<< "INTRUCUDE LA SIGUIENTE:\n";
cin >> c2;
cout<< "INTRUCUDE LA SIGUIENTE:\n";
cin >> c3;
cout<< "INTRUCUDE LA SIGUIENTE:\n";
cin >> c4;
cout<< "INTRUCUDE LA SIGUIENTE:\n";
cin >> c5;
cout<< "INTRUCUDE LA SIGUIENTE:\n";
cin >> c6;
cout<< "INTRUCUDE LA SIGUIENTE:\n";
cin >> c7;
cout<< "INTRUCUDE LA SIGUIENTE:\n";
cin >> c8;
cout<< "INTRUCUDE LA SIGUIENTE:\n";
cin >> c9;
}
if(caracteres == 10)
{
cout<< "INTRUCUDE TU PRIMER LETRA";
cin >> c1;
cout<< "INTRUCUDE LA SIGUIENTE:\n";
cin >> c2;
cout<< "INTRUCUDE LA SIGUIENTE:\n";
cin >> c3;
cout<< "INTRUCUDE LA SIGUIENTE:\n";
cin >> c4;
cout<< "INTRUCUDE LA SIGUIENTE:\n";
cin >> c5;
cout<< "INTRUCUDE LA SIGUIENTE:\n";
cin >> c6;
cout<< "INTRUCUDE LA SIGUIENTE:\n";
cin >> c7;
cout<< "INTRUCUDE LA SIGUIENTE:\n";
cin >> c8;
cout<< "INTRUCUDE LA SIGUIENTE:\n";
cin >> c9;
cout<< "INTRUCUDE LA SIGUIENTE:\n";
cin >> c10;
cout<< "Tu numero de caracteres es:\n";
cout << caracteres <<endl;
}
system("PAUSE");
return 0;
}



martes, 7 de febrero de 2017

Arquitectura de los compiladores e intérpretes

Unidad 1

Arquitectura de los compiladores e intérpretes

Unidad de competencia:


Determina la estructura general de los compiladores de intérpretes con base a su funcionalidad de sus etapas y fases. 

Compiladores e intérpretes

Compiladores.

Concepto: Es aquel traductor que tiene como entrada una sentencia en lenguaje formal y como salida tiene un fichero ejecutable, es decir, realiza una traducción de un código de alto nivel a código máquina (también se entiende por compilador aquel programa que proporciona un fichero objeto en lugar del ejecutable final).

Características:

◦ Generación de código intermedio.
◦ Generación de código objeto.
◦ Optimización (mezclada con las anteriores).

Aplicabilidad

Se realizara la implementación de un Generador de Analizadores Léxicos.
Se realizara la implementación de un Generador de Analizadores Sintácticos.
 Se realizara la implementación de un Analizador Semántico y la de un Generador de Código Intermedio.
Se realizara la implementación de un Intérprete de un lenguaje XXX2.

Ejemplo (1):


Ejemplo (2):



Interpretes:

Concepto: Es como un compilador, solo que la salida es una ejecución. El programa de entrada se reconoce y ejecuta a la vez. No se produce un resultado físico (código máquina) sino lógico (una ejecución). Hay lenguajes que sólo pueden ser interpretados, como p.ej. SNOBOL (StriNg Oriented SimBOlyc Language), LISP (LISt Processing), algunas versiones de BASIC (Beginner’s All-purpose Symbolic Instruction Code), etc.

Características:

Mientras que el objetivo de los compiladores es obtener una traduccion del programa fuente a otro lenguaje, los interpretes tienen como objeto la obtencion de los resultados del programa. Para ello deben realizar dos tareas: analizar su entrada y llevar a cabo las acciones especificadas por ella. 

La parte de analisis puede realizarse de manera identica a como se lleva a cabo en los compiladores. Es la parte de sıntesis la que se diferencia sustancialmente

◦ Generación directa de resultados.
◦ Generación de código intermedio e interpretación del código intermedio.

Aplicabilidad

Se realizara la implementación de un Intérprete de un lenguaje XXX2.
Se realizara la implementación de un Compilador de un lenguaje XXX3.

Ejemplo (1):



Ejemplo (2):




Arquitectura de los compiladores e intérpretes

Interprete:

A la hora de construir un intérprete es conveniente utilizar una Representación Interna (RI) del lenguaje fuente a analizar.

De esta forma, la organización interna de la mayoría de los intérpretes se descompone en los módulos: Traductor a Representación Interna: Toma como entrada el código del programa P en Lenguaje Fuente, lo analiza y lo transforma a la representación interna correspondiente a dicho programa P. Representación Interna (P/RI): La representación interna debe ser consistente con el programa original.

Entre los tipos de representación interna, los árboles sintácticos son los más utilizados y, si las características del lenguaje lo permiten, pueden utilizarse estructuras de pila para una mayor eficiencia. Tabla de símbolos: Durante el proceso de traducción, es conveniente ir creando una tabla con información relativa a los símbolos que aparecen. 
Características:


 Aplicabilidad:

Compiladores:

Verifica en forma permanente la correcta escritura de las sentencias, teniendo como parámetros un conjunto de reglas denominada ―gramática‖.

Interpretes:

Intérpretes de Comandos: Los sistemas operativos cuentan con intérpretes de comandos como el Korn-Shell, C-Shell, JCL, etc. Estos intérpretes toman un lenguaje fuente que puede incluir sentencias de control (bucles, condiciones, asignaciones, etc.) y ejecutan los diferentes comandos a medida que aparecen en el lenguaje.

Lenguajes basados en Escritos (Scripting Languages), diseñados como herramientas que sirvan de enlace entre diferentes sistemas o aplicaciones. Suelen ser interpretados con el fin de admitir una mayor flexibilidad a la hora de afrontar las peculiaridades de cada sistema. 

Podrían destacarse Perl, Tcl/Tk, JavaScript, WordBasic [Ousterhout 97]
Entornos de Programación: Existen ciertos lenguajes que contienen características que impiden su compilación o cuya compilación no es efectiva. Estos lenguajes suelen disponer de un complejo entorno de desarrollo interactivo con facilidades para la depuración de programas. Entre estos sistemas pueden destacarse los entornos de desarrollo para Lisp, Visual Basic, Smalltalk, etc.

Ejemplo (1):




Máquinas virtuales

Concepto: Entendamos por una máquina virtual a un Software que simula a una computadora y puede instalar y usar otros sistemas operativos de forma simultanea como si fuese una computadora real sobre nuestro sistema operativo1.

Una característica esencial de las máquinas virtuales es que los procesos que ejecutan están limitados por los recursos y abstracciones proporcionados por ellas. Estos procesos no pueden escaparse de esta "computadora virtual". Uno de los usos domésticos más extendidos de las máquinas virtuales es ejecutar sistemas operativos para "probarlos".

De esta forma podemos ejecutar un sistema operativo que queramos probar (GNU/Linux, por ejemplo) desde nuestro sistema operativo habitual (Windows 7 por ejemplo) sin necesidad de instalarlo directamente en nuestra computadora y sin miedo a que se des configure el sistema operativo primario.

Características:

Particionamiento:

El particionamiento es una de las características de las máquinas virtuales que permite que se ejecuten varios sistemas operativos en una misma máquina física y se distribuyan los recursos del sistema entre las máquinas virtuales.

Aislamiento

El aislamiento es aquella característica de las máquinas virtuales que ofrece seguridad a nivel de hardware y el aislamiento por fallas y protege el rendimiento mediante controles de recursos avanzados.

Encapsulación

La encapsulación es una característica de las máquinas virtuales que garantiza que se guarde el estado completo de una ordenador virtual en archivos y que se muevan y copien máquinas virtuales con la misma facilidad que si fueran archivos.

Independencia del hardware

La independencia del hardware es característica de máquinas virtuales que proporciona que se migre cualquier máquina virtual a cualquier servidor físico.

Crear una máquina virtual

Ya conocemos las máquinas virtuales y sus características pero aún no sabemos cómo crear una máquina virtual.
Aplicabilidad:

Varios sistemas operativos distintos pueden coexistir sobre la misma computadora, en sólido aislamiento el uno del otro, por ejemplo para probar un sistema operativo nuevo sin necesidad de instalarlo directamente. La máquina virtual puede proporcionar una arquitectura de instrucciones que sea algo distinta de la verdadera máquina.

Es decir, podemos simular Hardware. Varias máquinas virtuales –cada una con su propio sistema operativo llamado sistema operativo "invitado" o "guest"–, pueden ser utilizadas para consolidar servidores. 

Esto permite que servicios que normalmente se tengan que ejecutar en computadoras distintas para evitar interferencias, se puedan ejecutar en la misma máquina de manera completamente aislada y compartiendo los recursos de una única computadora.

Ejemplos:

·         Virtualbox: Software desarrollado por Oracle. Se trata de un software multiplataforma capaz de virtualizar prácticamente la totalidad de sistemas operativos con arquitectura x86/amd64. Es la máquina virtual multiplataforma, la base de este software dispone de una la licencia GPL2, mientras que el pack de extensiones que añaden funcionalidades están bajo licencia privativa. Virtualbox es gratuito para un uso no comercial.

·         Vmware Workstation Player: Software privativo multiplataforma desarrollado por EMC corporation y que es utilizado ampliamente en el entorno profesional en las áreas del cloud computing entre muchas otras. Al igual que Virtualbox, esta máquina virtual nos permite virtualizar una gran diversidad de sistemas operativos.

Parallels: Aunque se trata de una máquina virtual multiplataforma, acostumbra a ser usado por los usuarios del sistema operativo OS X de Apple que desean virtualizar el sistema operativo Windows. Esta máquina virtual es de pago y únicamente puede virtualizar los sistemas operativos Windows y Mac OS.