sábado, 10 de octubre de 2009

Pseudocodigo

1. Inicio
2. Declara Matriz.
int matriz [7][5]={
{0,1,2,3,4,},
{1,2,2,6,6,},
{2,3,3,6,6,},
{3,4,4,6,6,},
{4,5,6,6,6,},
{5,5,5,6,5,},
{6,6,6,6,6,}};
3. Declara Cadena
char cadena [50];
4. Declara C para saber el estado int c=0;
5. Declara Estado
int estado=1
6.Muestra mensaje cout <<"Introduce Cadena";
7. Introduce Cadena
cin >> cadena;
8. Analizar Cadena
for (int n= strlen (cadena)-1;n>=0; n--)
{ for(int n=0 ;n<=0 ;n++)
9. Verifica que carácter que cae a la matriz [7] [5]
switch (cadena [n] )
{ case 'a' ; c=1 ; break;
case 'b' : c=2 ; break;
defaul: c=3; break; }
10. Asignar la posicion en la matriz a estado
estado=matriz [estado][c] ; }
11. Verifica Estado
if (estado ==5 )
12. Muestra el siguiente
mesaje cout <<"Cadena Aceptado"
13. Si no
14. Muestra el siguiente mensaje
cout<<"Cadena Rechazada";
15. Fin del programa.

Compiladores

INTRODUCCIÓN

En 1946 se desarrolló el primer ordenador digital. En un principio, estas máquinas ejecutaban instrucciones consistentes en códigos numéricos que señalan a los circuitos de la máquina los estados correspondientes a cada operación. Esta expresión mediante códigos numéricos se llamó Lenguaje Máquina, interpretado por un secuenciador cableado o por un microprograma. Pero los códigos numéricos de las máquinas son engorrosos. Pronto los primeros usuarios de estos ordenadores descubrieron la ventaja de escribir sus programas mediante claves más fáciles de recordar que esos códigos numéricos; al final, todas esas claves juntas se traducían manualmente a Lenguaje Máquina. Estas claves constituyen los llamados lenguajes ensambladores, que se generalizaron en cuanto se dio el paso decisivo de hacer que las propias máquinas realizaran el proceso mecánico de la traducción. A este trabajo se le llama ensamblar el programa.

Clasificación

Los compiladores son programas de traducción insertados en la memoria por el sistema operativo para convertir programas de cómputo en pulsaciones electrónicas ejecutables (lenguaje de máquina). Los compiladores pueden ser de:

Øuna sola pasada: examina el código fuente una vez, generando el código o programa objeto.
Øpasadas múltiples: requieren pasos intermedios para producir un código en otro lenguaje, y una pasada final para producir y optimizar el código producido durante los pasos anteriores.
ØOptimación: lee un código fuente, lo analiza y descubre errores potenciales sin ejecutar el programa.

Compiladores incrementales: generan un código objeto instrucción por instrucción (en vez de hacerlo para todo el programa) cuando el usuario teclea cada orden individual.

El otro tipo de compiladores requiere que todos los enunciados o instrucciones se compilen conjuntamente.

Ensamblador: el lenguaje fuente es lenguaje ensamblador y posee una estructura sencilla.

Compilador cruzado: se genera código en lenguaje objeto para una máquina diferente de la que se está utilizando para compilar. Es perfectamente normal construir un compilador de Pascal que genere código para MS-DOS y que el compilador funcione en Linux y se haya escrito en C++.

Compilador con montador: compilador que compila distintos módulos de forma independiente y después es capaz de enlazarlos.

Autocompilador: compilador que está escrito en el mismo lenguaje que va a compilar. Evidentemente, no se puede ejecutar la primera vez. Sirve para hacer ampliaciones al lenguaje, mejorar el código generado, etc.

Metacompilador: es sinónimo de compilador de compiladores y se refiere a un programa que recibe como entrada las especificaciones del lenguaje para el que se desea obtener un compilador y genera como salida el compilador para ese lenguaje.

El desarrollo de los metacompiladores se encuentra con la dificultad de unir la generación de código con la parte de análisis. Lo que sí se han desarrollado son generadores de analizadores léxicos y sintácticos. Por ejemplo, los conocidos:


LEX: generador de analizadores léxicos.


YACC: generador de analizadores sintácticos desarrollados para UNIX. Los inconvenientes que tienen son que los analizadores que generan no son muy eficientes.

Descompilador: es un programa que acepta como entrada código máquina y lo traduce a un lenguaje de alto nivel, realizando el proceso inverso a la compilación.


Conceptualmente un compilador opera en fases. Cada una de las cuales transforma el programa fuente de una representación en otra.

Diagrama Earley

Es un algoritmo no determinístico de análisis sintáctico para las gramáticas libres de contexto descrito originalmente por Jay Earley. Se ordena, a los lados de los algoritmos CYK y GLR, entre los algoritmos que usan la noción de reparto (de cálculos y de estructuras) y que construyen todos los análisis posibles de una frase (y no sólo uno de estos análisis). Es uno de los algoritmos no determinísticos que usan ideas de la programación dinámica.

En caso de que la gramática fuese ambigüa (algo habitual en gramáticas del lenguaje natural), el tiempo de reconocimiento será proporcional al cubo de la longitud de la cadena de entrada, aunque para ciertas gramáticas el tiempo medio se puede reducir a cuadrático e incluso lineal.

El algoritmo de Earley se basa en tres operaciones o acciones que se realizan iterativamente a lo largo del análisis de la cadena de entrada:

ØPredecir. Si tenemos un estado {A --> ... ·B ..., i} es que necesitamos desarrollar B, y por tanto, debemos recorrer las reglas del tipo B --> ... para predecir lo que buscamos.
ØCompletar. Si hemos llegado a un estado completo {A --> ...·, i}, algún estado previo debe haber predicho A y necesitará que le avancemos el puntero · quedando {B --> ...A·..., i}.

ØAvanzar. Una vez realizadas todas las posibles predicciones de símbolos terminales, debemos comprobar que la cadena de entrada posee alguno de ellos y avanzar el puntero correspondiente del elemento {A --> ...a·..., i).

Earley en su forma básica

[Inicialización]
Para cada regla
S --> ...
Nuevos =
Añadir Elemento({S --> ·...,0}, Agenda[0])

Realizar
Nuevos = 0
Para cada elemento {
A --> ...·B...,0} de Agenda[0]
Para cada regla
B --> ...
Nuevos =
Añadir Elemento({B --> ·...,0}, Agenda[0])
Para cada elemento {
A --> ...·,0} de Agenda[0]
Para cada elemento {
B --> ...·A...,0} de Agenda[0]
Nuevos =
Añadir Elemento({B --> ...A·...,0}, Agenda[0])

mientras Nuevos = 1

[Iteración]
Para
Nuevos = 0
Para cada elemento {
A --> ...·a...,i} de Agenda[j-1]
Si
a pertenece a Entrada[j]
Nuevos =
Añadir Elemento({A --> ...a·...,i}, Agenda[j])

Si Nuevos = 0
Devolver ERROR

Realizar
Nuevos = 0
Para cada elemento {
A --> ...·B...,i} de Agenda[j]
Para cada regla
B --> ...
Nuevos =
Añadir Elemento({B --> ·...,j}, Agenda[j])
Para cada elemento {
A --> ...·,i} de Agenda[j]
Para cada elemento {
B --> ...·A...,k} de Agenda[i]
Nuevos =
Añadir Elemento({B --> ...A·...,k}, Agenda[j])

mientras Nuevos = 1

Si existe algún elemento {S --> ...·,0} en Agenda[N]
Devolver BIEN
si no existe Ç

Devolver ERROR

Ejemplo


P → S # La regla de principio

S → S + M

| M

M → M * T

| T

T → número


La cadena vacía requiere un tratamiento especial, será un símbolo no terminal que no necesita de la operación completar para el avance de su puntero. Cada vez que añadamos un elemento a Agenda[i] del tipo {A --> ...· ..., j} donde el puntero · queda a la izquierda de una cadena vacía, añadiremos también el elemento {A --> ... · ..., j}. Al reconstruir la estructura de la cadena de entrada, en el bucle que recorre los símbolos de la parte derecha de una regla B --> B1 ... Bm debemos distinguir no sólo los terminales y no terminales sino también la cadena vacía, y al llegar al símbolo nulo del elemento {A --> ... ..., j} reduciremos sólo k y proseguiremos.

Algoritmo de reconstrucción del árbol sintáctico del algoritmo de Earley en ausencia de ambigüedades


Reconstruye({A --> B1 B2 ... Bm ·,i}, j)
Añade a Lista De
Reglas el elemento (A --> B1 B2 B3 ... Bm)

k = m
l = j

Realizar
Si B
k es terminal
k = k-1
l = l-1
si no
Si existe en Agenda[l] un {B
k --> ·, r} tal que en
Agenda[r] esté {A --> B
1 B2 ...·Bk, i}
Reconstruye({B --> ·, r}, l)
k = k-1
l = r

mientras k>0