SlideShare ist ein Scribd-Unternehmen logo
1 von 172
Downloaden Sie, um offline zu lesen
UNIVERSIDAD DEL CONO SUR DE LAS AMÉRICAS
TESIS DE GRADO DE LA CARRERA INGENIERÍA EN INFORMÁTICA
TEMA: Creación de un Traductor Pseudocódigo a Lenguaje C (P2C)
REALIZADO POR
Francisco Javier Gray Benítez
Tutor: Prof. Lic. Julio César Suárez Ortega
Año 2008
Asunción – Paraguay
DEDICATORIA
A mis padres y hermanos…
AGRADECIMIENTO
Al Prof. Lic. Julio Suárez, quien más que un tutor, es un gran maestro y ha estado
brindándome su apoyo desde incluso mucho antes de emprender está empresa.
.
RESUMEN
El propósito del presente trabajo de investigación y desarrollo, fue
la de crear un traductor de Lenguaje Pseudocódigo (español estructurado) a
Lenguaje C. Para la construcción del traductor inicialmente se escribió una
gramática libre de contexto, la cual sirvió de base para la construcción de la
misma, a través de la utilización de metacompiladores fue generado un código
equivalente en Lenguaje C, el cual posteriormente se compiló a fin de producir el
traductor final. Al traductor se lo dotó de la capacidad de traducir (del Lenguaje
Pseudocódigo al Lenguaje C) tipos de datos primitivos, estructuras condicionales,
estructuras de repetición, entradas y salidas de datos, funciones propias del
lenguaje y funciones definidas por el usuario, incluyendo las combinaciones
posibles que de ellas puedan surgir, aportando de esta manera una herramienta que
facilite el proceso de aprendizaje de las técnicas de algorítmicas a todas aquellas
personas que se inician en el mundo de la programación.
INDICE DE TABLAS
Tabla 1 - Tabla de análisis sintáctico LL(1) ...........................................................47
Tabla 2 - Tabla de Análisis LR...............................................................................55
Tabla 3 - Reconocimiento de id*id+id....................................................................56
Tabla 4 - Tabla de análisis LR(1) para S$S'→ ......................................................68
Tabla 5 - Tabla de análisis LALR(1) para S$S'→ .................................................71
Tabla 6 – Operadores aritméticos admitidos por el traductor P2C......................... 148
Tabla 7 - Operadores relacionales admitidos por el traductor P2C........................ 148
Tabla 8 - Operadores lógicos admitidos por el traductor P2C ............................... 148
Tabla 9 – Operador de asignación admitido por el traductor P2C ......................... 149
INDICE DE FIGURAS
Figura 1 - Evolución de los lenguajes de programación..........................................21
Figura 2 - Esquema Básico de un Traductor...........................................................22
Figura 3 - Entrada y salida de un traductor.............................................................27
Figura 4 - Funcionamiento del enlazador ...............................................................27
Figura 5 - Etapas de un traductor............................................................................28
Figura 6 - Funcionamiento de un analizador léxico ................................................31
Figura 7 - Procesos de las dos fases primeras del análisis.......................................32
Figura 8 - Árbol de análisis sintáctico para E=m*c^2.............................................36
Figura 9 - PRIMEROS y SIGUIENTE...................................................................47
Figura 10 - Esquema Sinóptico de un analizador LR ..............................................54
Figura 11 - Conjunto LR(0) canónico para EE' ⋅→ ...............................................59
Figura 12 - Diagrama AFD para los prefijos viables...............................................60
Figura 13 - Grafo de transiciones para los goto de S$S'→ ....................................68
Figura 14 - AFD Simplificado para LALR(1).........................................................70
Figura 15 - Interacción de etapas con la Tabla de Símbolos....................................75
Figura 16 - Árbol y GDA para char x char → pointer(integer) ...............................81
Figura 17 - Representación de las expresiones de tipos ..........................................85
Figura 18 - Posición del generador de códigos en un traductor...............................88
Figura 19 - Árbol de análisis sintáctico para 4*5+6................................................90
Figura 20 - Árbol de análisis sintáctico con atributo heredados ..............................91
Figura 21 - Grafo de dependencias.........................................................................93
Figura 22 - Grafo de dependencias circular ............................................................95
Figura 23 - Momento en que se procede a traducir un programa P2C................... 162
Figura 24 - Salida de la traducción en lenguaje C por pantalla.............................. 162
Figura 25 - Pantalla de Tc-Lite, previa a la compilación....................................... 164
Figura 26 – Compilación del programa C generado.............................................. 165
Figura 27 – Compilación del programa C generado.............................................. 165
Figura 28 - Ejecución del programa compilado en C(parte 1)............................... 166
Figura 29 - Ejecución del programa compilado en C(parte 2)............................... 166
INDICE
CAPITULO I - INTRODUCCION ...................................................................... 8
CAPITULO II - MARCO TEORICO................................................................. 21
II.1 TRADUCTORES........................................................................................21
II.1.1. Origen .....................................................................................................21
II.1.2. ¿Qué es un traductor? ..............................................................................22
II.1.3. Tipología de los traductores según su utilización .....................................22
II.1.4. Fases de una traducción compilada..........................................................26
II.1.5. Etapas de la construcción de un traductor ................................................28
II.2 - ETAPA DE ANÁLISIS.............................................................................30
II.2.1. Análisis léxico (Scanner).........................................................................30
II.2.2. Análisis Sintáctico (Parser)......................................................................35
II.1.3. Análisis Semántico..................................................................................74
II.3 - ETAPA DE SINTESIS..............................................................................87
II.3.1 Generación de código ...............................................................................87
II.4 – DEFINICIÓN DIRIGIDA POR SINTAXIS..............................................88
II.4.1. Definición dirigida por sintaxis por medio de gramáticas con atributos....88
II.4.2. Grafos de dependencias ...........................................................................92
II.4.3. Esquemas de traducción...........................................................................96
CAPITULO III - MARCO METODOLOGICO................................................. 97
III.1. Descripción de la profundidad y Diseño de la tesis....................................97
III.2. Cómo se realizó la tesis.............................................................................98
III.3. Descripción de los instrumentos y procedimientos utilizados para la
recolección y tratamiento de la información.......................................................99
III.4. Descripción de la muestra utilizada. ..........................................................99
III.5. Adecuación de los métodos a los objetivos de la tesis................................99
III.6. Tratamiento de los datos.......................................................................... 100
CAPITULO IV - RESULTADOS.................................................................... 101
IV.1. Código fuente de los Analizadores Sintáctico y Semántico del traductor
P2C.................................................................................................................. 101
IV.2. Tabla de símbolos en Lenguaje C del traductor P2C................................ 135
IV.3. Código fuente del Analizador Léxico del traductor P2C.......................... 142
IV.4. Manual de programador del traductor P2C.............................................. 144
CAPITULO V – CONCLUSIONES ................................................................ 167
CAPITULO VI –RECOMENDACIONES....................................................... 170
BIBLIOGRAFIA............................................................................................. 171
8
CAPITULO I - INTRODUCCION
Desde los comienzos de la informática, se ha buscado la manera
mas práctica de interactuar con el ordenador, y hacer que éstos realicen las
acciones tal y como se desea, con resultados en tiempo y forma.
Con el pasar de las generaciones han ido surgiendo técnicas cada
vez más sofisticadas, las cuales han dado como resultado toda una nueva rama de
la informática, la de los traductores. Así en el año 1952 Grace Murray Hopper
(1906-1992), dio origen al primer traductor, cuyo objetivo era el de traducir las
instrucciones escritas en inglés al lenguaje máquina de un ordenador.
Tal y como el trabajo de Murray Hopper, el propósito de este
trabajo de investigación y desarrollo, fue crear un traductor de lenguajes que
permita traducir programas escritos en LENGUAJE PSEUDOCODIGO a
LENGUAJE C.
Pensando en quienes se inician en la codificación de programas
para computadoras, han surgido las siguientes cuestiones, ¿existen herramientas
sencillas, que permitan realizar comprobaciones de la eficacia de los algoritmos?,
¿responden a estas necesidades los lenguajes actuales del mundo computacional?
Exceptuando al Lenguaje Basic en sus versiones iniciales,
actualmente no existen otras herramientas fáciles de acceder, como el desarrollado
por medio de la presente tesis. Es pues esto, una respuesta a las necesidades de
contar con un programa sencillo de utilizar, con un lenguaje de programación
próximo al lenguaje natural, el cual simplifica la complejidad del estudio de la
algorítmica.
9
El traductor desarrollado facilita la traducción de un lenguaje de
uso académico (sin aplicación práctica a otros ámbitos) a un lenguaje de porte
científico-comercial de amplia aplicación y utilización mundial, cual es el
lenguaje C.
El desarrollo fue realizado entre enero de 2006 y febrero de 2007,
tiempo en que se ha trabajado en el diseño, programación, pruebas y puesta a
punto.
Para la realización de la tesis fueron fijados los siguientes
objetivos:
Objetivo General:
Desarrollar un traductor de códigos teniendo como origen el
Lenguaje Pseudocódigo y como destino el Lenguaje de C.
Objetivos Específicos:
1. Diseñar las construcciones sintácticas de un lenguaje
Pseudo-codificado, en español, de fácil utilización para el
usuario.
2. Diseñar y desarrollar las analizadores léxicos y sintácticos
para la verificación de los programas fuentes.
3. Diseñar y desarrollar un sistema de tipos de datos, para la
implementación de un comprobador de tipos, que permita
la verificación del programa fuente, en concordancia con la
tipificación básica del lenguaje objeto.
4. Diseñar y desarrollar la comprobación estática de unicidad
y flujo de control.
5. Diseñar y desarrollar el generador del código objeto.
El presente material inicia con el primer capitulo desarrollando una
introducción al tema, el segundo capitulo expone paso a paso el marco teórico, el
tercer capitulo aborda la metodología seguida para la realización del presente
10
trabajo de tesis, en el capitulo cuatro se exponen los resultados obtenidos, en el
quinto capitulo las conclusiones a las que se ha arribado por medio del presente
trabajo de tesis, y finalmente, en el capitulo sexto se establecen unas series de
recomendaciones para aquellas personas que deseen proseguir el tema abordado
por la presente tesis.
11
GLOSARIO DE TÉRMINOS:
A
ADA: Lenguaje de programación estructurado y fuertemente tipado de forma
estática que fue diseñado por Jean Ichbiah de CII Honeywell Bull por encargo
del Departamento de Defensa de los Estados Unidos.
Ad-hoc: Es una hipótesis creada expresamente para explicar un hecho que
contradice una teoría.
Alfabeto: También conocida como vocabulario; es un conjunto finito, no-vacío de
símbolos (objetos atómicos o indivisibles), se denota con la letra griega Σ
(Sigma).
Ambigüedad: Es cuando una gramática genera para una misma cadena de
entrada dos árboles de análisis distintos.
Ámbito: Contexto en cual una declaración tiene validez. P.e., un programa, un
trozo de programa.
Árbol de derivación: Es una representación gráfica de como se deriva una forma
sentencial a partir del símbolo no-terminal inicial.
Árbol universal: Es aquel que tiene como raíz al axioma inicial y representa
todas las derivaciones posibles a partir de cualquier forma sentencial.
Asociatividad: Estable el orden de evaluación de las operaciones (asociatividad
por izquierda, asociatividad por derecha).
Atributo: Es cada una de las características o propiedades de una entidad o
interrelación.
Atributo Sintetizado: Es llamado atributo sintetizado a un atributo cuyo valor es
determinado por los valores de sus atributos hijos, en un proceso de recorrido del
árbol de derivación de abajo para arriba (Bottom-up).
Atributo Heredado: Es aquel atributo cuyo valor es calculado a partir de los
valores de sus hermanos y su padre.
Autómata finito: También conocida como maquina de estado finito; es un modelo
matemático de un sistema que recibe una cadena constituida por símbolos de un
alfabeto y determina si esa cadena pertenece al lenguaje que el autómata
reconoce.
Autómata finito de pilas (AFP): Son maquinas abstractas construidas para
reconocer los lenguajes regulares.
12
Autómata finito determinista (AFD): Es aquel autómata finito cuyo estado de
llegada está unívocamente determinado por el estado inicial y el carácter leído
por el autómata.
Autómata finito no determinista (AFN): Es aquel que presenta cero, una o más
transiciones por el mismo carácter del alfabeto
Axioma inicial: Es el no-terminal inicial de la cadena de entrada.
B
Backpatching: También conocida como relleno de retroceso; es una técnica de
generación de código, utiliza banderas para marcar las posiciones para valores
desconocidos, para posteriormente completarlas (paching).
Base de datos: También conocida como banco de datos; es un conjunto de datos
que pertenecen al mismo contexto, almacenados sistemáticamente para su
posterior uso.
BNF: Acrónimo de Backus Naur Form (en español, Forma Normal de Backus);
es una metasintaxis usada para expresar gramáticas independientes del contexto.
Es una manera formal de describir lenguajes formales.
Buffer: Es una ubicación de la memoria en una computadora, ó en un
instrumento digital, reservada para el almacenamiento temporal de información
digital mientras que está esperando ser procesada.
C
C: Es un lenguaje de programación creado en 1969 por Ken Thompson y Dennis
M. Ritchie en los Laboratorios Bell como evolución del anterior lenguaje B, a su
vez basado en BCPL. Al igual que B, es un lenguaje orientado a la
implementación de Sistemas Operativos, concretamente Unix. C es apreciado por
la eficiencia del código que produce y es el lenguaje de programación más
popular para crear software de sistemas, aunque también se utiliza para crear
aplicaciones.
Cadena de entrada: Es el conjunto de caracteres proporcionados como entrada
de un traductor para su análisis y traducción posterior si fuera el caso.
Cierre: Conjunto de todas las posibles cadenas finitas sobre un alfabeto, se
denota como Σ*.
Clase: También conocida como clausura ó Clausura de Kleene; son
declaraciones o abstracciones de objetos, lo que significa, que una clase es la
13
definición de un objeto. Cuando se programa un objeto y se definen sus
características y funcionalidades, realmente se programa una clase.
Código de máquina: Es una codificación de programas en sistema binario que es
el único que puede ser directamente ejecutado por una computadora.
Comando: Del inglés command; es una instrucción o mandato que el usuario
proporciona al sistema.
Compilador: Es un programa informático que traduce un programa escrito en un
lenguaje de programación a otro lenguaje de programación, generando un
programa equivalente que la máquina será capaz de interpretar.
Condición de seguridad: Es una herramienta de la se dota a los lenguajes a fin
de realizar una evaluación previa a la ejecución de una aplicación, la cual
terminará ejecutándose, si y solo si satisface la condición de seguridad.
Constructor de tipos: Método o función propia de un lenguaje computacional,
tiene como tarea principal la definición.
Contexto: Son las circunstancias bajo las cuales un dispositivo ó programa está
siendo utilizado.
Corrutina: Es aquella porción de código que funciona subordinada a otra.
Costos: Es el tiempo en el cual se incurre durante un proceso dado. P.e.: Costos
de CPU. Costos de E/S.
CPU: Acrónimo de Central Processing Unit; es la unidad central de
procesamiento, ó simplemente, el procesador. En una computadora digital, es el
componente que interpreta las instrucciones y procesa los datos contenidos en los
programas.
Criterios de poda: Se define como todo aquél criterio que permite saber, dada
una solución parcial, que no va a ser posible obtener a partir de ellas soluciones
válidas.
D
Derivación: Secuencia de sustituciones de no-terminales que, partiendo de un
símbolo inicial (denotado con S), produce un resultado (denotado con ω).
Dirección de memoria: Es una colección de casilla ó celdas que almacena datos
e instrucciones, identificadas unívocamente por una serie de números.
14
DOS: Acrónimo de Disk Operating System (Sistema Operativo de Disco), creado
originalmente para las computadoras IBM PC, las cuales utilizaban procesadores
8086/8088 de 16 bits.
E
E/S: Acrónimo de entrada/salida, del inglés input/output (I/O); es el conjunto de
dispositivos físicos utilizados por el sistema para comunicarse unas con otras,
como para comunicar al sistema con las personas.
Encapsulamiento: Se denomina encapsulamiento al ocultamiento del estado, es
decir, de los datos miembros de un objeto, de manera que sólo se puede cambiar
mediante las operaciones definidas para ese objeto.
Enlazador: Del inglés, linker; programa que a partir de ficheros objetos
generados en los primeros procesos de traducción, quita todos los recursos no
precisados y enlaza el código objeto con las bibliotecas, para finalmente producir
un código ejecutable ó biblioteca.
EOF: Acrónimo de end-of-file; es un indicador o marca de que no hay mas
información que recuperar de una fuente de datos.
Épsilon (E ε): Es la quinta letra del alfabeto griego. En el sistema de numeración
griega tiene un valor de 5. En matemáticas, ε suele designar a pequeñas
cantidades, o cantidades que tienden hacia cero, en particular en el estudio de los
límites o de la continuidad.
Estructura de datos: Conjunto de datos en que sus componentes están
relacionados entre sí de una forma particular y sobre los que se pueden realizar
ciertas operaciones según sea el tipo de relación que hay entre ellos.
F
Fichero: También conocido como archivo; es un conjunto de información que se
almacena en algún medio de escritura que permita ser leído o accedido por una
computadora.
Fichero Ejecutable: También conocido como archivo ejecutable; es aquel cuyo
contenido se interpreta por el ordenador como un programa.
Fichero Fuente: También conocido como archivo fuente; es un conjunto de
códigos escritos en texto simple, capaz de ser leído por cualquier editor de texto,
en el, están dispuestos todas las tareas que deberá realizar la computadora.
15
Fichero Objeto: También conocido como archivo objeto; contiene al conjunto de
caracteres en código de maquina o bytecode, resultado de la compilación del
fichero fuente.
G
Gramática: Ciencia que estudia los elementos de una lengua y sus
combinaciones.
H
Herencia: Es uno de los mecanismos de la programación orientada a objetos,
por medio del cual una clase se deriva de otra, llamada entonces superclase, de
manera que extiende su funcionalidad.
I
Instrucciones: Conjunto de reglas o advertencias necesarias para ejecutar una
tarea, un trabajo, un juego, etc.
Inteligencia Artificial: Ciencia que intenta la creación de programas para
máquinas que imiten el comportamiento y la comprensión humana.
Interpretes: Es un programa capaz de analizar y ejecutar otros programas
escritos en un lenguaje de alto nivel, realizando la traducción a medida que sea
necesario, típicamente, instrucción por instrucción, y normalmente no guardan el
resultado de dicha traducción, por lo que la ejecución de programas en lenguajes
interpretados suelen ser bastante lentos.
Invocación: Se denomina invocación al acto de solicitar la ejecución de un
programa, rutina o subrutina.
J
Java: 1- Del Indonesio Jawa; es la isla más poblada del archipiélago indonesio,
donde se sitúa la capital, Yakarta, muy conocida también por su café. 2-
Tecnología desarrollada por Sun Microsystems para aplicaciones de software
independientes de la plataforma (lleva el nombre de Java en honor al café
producido en dicha Isla, de ahí que el símbolo de dicho programa sea una taza de
café).
16
L
Lenguaje: Es cualquier conjunto de cadenas finitas sobre un alfabeto.
Lenguaje de alto nivel: Es aquel lenguaje de programación en el que las
instrucciones enviadas al ordenador son similares al lenguaje humano.
Lenguaje de maquina: Es un lenguaje compuesto por un conjunto de
instrucciones que determinan acciones a ser tomadas por la máquina. Un
programa de computadora consiste en una cadena de estas instrucciones de
lenguaje de máquina (más los datos).
Lenguaje ensamblador: Lenguaje de programación que está a un paso del
lenguaje de máquina, por ende los programadores deben estar familiarizados con
la arquitectura del computador para el cual están programando, siendo los
programas en lenguaje ensamblador no documentados, y en consecuencia
difíciles de mantener, como el lenguaje ensamblador es dependiente del
hardware; hay un lenguaje ensamblador diferente para cada serie de CPU.
Lenguaje Natural: El lenguaje natural es el lenguaje hablado y/o escrito por los
seres humanos para propósitos generales de comunicación.
Lenguaje Orientado a Objetos: Es aquel lenguaje de programación capaz de
soportar Programación Orientada a Objetos.
Librería: También conocida como biblioteca; conjunto de procedimientos y
funciones (subprogramas) agrupadas en un fichero con el fin de que puedan
aprovecharlas otros programas. Existen dos tipos de librerías; 1- Estáticas (de
enlace estático) y, 2- Compartidas (de enlace dinámico).
M
Macros: Grupo de comandos de una aplicación, organizados según un
determinado juego de instrucciones y cuya ejecución puede ser pedida de una
sola vez para realizar una función deseada.
Memoria: Del inglés, Memory; es el espacio de trabajo del computador
(físicamente suelen ser una colección de chips RAM), determina el tamaño y el
número de programas que pueden ejecutarse al mismo tiempo, así como también
la cantidad de datos que puede procesarse en cada instante.
Módulo: Es un componente de un programa computacional, el cual de las varias
tareas que un programa deberá realizar para cumplir su función ú objetivo, el
módulo realizará una o varías de estas tareas.
17
N
No Terminales: Es aquel símbolo que en una derivación siempre es reemplazada
demás, pues no termina en una derivación, algunos autores tal y como es el caso
de Kenneth C. Louden, lo llaman como estructuras.
Nodos: Es una estructura ó registro que dispone de varios campos, y al menos
uno de ellos deberá ser un puntero a otro nodo, es utilizada para la realización de
lista enlazada, árbol ó grafo.
O
Operadores: Son expresiones que permiten manipular los datos que se les pasa,
cada uno de los datos que se pasa a un operador se llama operando, y según el
número de operandos de que disponga un operador estaremos hablando de un
operador unario (un operando), binario (dos operandos), ternario (tres
operandos), etc.
P
Palabras reservadas: Son palabras propias de un lenguaje de programación, las
mismas no pueden ser utilizadas como nombre de identificadores.
Parámetros: También conocida como argumento, es una información que
determina el funcionamiento de un programa informático. Los parámetros pueden
tener valores de todo tipo. Por ejemplo: números, textos, expresiones o incluso el
nombre de un archivo.
Pasadas: Es el acto de leer el código fuente por parte de un traductor.
PASCAL: Lenguaje de programación desarrollado por el Profesor Suizo Niklaus
Wirth a finales de los años 60, bajo el objetivo crear un lenguaje que facilitara el
aprendizaje de la programación a sus alumnos, sin embargo, con el tiempo este
se convirtió en una herramienta para desarrollo de aplicaciones de todo tipo.
PERL: Acrónimo de Practical Extracting and Reporting Lenguaje; es un
Lenguaje de programación de amplia difusión usado para web mediante la
programación de CGI. Muy usado para extraer información de archivos de texto
y para la generación de informes a partir de ficheros.
Pila: Es un tipo de estructura de datos en el que el último dato en llegar es el
primero en salir.
18
PL/I: Acrónimo de Programming Language 1 (Lenguaje de Programación 1); fue
propuesto por IBM hacia 1970 para responder a las necesidades de las
aplicaciones científicas y comerciales, disponible en las novedosas plataformas
de utilidad general IBM 360 y más adelante IBM 370.
Polimorfismos: Es la capacidad que tienen objetos de diferentes clases de
responder al mismo mensaje o evento.
Portabilidad: Se define como la dependencia del software respecto a la
plataforma del mismo, y es inversamente proporcional a la dependencia de
plataforma. A menor dependencia, mayor portabilidad.
Precedencia: Especifica la prioridad de cada operador respecto a otro.
Programa: Secuencia de instrucciones que realizan una tarea específica.
Programa fuente: Código del programa original, el cual es almacenada en un
fichero, conocido como fichero o archivo fuente.
Programa objeto: Un programa a nivel de lenguaje máquina que resulta de la
compilación de un programa fuente.
Programación: Es la implementación de un algoritmo en un determinado
lenguaje de programación, conformando un programa.
Programación Orientada a Objetos (POO): Es un paradigma de programación
que usa objetos y las interacciones entre estas para diseñar y desarrollar
aplicaciones, se basa en varias técnicas, como la herencia, modularidad,
polimorfismo y encapsulamiento. Popularizado recién a principio de la década de
los 1990, en la actualidad cuenta con varios lenguajes soportan la orientación a
objetos.
R
Recursividad: Propiedad de algunos lenguajes de programación de permitir que
un programa solicite su propia ejecución en el curso de su ejecución.
Rutina: Es un conjunto de código, escrito para un propósito particular, que al ser
llamado dentro de un programa el código principal de éste se detenga y se dirija
al código de la rutina invocada.
S
Shell: Es un programa informático que actúa como Interfaz entre el usuario y el
sistema operativo mediante una ventana que espera comandos textuales
19
ingresados por el usuario en el teclado, los interpreta y los entrega al sistema
operativo para su ejecución. Líneas de comandos, Intérprete de mandatos,
Intérprete de línea de mandatos, Intérprete de comandos, Terminal, Consola,
Shell ó su acrónimo en inglés CLI por Command line interface, son formas
comunes de llamarlo.
Símbolos inaccesibles: No terminales a los que no se puede llegar por medio de
derivación alguna.
Símbolos muertos: No terminales incapaces de generar una cadena de
terminales, a través de derivaciones sucesivas.
Sintaxis: Son las reglas que rigen la formulación de las instrucciones en un
programa de computación, en base a una gramática dada.
Sistema Operativo: Es un programa o conjunto de programas destinados a
permitir una gestión eficaz de recursos, éste empieza a trabar cuando se enciende
el computador, es el encargado de gestionar el hardware de la maquina desde los
niveles más básicos, permitiendo interacción con el usuario.
Sobrecarga: Es la posibilidad de tener dos o más funciones (sobrecarga de
funciones) y/u operadores (sobrecarga de operadores) con el mismo nombre pero
brindando funcionalidades distintas, el compilador utilizará una ú otra
dependiendo de los parámetros enviados.
T
Terminal: Son símbolos del alfabeto en los cuales termina una derivación.
Tiempo de ejecución: Del inglés Runtime; es el intervalo de tiempo en el que un
programa de informático se ejecuta en un sistema operativo.
Tira nula: Secuencia de vacía de símbolos.
U
Unix: Registrado oficialmente como UNIX®, es un sistema operativo portable,
multitarea y multiusuario; desarrollado, en principio, en 1969 por un grupo de
empleados de los laboratorios Bell de AT&T, entre los que figuran Ken
Thompson y Dennis Ritchie. Según Dennis Ritchie, «después de treinta años de su
creación, UNIX sigue siendo un fenómeno»
20
V
Variables: Son estructuras de datos que, como su nombre indica, pueden cambiar
de contenido a lo largo de la ejecución de un programa. Una variable
corresponde a un área reservada en la memoria principal del computador.
W
Windows: Microsoft Windows, conocido simplemente como Windows, es un
sistema operativo con interfaz gráfica para computadoras personales cuyo
propietario es la empresa Microsoft.
21
CAPITULO II - MARCO TEORICO
II.1 TRADUCTORES
II.1.1. Origen
El estudio de los traductores se remonta a los orígenes de la
informática misma, ya en 1952 Grace Murray Hopper (1906-1992), en vista a la
necesidad que tenía creo un traductor Inglés-Lenguaje Maquina, posterior a esto y
hasta nuestros días la ciencia-arte de crear lenguajes ha evolucionado
notablemente, empezando por los ensambladores que fueran creados por cada uno
de los fabricantes, con sus propias especificaciones para cada procesador, hasta
los lenguajes de alto nivel y llegando a los lenguajes orientados a objetos los
cuales trabajan a base de acciones o comportamiento acorde a los distintos
impulsos externos que reciben.
Figura 1 - Evolución de los Lenguajes de Programación.
Fuente: CUEVA 1998:10
Al principio, el desarrollo de un traductor era considerado una
tarea difícil y de alto consumo de tiempo, por lo que eran escasos los centros e
institutos de investigación que dedicaban esfuerzos en su construcción, (GALVEZ
22
et al., 2005), mencionan que, para la implementación del primer compilador
FORTRAN (Formula Translator) se necesito lo equivalente a 18 años de trabajo
individual.
Hoy día gracias a las técnicas avanzadas de desarrollo de
traductores y lenguajes, el desarrollo de un traductor puede ser realizado por un
pequeño grupo de investigación ó como trabajo de fin de carrera, tal cual el
presente estudio, en la década de 1950, tiempo en que se desarrollo el primer
compilador FORTRAN esto era toda una utopía.
II.1.2. ¿Qué es un traductor?
Un traductor se define como un programa que traduce un programa
escrito en un lenguaje (lenguaje fuente) a otro programa o lenguaje (lenguaje
destino), y como parte de este proceso el traductor debe emitir mensajes de errores
al usuario si los hubiere.
II.1.3. Tipología de los traductores según su utilización
Traductores de idioma: Son programas que se encargan de realizar
traducciones de un idioma a otro, este tipo de traductores deben sortear un sin fin
de dificultades, como ser:
1. Significado de las palabras según el contexto.
Programa
Destino
Mensajes
de Error
Figura 2 - Esquema Básico de un Traductor
Traductor
Programa
Fuente
Fuente: Suárez 2006:1
23
2. Necesidad de un alto nivel de Inteligencia Artificial: Debido
al hecho de que el nivel de inteligencia artificial sigue siendo pobre para lo que se
precisa a nivel de traductores de idiomas, esto se presenta como una dificultad
complicada de superar en la traducción de frases hechas, al respecto cabe
mencionar una anécdota expuesta por (GALVEZ et al., 2005), donde mencionan
que durante la guerra fría, en un intento por realizar traducciones en forma
automática del ruso al inglés y viceversa, se puso a prueba un prototipo
introduciendo un texto en inglés: “El espíritu es fuerte, pero la carne es débil”
cuya traducción al ruso se paso de nuevo al inglés para ver si coincidía con el
original, y para sorpresa de los desarrolladores el resultado fue: “El vino está
bueno, pero la carne está podrida”, esto es debido a que en inglés spirit, posee más
de un significado, según se aplique puede significar espíritu ó alcohol.
• Intérpretes: Los intérpretes tienen una estructura similar al
de los traductores compiladores, en funcionamiento, varían sustancialmente,
debido a que los intérpretes, reciben como entrada un programa fuente en lenguaje
formal y como salida realiza la ejecución del programa, esto es debido a que el
programa fuente es reconocida y ejecutada al mismo tiempo, algunos ejemplos de
lenguajes intérpretes son; SNOBOL (StriNg Oriented SimbOlyc Language), LISP
(LISt Processing), algunas versiones del BASIC (Beginner´s All-propuse
Symbolic Instruction Code).
• Preprocesadores: Los preprocesadotes se utilizan con la
finalidad de modificar el programa fuente, al respecto (AHO et al., 1990), habla
sobre las distintas funciones que estas pueden realizar, las cuales se describen a
continuación:
i. Procesamiento de macros: Permite al
programador-usuario definir macros, las cuales son abreviaturas de
construcciones muchos más grandes.
24
ii. Inclusión de ficheros: Inserta ficheros completos
dentro del programa en cual aparece en la cabecera, en la misma
línea en la que aparece se insertará su contenido antes de que el
programa fuente sea compilado, el lenguaje C es un lenguaje que
típicamente utiliza este tipo de preprocesamiento, por medio de la
cláusula #include <nombre_de_archivo>.
iii. Procesadores “racionales”: Utilizados con la
finalidad de enriquecer a los lenguajes antiguos con nuevas
funcionalidades, como los controles de flujos, nuevos tipos de
datos, interfaces con dispositivos de hardware, entre otros.
iv. Extensiones a Lenguajes: Tratan de crear
posibilidades al lenguaje al cual son insertados, por medio de
macros, en este aspecto puede considerarse el lenguaje Equel
STONEBRAKER et al. (1976, citado de AHO, 1990), el cual es un
lenguaje de consultas a base de datos integrado al lenguaje C. Las
proposiciones que inician con ##, son tomadas por el
preprocesador, sin que ellas pasen por el lenguaje C y las traduce
directamente a llamadas a la base de datos.
• Intérpretes de comandos: Un intérprete de comandos
traduce sentencias1
simple s a invocaciones de programas, ejemplos de estos
son las shell de los sistemas operativos de la familia UNIX, o la línea de comando
MS-DOS de los sistemas MS-Windows.
• Ensambladores: En los albores de la informática, tiempos en
los que todos los programas eran escritos en lenguaje de maquina, aparecieron
como el primer gran salto hacia los lenguajes de altos niveles como los que hoy
poseemos.
1
Sentencia(s): Secuencia de expresiones (terminales) que especifica una o varias operaciones.
25
Un ensamblador es un traductor sencillo, el lenguaje fuente tiene
una estructura, en la que una sentencia se traduce de manera única a una
instrucción en código de maquina, por lo que la correspondencia ensamblador-
código de maquina es una a una.
• Traductores fuente-fuente: Permite obtener mayor
portabilidad del código fuente escrito, debido a que traduce códigos fuentes
escritos para un lenguaje a otro. El presente trabajo de investigación y desarrollo
tiene como fin desarrollar un traductor fuente-fuente.
En estos tipos de traductores, es habitual que los programas fuentes
resultantes requieran de retoques manuales debidos a diversos motivos (Gálvez
Rojas et al., 2005):
i. En situaciones en las que el lenguaje destino
carece de importantes características, de las que dispone el
lenguaje origen, por ejemplo una conversión Java a C, podría
precisar retoques, debido a que el lenguaje C no dispone del
recolector de basuras (Garbage Collector).
ii. En situaciones en las que la traducción no es
inteligente y los programas destinos son ineficientes.
Un ejemplo de traductor fuente-fuente podría ser como ya se ha
mencionado anteriormente el traductor realizado por Murray Hopper en 1952, así
como el POLARIS el cual recibe como entrada programas escritos en
FORTRAN77 y los traduce en programas eficientes a fin de ser utilizados en
computación paralela, cuya salida nuevamente es FORTRAN77, aunque con
directivas explicitas que provee paralelismo.
• traductor cruzado: Es aquel que genera un código objeto
ejecutable para una maquina distinta a la que se escribe y/o realiza la compilación.
26
• Traductores de circuitos de silicios: Posee un lenguaje
fuente similar o idéntico que un lenguaje de programación convencional,
marcando diferencia, en el hecho de que las variables no representan ubicaciones
en memoria, sino señales lógicas (0 y 1) o grupos de señales de conmutación,
produciendo como salida el diseño del circuito impreso mediante técnicas
fotográficas.
• Conversores de formatos: Son programas que aplican las
técnicas de traductores de lenguajes para convertir ficheros escritos en un formato
dado a otro.
• Reconocimiento del habla: Los sistemas de reconocimientos
del habla realizan un análisis detallado del sonido, por medio de las técnicas de
traductores.
II.1.4. Fases de una traducción compilada
Todo traductor tipo compilador se dota de tres fases básicas, las
cuales son traducción, enlace (enlazador) y carga ó ejecución.
Traducción, Generalmente un traductor tipo compilador no
produce un programa o fichero ejecutable, en su lugar emite como salida
programa ó fichero objeto, que contiene informaciones relativas al código, así
como una tabla de símbolos con las variables y estructuras de tipos utilizadas en el
programa fuente.
27
Enlace, El enlazador se encarga de enlazar todos los componentes
generados en la etapa de traducción (referencias cruzadas), generar la estructura
de bloque de memoria para almacenar las variables en tiempo de ejecución y el
programa ó fichero ejecutable, incorporando rutinas adicionales precedentes de las
librerías, como las instrucciones de entrada/salida, funciones matemáticas y de
sistemas, entre otros.
Figura 4 - Funcionamiento del enlazador
Fuente: Elaboración propia, basado en Vélez 2003:4.
Cargador, Esta fase del compilador tiene como tarea, la de cargar el
fichero ejecutable (.exe), en otras palabras, lo que hace el cargador es, tomar el
Traductor
Entrada:
Fichero.prg
Salida:
Fichero.obj
Figura 3 - Entrada y salida de un traductor
Fuente: Elaboración propia, basado en Suárez 2006:3.
28
código de maquina relocalizable producida por el enlazador, modificar las
direcciones de memorias relocalizables y ubicar las instrucciones y los datos
modificados en las posiciones apropiadas de la memoria. Dependiendo del
microprocesador, la estructura de memoria difiere, aunque lo más usual es que el
fichero .exe, esté divido en segmentos de códigos, datos, pilas, etc., al comienzo
del módulo de carga se le asigna la dirección de memoria relativa 0, siendo las
demás referencias dentro del modulo apuntadas al comienzo del mismo. Teniendo
todas las referencias a memoria en código relocalizable, la tarea de carga se
convierte en una tarea sencilla, pues sabiendo la dirección de memoria relativa en
la que será cargada el modulo (dirección x), bastará sumar dicha dirección (x) a
medida que se vaya produciendo la carga del módulo en memoria, a fin de obtener
las posiciones de memorias relativas de cada componente.
II.1.5. Etapas de la construcción de un traductor
El traductor puede ser dividido inicialmente en dos grandes etapas,
la etapa inicial ó de análisis y la etapa final ó de síntesis, la primera se refiere a
todo aquello que tenga que ver con el programa fuente e independiente del
programa objeto, en ella se incluyen los análisis léxicos, sintácticos y semánticos,
la construcción de tablas de símbolos, la generación de código intermedio y en
cierta medida la optimización de código, siendo la etapa final todo lo concerniente
a las partes del traductor que tienen directa relación con la maquina para la que se
construye, siendo completamente independientes del lenguaje fuente, en está etapa
se incluyen todo lo referente a la generación y optimización de código, manejo de
errores y operaciones con las tablas de símbolos.
Figura 5 - Etapas de un traductor
Fuente: Gálvez et. al 2005:12
AnálisisProg. Fuente
Errores en el
programa fuente
Síntesis Prog. Destino
Errores en la
generación de código
29
El traductor como parte importante de su función, debe leer el
programa fuente, existen situaciones en las cuales, el leer una sola vez el
programa fuente, no resulta ser suficiente, como en el caso de la recursividad,
suponiendo que se tiene una función X, que hace referencia a una función Y, y
viceversa, en este caso hipotético, al leer la función X no se sabe si la función Y
está declarada ó si es que se ha hecho de forma correcta, sin embargo al leer la
función Y ya se sabrá que de antemano que X está y que ya ha cumplido con
todos los parámetros necesarios para ser utilizado, siendo la única alternativa de
solución para la invocación de la función Y desde X, el volver a pasar una vez
más(releer el código) a fin de rellenar los datos faltantes y corregir los errores, a
este proceso se lo conoce con el nombre de pasadas.
Lo deseable es tener la menor cantidad de pasadas, a fin de reducir
los costos (tiempos) de e/s, para ello se han creado bastas alternativas, como ser el
lenguaje PL/I, solo permite utilizar variables previamente declaradas y no se
puede realizar representaciones de código objeto para las construcciones de las
que se desconozcan los tipos de variables involucradas, del mismo modo otros
lenguajes permiten la utilización de construcciones GOTO a fin de saltar adelante
en el código para verificar la existencia de las referencias o variables utilizadas,
otros lenguajes utilizan la técnica backpatching (relleno de retroceso), dicha
técnica consiste que al encontrar un salto hacia delante (GOTO destino), generar
la estructura de una instrucción, con el código de operación para el GOTO y dejar
espacios en blancos para la dirección, guardando todas las instrucciones con
espacios en blanco para la dirección destino en una lista asociada con la entrada
de destino en la tabla de símbolos, los cuales son rellenados una vez que se
encuentra la instrucción (destino: MOV algo, R1).
30
II.2 - ETAPA DE ANÁLISIS
La etapa de análisis está formada por los siguientes tres componentes:
1. Análisis léxico ó scanner, se encarga de recorrer el programa
fuente, identificar y agrupar los caracteres en tokens.
2. Análisis sintáctico ó parser, agrupa los componentes léxicos de
forma jerárquica, construyendo frases gramaticales los cuales
son utilizados por el compilador en la fase de síntesis.
3. Análisis semántico, verifica el programa fuente a fin de
asegurarse que no haya errores de significado y realiza la
comprobación de tipos.
II.2.1. Análisis léxico (Scanner)
El análisis léxico es la primera fase de todo traductor, su labor
principal consiste en leer una cadena de entrada (lexema) del programa fuente a
analizar de izquierda a derecha y agruparlos en componentes léxicos (tokens), los
cuales poseen un significado propio, para realizar dicho proceso debe existir una
regla o un conjunto de reglas previamente predefinidos (patrón). Cada lexema o
cadena del programa fuente es comparada con el conjunto de patrones
(expresiones regulares).
Ejemplo de una cadena de entrada:
E = m * c ^ 2
De esta forma los componentes léxicos se agruparían de la
siguiente manera:
1. El identificador ‘E’.
2. El símbolo de asignación ‘=’.
3. El identificador ‘m’.
4. El signo de multiplicación ‘*’.
5. El identificador ‘c’.
6. El signo exponencial ‘^’.
7. El número 2.
31
Figura 6 - Funcionamiento de un analizador léxico
E = m * c ^ 2
<ID> <ASIG> <ID> <*> <ID> <^> <NUM>.2
Fuente: Elaboración propia, basado en Gálvez et. al 2005:18
Otras tareas desarrolladas por el analizador léxico incluyen:
• Procesar directivas del traductor.
• Introducir información preliminar en la tabla de símbolos.
• Eliminar comentarios, espacios en blancos, tabulaciones,
retorno de carro y todo aquello que no tenga significado
según las reglas del lenguaje.
• Sustituir macros.
• Llevar la cuenta de números de líneas, a fin de que al
momento de un error pueda informar en que línea se ha
producido y facilitar la recuperación.
• Informar de errores léxicos encontrados, como por ejemplo
cuando se encuentra un ‘;’ y este no forma parte del
lenguaje.
• Cuando algunos componentes léxicos requieren algo más
que una identificación, el analizador léxico primero
identifica el componente léxico, y luego lo evalúa.
• Si el lenguaje a analizar es complejo en su sintaxis, el
analizador léxico realiza primeramente una fase de examen,
y posteriormente realiza la fase de análisis propiamente
dicha.
E
m
c
Punteros a
tabla de
Símbolos.
Asociación de
valor al terminal
NUM
Tabla de Símbolos
32
Los componentes léxicos más habituales suelen ser; las palabras
reservadas, identificadores, operadores, constantes, símbolos de puntuación y
caracteres especiales. El analizador léxico aparte del token, también debe devolver
información adicional sobre el mismo, como ser el valor, si se tratará de una
constante ó la identificación si fuera un operador. Toda información adicional
necesaria es enviada a través de atributos, asociados a los símbolos de la
gramática que define la sintaxis del lenguaje fuente.
El analizador léxico reconoce palabras conforme a una gramática
regular en el cual se define un conjunto de caracteres con los que el mismo puede
trabajar, conocidos con el nombre de alfabeto, así el analizador léxico se
encuentra sometido al analizador sintáctico, actuando como una subrutina o
corrutina del mismo.
Figura 7 - Procesos de las dos fases primeras del análisis.
Donde:
N: Conjunto de No Terminales.
T: Conjunto de Terminales.
P: Reglas de producción de la gramática.
S: Axioma o símbolo inicial.
Fuente: Gálvez et. al 2005:24
Analizador
Léxico (scanner)
Secuencia de
caracteres. Analizador
sintáctico (parser)
Secuencia de
terminales.
Árbol
sintáctico.
El control viene dado
por una gramática
(N, T, P, S)
33
La necesidad de construir un analizador Léxico:
1. Simplicidad del diseño: Facilita el diseño y el mantenimiento
primeramente del analizador léxico y a la larga del traductor. El
límite entre ambos analizadores es una línea muy delgada, se
podría considerar que todo aquello que se vaya reconociendo
carácter a carácter sin poseer un significado semántico por si
solo forma parte del analizador léxico, por ejemplo:
Analizador léxico Analizador Sintáctico
0. A:=0;
123 Suma+=123
printf printf(“Hola mundo”);
2. Eficiencia: Debido a que gran parte del tiempo de la traducción
se invierte en la etapa de reconocimiento de componentes
léxicos, el diseño e implementación de un analizador léxico
independiente mejora notablemente el funcionamiento de un
traductor, pues se le puede aplicar técnicas de manejo de buffer
para lectura de caracteres de entrada y procesamiento de
patrones.
3. Portabilidad: Al poseer un analizador léxico independiente se
puede limitar los problemas de compatibilidad de dispositivos,
peculiaridades del alfabeto de partida y juego de caracteres
bases, lo cual hace que el resto del sistema sea ajeno a estos
problemas y como consecuencia facilita la portabilidad entre
plataformas.
4. Facilidad en reconocimiento de patrones complejos: Una razón
más por la que se suele realizar el análisis léxico por separado
es el reconocimiento de patrones complejos. Por ejemplo si se
34
requiriere realizar un proceso de prebúsqueda2
, la complejidad
del diseño y de la implementación se dispararía por lo que
tratarlo dentro del analizador sintáctico sería algo
prácticamente imposible.
Se podría considerar que la implementación de un analizador
léxico posee la estructura:
(Expresión regular1) {Acción a ejecutar1}
(Expresión regular2) {Acción a ejecutar2}
. .
. .
. .
(Expresión regularn) {Acción a ejecutarn}
Donde cada acción a ejecutar describe que acción deberá realizar el
analizador léxico cuando una cadena de entrada coincida con la expresión regular
definida, finalizando casi siempre con la devolución de una categoría léxica
asociada, siendo los siguientes conceptos de fundamental importancia al momento
de estudiar la construcción de analizadores léxicos.
• Patrón: Es una expresión regular.
• Token: Categoría léxica, es directamente asociada a cada
patrón, cada token es convertida en número ó identificador
único en todo el traductor, siendo en ocasiones asociada al
mismo, valores adicionales necesarios para las fases
posteriores. Es lo que el terminal para el analizador
sintáctico.
• Lexema: Es una secuencia de caracteres que encaja con un
patrón.
2
Prebúsqueda (en ingles, Look Ahead): Es una técnica utilizada en la construcción de lenguajes y
traductores de lenguajes, el cual permite entre otras cosas saber con anticipación que valores
siguen al que se está analizando.
35
II.2.2. Análisis Sintáctico (Parser)
Un analizador sintáctico puede escribirse/diseñarse por medio de
una gramática libre de contexto (en adelante, GLC) ó por medio de la forma
normal de Backus (Backus-Naur Form, en adelante BNF), su tarea consiste en
agrupar los tokens en forma jerárquica y construir las frases gramaticales, si estas
frases son reconocidas como parte del lenguaje definido en analizador sintáctico
construye el árbol sintáctico (ver Figura 8), el cual es el punto de partida para el
analizador semántico.
El hecho de previamente formalizar la escritura del analizador
sintáctico ofrece la ventaja de poder encontrar ambigüedades, facilita la
ampliación y/o modificación del lenguaje, y al proporcionar una estructura de
lenguaje facilita la generación de código y detección de errores.
El analizador sintáctico debe informar de los errores sintácticos
que vaya encontrando e intentar recuperarse de ellos a fin de continuar la
traducción, controlar el flujo de tokens reconocidos por la fase anterior de análisis
y en ocasiones incorpora acciones semántica, con lo cual el proceso se convierte
en una traducción dirigida por sintaxis.
36
De las tres fases de análisis, es en el analizador sintáctico en quien
recae la dura misión de manejar los errores.
Considerar el manejo de errores desde el momento de que se
comienza a pensar en construir un traductor es lo más sensato, pues caso contrario
en algún momento de la construcción del mismo se tendrá que hacer una vuelta
atrás para incorporarlo.
Los errores habituales pueden englobarse en los siguientes puntos:
• Léxicos, mala escritura de identificador, palabra clave u
operador.
Proposición
de asignación
=
Identificador
E
Expresión
Expresión
Identificador
*
^
m
Identificador
c
Numero
2
Expresión
Expresión Expresión
Fuente: Gálvez et. al 2005:57
Figura 8 - Árbol de análisis sintáctico para E=m*c^2.
37
• Sintácticos, expresión aritmética ó paréntesis no
equilibrados, fin de sentencia ‘;’.
• Semánticos, operador aplicado a un operando incompatible.
• Lógicos, como ser una llamada infinitamente recursiva.
• De corrección, el programa no hace lo que se esperaba.
De todos los errores citados los errores de corrección y lógicos
resultan imposibles de detectar por el traductor, el primero por ser una concepción
de la abstracción del programador y en lo que respecta a errores lógicos por el
exceso de esfuerzo computacional a fin de seguir los flujos del programa en
ejecución para averiguar en que punto donde se generó la llamada recursiva, lo
cual resulta no solo altamente costoso, si no que habitualmente imposible.
Se desea que al momento de detectar algún tipo de error un
analizador sintáctico sea capaz de:
• Informar la naturaleza del error y su localización lo más
exactamente posible.
• Una vez corregido el error continuar construyendo el árbol
sintáctico desde el punto en que se detuvo.
• Distinguir entre errores y advertencias.
• No debe ralentizar en forma significativa al compilador.
Existen diversos métodos o forma para el menaje de errores a
continuación se exponen algunos de ellos, como ser:
Ignorar el problema (en inglés, panic mode); Consiste en ignorar
todas las entradas hasta encontrar una condición de seguridad, como fin de
sentencia, a partir del mismo se prosigue con el análisis en forma normal y todos
los tokens desde que se produjo el error hasta la condición de seguridad quedan
descartados, así como los consumidos antes del mismo.
38
Recuperación a nivel de frase; Cada vez que encuentra un error
intenta corregirlo, por ejemplo si fuera el caso de una sentencia al que le falta un
punto y coma (‘;’), el traductor agrega dicho carácter y prosigue, el problema con
esto es que puede dar lugar a recuperaciones infinitas, pues al intentar corregir el
error podría producir un nuevo error y luego intentaría corregir el error que
produjo y así sucesivamente.
Reglas de producción adicionales; Se añade a la gramática del
lenguaje reglas de producción para reconocer errores comunes, lo cual da mayor
control y permite emitir finalmente mensajes de advertencia, en lugar de un
mensaje de error, pues permite detectar un error, corregirlo y proseguir.
Corrección global, Para este método al momento de detectar un
error, el analizador sintáctico solicita toda la secuencia de tokens al analizador
léxico, a fin de producir una secuencia y su correspondiente árbol sintáctico, de
forma que sea lo más parecido posible al original, pero sin errores.
Gramática utilizada por los analizadores sintácticos.
Desde el punto de vista de la escritura de un analizador sintáctico,
se puede considerar la gramática de libre contexto, si bien existen construcciones
sintácticas que no pueden generarse mediante una gramática de libre contexto, si
no por medio de una de nivel superior, como ser las construcciones del lenguaje
PERL.
Una gramática G se define por medio de los elementos (N, T, P, S),
en donde:
N: Conjunto de No Terminales.
T: Conjunto de Terminales.
P: Conjunto de reglas de Producción.
S: Axioma o símbolo inicial de la gramática.
39
Ejemplo: gramática no ambigua que reconoce las operaciones
aritméticas (suma y multiplicación).
E E + T
| T
T T * F
| F
F id
| num
| ( E )
Siguiendo con la definición formal de lo que es una gramática
tenemos que:
N = {E, T, F}
T = {+, *, id, num, (, )}
P = Son las 7 reglas de la gramática.
S = E (símbolo inicial).
Una regla de producción es una regla de reescritura, en donde el
lado izquierdo de la regla (el no terminal) es sustituido por su parte derecha
(secuencia de terminales y/o no terminales), por ende se dice que una cadena α
deriva en β, y se denota βα ⇒ , cuando:
*T)(Ne ∪,⇒ βαβα con , y siendo:
δAσα ≡
δτσβ ≡
Dentro de una α puede haber varios A, lo cual da lugar a varias
derivaciones posibles, surgiendo los siguientes conceptos:
• Derivación por izquierda: Aquella en la que la reescritura
se realiza en la terminal de más a la izquierda.
( ) ΡτΑ*ΤΝδττσ,ΝΑdonde ∈→∧∪∈∧∈
40
• Derivación por derecha: Aquella en la que reescritura se
realizar en la terminal de más a la derecha.
Ejemplos de derivaciones, sobre la base de la siguiente gramática
ambigua:
( )Ε|
id|
num|
ΕΕ
ΕΕΕ
∗
+→
Derivación por izquierda:
↑↑↑↑↑
+∗⇒+∗⇒+∗⇒+∗⇒+⇒ idididΕididΕΕidΕΕΕΕΕΕ 321211
Derivación por derecha:
↑↑↑↑↑
+∗⇒+∗⇒+∗⇒+∗⇒+⇒ idididΕididΕΕidΕΕΕΕΕΕ 321211
Tanto en caso de la derivación por izquierda como en la derivación
por derecha, el símbolo a derivar se encuentra señalado con una flecha apuntando
al mismo (↑ ), la cadena resultante de una derivación recibe el nombre de formas
senténciales3
.
De este modo al analizar una sentencia del lenguaje se puede
obtener su árbol de análisis sintáctico, el cual a su vez obedece a una gramática y
es la representación utilizada para describir la derivación la misma. Se parte del
axioma inicial como raíz del árbol y conforme se vaya necesitando se grafica las
hojas hasta llegar a la cima o fondo del mismo.
3
Forma Sentencial: Cualquier secuencia de terminales y no terminales obtenida mediante
derivaciones a partir del axioma inicial.
41
El árbol debe poseer una sola raíz, el cual es el axioma inicial y
conforme se va avanzando las hojas del árbol se construyen, las cuales
constituyen la forma sentencial, si por alguna circunstancia la sentencia a
reconocer no perteneciera al lenguaje generado a partir de la gramática definida,
será imposible obtener su árbol de análisis sintáctico, en cambio si a partir de la
sentencia se pudiera construir más de un árbol para reconocerlo, nos encontramos
frente a un caso de gramática con ambigüedades o ambigua.
Partiendo de la generación del árbol sintáctico se podría decir que
existen dos tipos de analizadores.
Tipos de analizadores sintácticos:
1. Descendentes: Parten del axioma inicial, realizando
derivaciones sucesivas hasta llegar a reconocer la sentencia,
estos pueden ser.
a. Con retroceso.
b. Con funciones recursivas.
c. Predictivos con gramáticas LL(1).
2. Ascendentes: Parten de la sentencia de entrada, aplicando
derivaciones inversas hasta el axioma inicial, estos pueden ser:
a. Con retroceso.
b. Gramáticas LR(k).
Análisis descendente con retroceso; se parte del axioma inicial y
se aplican todas las reglas de posibles al no terminal de más a la izquierda de la
forma sentencial. Todas las sentencias de un lenguaje representan nodos del árbol
(árbol universal), el cual es producto de todas las posibles derivaciones desde el
axioma inicial, por ende cuando comprueba la pertenencia de una sentencia al
lenguaje, el árbol sintáctico de la misma está compuesto por los nodos desde el
punto actual a la raíz. Es recomendable utilizar una cláusula de cancelación
42
(criterios de poda) para los casos de búsquedas infructuosas, a fin para evitar que
el árbol se vuelva infinito en el intento de reconocer alguna sentencia no válida.
Con este método lo que se desea es buscar por medio del árbol
universal, recorriendo rama a rama, en caso de que una forma sentencial no
coincida con la sentencia analizada, la rama es desechada, en caso de coincidir, la
sentencia es aceptada y se retorna su árbol y por ultimo, si por medio de ninguna
rama se reconoce la sentencia, la misma debe ser rechazada.
El análisis descendente con retroceso no suele ser utilizado, si
quiera para el lenguaje natural, debido a que el retroceso es altamente ineficiente,
sumado a esto la existencia de varias otras técnicas de análisis más potentes y
basados en diferentes técnicas de análisis lenguajes.
Algoritmo de análisis descendente con retroceso:
Precondición: FormaSentencial= ( )**
ΤΝδyΝ,Α,Τμconδ,Αμ ∪∈∈∈
AnálisisDescendenteConRetroceso(formasentencial,entrada)
Si ≡μ ParteIzquierda(entrada) retornar Fin Si
ParaCada ∈ip
i
αΑp/Pp ii →≡∈
FormaSentencial ( )Τ∪Ν∈
Ν∈ΑΤ∈Α≡≡ ∗
'y
,','con,'''δαμ i
δ
µδµ
Si ≡'µ entrada
¡Sentencia reconocida !
Retornar
Fin Si
AnalisisDescendenteConRetroceso(FormaSentencial´, entrada)
Si ¡Sentencia reconocida ! Retornar Fin Si
Fin Para
Fin AnálisisDescendeteConRetroceso
Para la llamada deberíamos hacerlo de la siguiente manera:
43
AnálisisDescendenteConRetroceso(AxiomaInicial,
CadenaAReconocer)
Si no ¡Sentencia reconocida !
¡¡ SENTENCIA NO RECONOCIDA !!
Fin Si
Análisis descendente con funciones recursivas; Unas de las
formas más sencillas de crear un análisis descendente con funciones recursivas
son las gramáticas en notación BNF, pues la misma permite utilizar palabras,
símbolos y sus sencillas reglas de escrituras a fin de escribir analizadores
sintácticos suficientemente potentes.
Análisis descendentes predictivos con gramáticas LL(1); En el
análisis descendente predictivo para cada símbolo no-terminal a ser analizada
existe únicamente una regla de producción que se puede aplicar. Este tipo de
análisis está basada en gramáticas LL(1), donde LL(1) significa:
L (Left-to-Right): Se lee la cadena de izquierda a derecha.
L (Left-most): Derivación por izquierda.
1: Símbolo de anticipación.
Construir un analizador sintáctico predictivo no recursivo por
medio de una pila es posible; el problema durante el análisis predictivo radica en
determinar la producción a aplicar a un no-terminal, partiendo del símbolo
analizado actualmente y el siguiente símbolo de entrada, para ello el analizador
sintáctico no recursivo busca en la tabla de análisis sintáctico que regla de
producción debe aplicar.
Pasos para reconocer una cadena por medio de LL(1):
• La cadena a reconocer, junto con el apuntador que indica el
token de pre-búsqueda actual, denotaremos por a al token
44
• Una pila de símbolos, el cual contendrá a los terminales y
no terminales, considérese X como la cima de la pila.
• Una tabla de chequeo de sintaxis, asociada de forma
univoca a la gramática, denotaremos por M a la tabla, el
cual tiene la configuración siguiente: M[N x T U {$}].
La cadena de entrada debe acabar con EOF (fin de cadena, en
adelante $), a fin de reconocer una cadena aplicando el presente algoritmo se debe
consultar de forma reiterada la tabla M hasta que se logre aceptar o rechazar la
sentencia, para el efecto se inicia con una pila que en su base contendrá a $ y el
axioma inicial S de la gramática sobre la misma. Los pasos a seguir durante la
consulta son excluyentes y se exponen a continuación:
1. Si X = a = $ entonces ACEPTAR.
2. Si X = a ≠ $ entonces
- Se desapila X, y
- Se avanza el apuntador.
3. Si X ∈ T y X ≠ a entonces RECHAZAR.
4. Si X ∈ N entonces consultar la entrada M[X, a] de la tabla, y:
- Sí M[X, a] está vacía: Rechazar.
- Sí M[X, a] no está vacía, se reemplaza X de la pila e
insertar el consecuente (parte derecha) en orden inverso.
Ejemplo; SI M[X, a] = {X → LMN}, quitar X de la
pila e insertar LMN en orden inverso, ó sea NML.
Una vez aplicada la regla, ya no se puede volver a desaplicar como
sucede con las gramáticas con retroceso.
La principal dificultad de este tipo de análisis radica en que existe
un número reducido de gramáticas LL(1), aunque si bien es cierto que existe un
algoritmo para convertir gramáticas de contexto libres a su equivalentes LL(1).
45
Operaciones para convertir una GLC en una Gramática
LL(1):
1. Limpiar la gramática, Eliminar símbolos muertos y símbolos
inaccesibles.
2. Factorizar por izquierda.
3. Eliminar recursividad por izquierda.
Gramática de partida G(E):
F^TQ
F-PL
id|(E)F
numLP
F|FT
T|ΕΕΕ
→
→
→
÷→
∗→Τ
+→
Limpiando la gramática. Eliminando símbolos muertos:
F^TQ
id|(E)F
F|FT
T|ΕΕΕ
→
→
∗→Τ
+→
Limpiando la gramática. Eliminando símbolos inaccesibles:
id|(E)F
F|FT
T|ΕΕΕ
→
∗→Τ
+→
Factorizado quedaría (Gramática equivalente):
id|(E)F
|FT'T'
FT'
|TE'E'
E'TΕ
→
∗→
→Τ
+→
→
ε
ε
46
Construcción de la tabla de símbolos:
Para la construcción de la tabla de símbolos se utilizan las
funciones: PRIMERO (α).
SIGUIENTE (A).
PRIMERO (α): Si α es una cadena de símbolos gramaticales,
entonces, PRIMERO (α) es el conjunto de terminales que inician las cadenas
derivada de α. SI εα ∗⇒ entonces, ( )αPRIMEROε ∈
Algoritmo para calcular PRIMERO(x):
- Si x es terminal entonces, PRIMERO(x) = {x}.
- Si ε→x es una producción añadir ε a PRIMERO(x).
- Si kyyyx ,...,, 21→ es una producción.
- Si a ∈ PRIMERO(Yi) y ε está en todos los PRIMERO(Yi)…
PRIMERO(Yi-1) entonces añadir a a PRIMERO(x).
- Si ε está en PRIMERO(Yj), para todo j = 1, 2, …, k entonces
añadir ε a PRIMERO(x).
- Ejecutar hasta que no se puedan añadir más elementos a
PRIMERO(x).
Algoritmo para calcular SIGUIENTE(A):
Aplicar a cada no-terminal de la gramática, hasta que no se puedan
añadir más elementos.
- Para el símbolo distinguido añádase $, a SIGUIENTE(A), si A
es axioma.
- Si hay una regla de producción αΒβΑ → , añádase al conjunto
PRIMERO( β ) a SIGUIENTE(B), excepto ε .
- Si αΒΑ → ó αΒβΑ → son reglas de producciones, tal que
PRIMERO( β ) contenga ( )εβε *⇒ . Entonces añadir el
conjunto SIGUIENTE(A) a SIGUIENTE(B).
47
Figura 9 - PRIMEROS y SIGUIENTE.
Ejemplo: Sea una producción ETΕ → :
T E’
… …
PRIMEROS(T) PRIMEROS(E’) SIGUIENTES(T)
Fuente: Revelle 2006:10
Calculando PRIMEROS y SIGUIENTES, para gramática (E):
Primero(E): {(, id}
Primero(E’): {+, ε}
Primero(T): {(, id}
Primero(T’): {*, ε}
Primero(F): {(, id}
Siguiente(E): { $, ) }
Siguiente(E’): {$, )}
Siguiente(T): {+, $}
Siguiente(T’): {+, $, ) }
Siguiente(F): {*, +, $, )}
Tabla 1 - Tabla de análisis sintáctico LL(1).
T
N
id + * ( ) $
E E'TΕ → E'TΕ →
E’ E'TΕ' +→ ε→E' ε→E'
T FT'T → FT'T →
T’ ε→T' FT'*T → ε→T' ε→T'
F idF → (E)F →
Fuente: Gálvez et. al 2005:71
La tabla viene asociada exclusivamente a la gramática, y representa
su autómata finito con pila, este método es mucho más eficiente que el análisis
con retroceso e igual de eficiente que el análisis con funciones recursivas. Una
ventaja sobre el método con funciones recursivas es que se puede automatizar la
generación de las tablas, por ende una modificación en la gramática de entrada no
48
es fuente de grandes problemas desde el punto de vista de la construcción de una
nueva tabla.
Recuperación de errores:
Una cadena de entrada es errónea desde el punto de vista
sintáctico, sí se llega a una celda vacía en la tabla de símbolos, en este punto no
solo es útil detectar el error, si no buscar los mecanismos para tratarlos, para ello
el traductor debe:
1. Anunciar el error a fin de facilitar su localización.
2. Recuperarse y seguir analizando el resto de la cadena de
entrada.
Métodos de manejos de errores:
1. A nivel de frase (Ad-Hoc): Llenar todas las celdas vacías con
marcas que permitan seleccionar el ó los procedimientos más
adecuados para el tratamiento del error, para lo cual es
necesario estudiar las posibles causas en cada celda dada y las
acciones correctivas para el error.
2. Método Panic(Sincronización): Este método consiste en saltar
tantos símbolos de la secuencia de cadena de entrada que sean
necesarios, hasta llegar a un símbolo situado al tope de la pila
que permita la selección de una celda no vacía.
El proceso de recuperación por el método Panic, permite ser
optimizado para lo cual será necesario realizar una delimitación
de los conjuntos de símbolos de sincronización.
Delimitación de símbolos de sincronización: Consiste en
seleccionar los tipos de errores que se desean tratar y para cada caso en particular
seleccionar los símbolos de sincronización.
49
Casos más comunes:
1. Para cada símbolo no terminal A, incluir como símbolos de
sincronización los SIGUIENTES(A).
2. Si el lenguaje utiliza delimitadores de bloques o sentencias y se
omite el delimitador de fin, incluir como símbolos de
sincronización PRIMEROS del bloque o sentencia.
3. Si un no terminal A se deriva en una cadena vacía, resulta
imposible detectar el error.
4. Si un símbolo terminal ‘a’ aparece en el tope de la pila y no
parea con el símbolo de entrada, se puede optar por quitar el
símbolo terminal ‘a’ de la pila y emitir un mensaje indicando
que el mismo ha sido omitido.
Análisis ascendentes; este tipo de análisis tiene como finalidad la
construcción del árbol de análisis sintáctico, partiendo del token hasta llegar al
axioma inicial, disminuyendo la posibilidad de reglas mal aplicadas y ampliando
la gama de gramáticas con posibilidades de ser analizadas.
Esté o no presente retroceso como parte de la definición de la
gramática, la cadena de entrada estará divida en dos partes, βα y :
β : Secuencias de tokens por consumir ( ∗
Τ∈β ) y siempre
coincidirá con algún trozo de la parte derecha de la cadena de
entrada. Inicialmente β coincide con toda la cadena a reconocer,
incluido $.
α : Resto de la cadena de entrada, trozo de la cadena de entrada al
que se ha aplicado algunas de reglas de producción, en sentido
inverso ( ( )∗
Τ∪Ν∈α ), representando al árbol sintáctico visto
desde arriba, la cual se forma conforme se van aplicando las reglas
50
de producción a β , en otras palabras α es una forma sentencial
ascendente.
La mecánica de todo análisis ascendente consiste en ir aplicando
operaciones, partiendo de una configuración inicial (entiendase configuración por
el par βα − ), las cuales permiten transitar de una configuración de origen a una
de destino, finalizando una vez consumidos todos los tokens de entrada en β y la
configuración de destino sea en la que α represente al árbol sintáctico por
completo. Las operaciones disponibles son cuatro, las cuales se exponen a
continuación:
1. ACEPTAR: Se acepta la cadena ≡β $ y ≡α S.
2. RECHAZAR: Cuando la cadena de entrada no es válida.
3. Reducir: Consiste en aplicar una regla de producción hacía
atrás a algunos elementos situados a la derecha de α .
4. Desplazar: Consiste en quitar el terminal más a la izquierda de
β y ponerlo a la derecha de α .
Una cadena de entrada es aceptada cuando, α es el axioma inicial,
β es una tira nula y solo contiene $, en caso de que β no sea una tira nula, α no
sea el axioma inicial y no puede aplicarse ninguna regla, la cadena se rechaza.
Antes de llegar a aceptar o rechazar cadena alguna, primeramente se debe aplicar
todas las reducciones a α y luego realizar los desplazamientos.
Análisis ascendentes con retroceso; Este tipo de análisis al igual
que su par del descendente intenta probar todas operaciones posibles de reducir y
desplazar, por medio de la fuerza bruta, hasta llegar a construir el árbol sintáctico
o agotar todas las opciones, en cuyo caso rechaza la cadena de entrada.
51
Precondición: ( )∗
Τ∪Ν∈α y
∗
Τ∈β
AnálisisAscendenteConRetroceso ),( βα
Para cada pi Ρ∈ hacer
Si consecuente(pi) ≡ cola(α )
'' βα − = reducir βα − por la regla pi
Si ≡α S and εβ ≡
¡ Sentencia reconocida ! (ACEPTAR)
si no
AnálisisAscendenteConRetroceso )','( βα
Fin si
Fin si
Fin Para
Si εβ ∗
'' βα − = desplazar βα −
AnálisisAscendenteConRetroceso )','( βα
Fin si
Fin AnálisisAscendenteConRetroceso
Y la llamada al algoritmo:
AnálisisAscendenteConRetroceso( ,ε CadenaAReconocer)
Si NOT ¡ Sentencia reconocida !
¡ Sentencia no reconocida ! (RECHAZAR)
Fin si
Ante gramáticas que posean reglas con épsilon (ε), el presente
algoritmo termina por producir recursividad infinita, debido a que dicha regla
puede aplicarse hacia atrás produciendo una rama infinita hacia arriba.
El método de análisis ascendente con retroceso consume más
rápidamente los tokens de entrada que el análisis descendente con retroceso, lo
que le permite tomar decisiones con mayor cantidad de información y de forma
52
más eficiente, aunque de igual modo resulta inviable para aplicaciones prácticas,
pues su ineficiencia frente a otros métodos sigue siendo notoria.
Análisis ascendente de gramáticas LR (k); Es el método más
difundido, conocida como gramática LR(k), permite el análisis de una amplia
gama gramáticas de libre contexto, en donde LR(k) significa:
L: Consume los símbolos de izquierda a derecha (en inglés, Left-
to-rigth).
R: Por reducir por la derecha (en inglés, Rightmost derivation).
k: Cantidad de símbolos anticipación que se utiliza para tomar las
decisiones, por defecto 1, razón por la cual es comúnmente conocida como LR(1).
El análisis LR resulta atractivo debido a diversas razones:
• Reconoce prácticamente todos los lenguajes derivados de
las gramáticas de contexto libres.
• Es el método de análisis sin retroceso más difundido, por su
fácil implementación.
• Realiza la detección de errores conforme se realiza el
análisis y realiza la recuperación de las mismas de forma
controlada.
Unas de las desventajas de este método radica en el hecho de que
construir manualmente un analizador sintáctico LR para una gramática de
contexto libre lleva mucho trabajo, afortunadamente existen las herramientas
generadoras, en las que proveyendo una gramática de contexto libre, se puede
generar el analizador sintáctico para la misma, si la gramática que se proporciona
al generador posee ambigüedades o construcciones difíciles de entender, emite un
mensaje al diseñador especificando la posible regla de producción, a fin de que el
mismo pueda verificarlo y corregirlo.
53
Los analizadores LR basan su funcionamiento en la existencia de
una tabla de análisis (acciones-transiciones) asociada a la gramática siendo en este
caso similar a los analizadores LL(1).
La confección de tabla de análisis podría resultar una tarea hasta si
se quiere algo compleja, si bien dependiendo del método seleccionado para hacer
la tabla la misma puede terminar siendo una tarea mecánica.
Existen tres métodos principales para la construcción de tablas
de análisis:
1. LR Sencillo (en inglés, SLR): El más fácil de aplicar aunque el
menos potente.
2. LR canónico ó LR(1): De los tres el más potente, y el más
complejo.
3. LALR (LR con examen de anticipación - en inglés, Look
Ahead LR): Es el método intermedio y el más difundido de las
tres, se implementa sin mucho esfuerzo de forma eficiente.
Un método es más potente que el otro en relación a la cantidad de
gramática admitidas, aunque a mayor número de gramáticas mayor complejidad.
La pila del autómata de pila utilizado para el análisis LR está
formado por parejas de símbolos (terminales y no terminales) y estados.
1. Si acción [Sm, ai] = Desplazar, el analizador inserta en la pila el
símbolo de entrada ai y el siguiente estado S, obtenido gracias a la evaluación
Goto [Sm, ai]. Ahora, ai+1 pasa a ser el símbolo de la entrada a leer y el analizador
se encuentra ahora en el estado S.
2. Si acción [Sm, ai] = Reducir, entonces el analizador ejecuta una
reducción utilizando la regla A β.
3. Si acción [Sm, ai] = Aceptar, el análisis se ha terminado con
éxito.
54
4. Si acción [Sm, ai] = Error, se ha descubierto un error. El analizar
llama al procedimiento de recuperación de error.
Figura 10 - Esquema Sinóptico de un analizador LR
Pila
Sm
Xm
Sm-1
Xm-1
…
S0
Fuente: Gálvez et. al 2005:78
Algoritmo para el Analizador LR:
Hacer que p apunte al primer símbolo de w$;
Mientras .t.
Sea S el estado situado en la parte superior de la pila;
Sea a el símbolo al que apunta p;
si acción[S, a] = Desplazar S’
Cargar a y luego S’ en la pila;
Hacer que p apunte al siguiente símbolo;
sino si acción[S, a] = Reducir A β
Sacar de la pila 2|β| símbolos pila;
Sea S’ el estado situado en el tope de la pila;
Cargar A y el resultado de goto[S’, A] en la pila;
Ejecutar las acciones semánticas de A β;
sino si acción [S, a] = Aceptar
Entrada a1 a2 a3 … an $
Tabla de Análisis
Estado Acción Goto
Analizador LR
(Programa de proceso) Salida de
Análisis
55
retornar;
sino
error();
fin si
fin mientras
Ejemplo de aplicación del algoritmo: Evolución del análisis con
gramática LR para el reconocimiento de la siguiente secuencia de entrada id * id +
id:
Sea la gramática:
idF6.
(E)F5.
FΤ4.
FTΤ3.
TΕ2.
ΕΕΕ1.
→
→
→
∗→
→
+→
Tabla 2 - Tabla de Análisis LR.
Fuente: Aho et. al 1990:225
Estado Acción Goto
Di: Desplazar hasta i.
Rj: Reducir por j.
acp: Aceptar.
id + * ( ) $ E T F
0 D5 D4 1 2 3
1 D6 acp
2 R2 D7 R2 R2
3 R4 R4 R4 R4
4 D5 D4 8 2 3
5 R6 R6 R6 R6
6 D5 D4 9 3
7 D5 D4 1
8 D6 D1
9 R1 D7 R1 R1
10 R3 R3 R3 R3
11 R3 R3 R3 R3
56
Tabla 3 - Reconocimiento de id*id+id.
Pila Entrada Acción-Goto
0 id * id + id$ Desplazar
0 id 5 * id + id$ Reducir: F -> id
0 F 3 * id + id$ Reducir: T -> F
0 T 2 * id + id$ Desplazar
0 T 2 * 7 id + id$ Desplazar
0 T 2 * 7 id 5 + id$ Reducir: F -> id
0 T 2 * 7 F 10 + id$ Reducir: T -> T * F
0 T 2 + id$ Reducir: E -> T
0 E 1 + id$ Desplazar
0 E 1 + 6 id$ Desplazar
0 E 1 + 6 id 5 $ Reducir: F -> id
0 E 1 * 6 F 3 $ Reducir: T -> F
0 E 1 + 6 T 9 $ Reducir: E -> E + T
0 E 1 $ Aceptar
Fuente: Aho et. al 1990:226
Construcción de tablas de análisis para un SLR (LR (0)):
Pivote (mango, asa): Es una subcadena que coincide con par parte
derecha de una producción (regla).
Prefijo viable: Secuencia de símbolos que se pueden formar de
izquierda a derecha hasta un símbolo t (terminal)
Ítems: Una producción representada entre paréntesis cuadrados “[”,
“]” y un punto “.” en la parte derecha de la producción.
El método SLR tiene como premisa construir un autómata finito de
determinista (en adelante, AFD) a partir de la gramática para reconocer los
prefijos viables. Los estados del AFD se obtienen gracias al agrupamiento de los
ítems asociados con cada producción de la gramática.
Construcción de una AFD para un analizador SLR:
1. Gramática aumentada: Sea S el símbolo inicial de una
gramática G, la cual se reescribe a fin de obtener una
57
gramática aumentada (G’) en la que S’ es el nuevo símbolo
inicial quedando SS' → .
2. Operación de Cierre o Clausura: Siendo I el conjunto de
ítems, la operación de cierre para I, denotaremos como
cierre(I), la cual será obtenida aplicando las siguientes
reglas.
a. I ∈ cierre(I).
b. Si Ββ].α[Α → ∈ cierre(I) y existe la producción
→Β µ, entonces [ →Β .µ] ∈ cierre(I).
c. Repetir paso b hasta que no se añada nuevo ítem a
cierre(I).
Sea I = { EE' ⋅→ }
Cierre(I)={[ EE' ⋅→ ], [ TΕΕ +⋅→ ], [ TΕ ⋅→ ], [ F*TT ⋅→ ],
[ FT ⋅→ ], [ )(F Ε⋅→ ], [ idF ⋅→ ]}
3. Función GOTO: Función de transición de estados a partir
de uno dado y según el símbolo de entrada en cuestión.
Goto(I, X), donde I es el conjunto de ítems y X es un
símbolo de la gramática. Se define Goto(I, X), como el
cierre del conjunto de todos los ítems
[ ] [ ] Iα.XβΑquetalαX.βΑ ∈→→ .
Gramática(G):
idF6.
(E)F5.
FΤ4.
FTΤ3.
TΕ2.
TΕΕ1.
→
→
→
∗→
→
+→
Gramática aumentada(G’):
idF7.
(E)F6.
FΤ5.
FTΤ4.
TΕ3.
TΕΕ2.
'1.
→
→
→
∗→
→
+→
Ε→Ε
58
En base a la gramática anterior (G’) y dado el siguiente
conjunto de ítems:
[ ] [ ]}TΕΕ,ΕΕ'{I +⋅→⋅→=
Entonces:
[ ] [ ]}TΕΕ{I,goto +⋅→=+
Función cierre( [ ]+I,goto ):
Cierre([ ]TΕΕ +⋅→ ) = {[ ]TΕΕ ⋅+→ , [ ]F*TT ⋅→ , [ ]FT ⋅→ ,
[ ])(F Ε⋅→ , [ ]idF ⋅→ }
Para obtener los estados asociados a una gramática SLR se debe
aplicar el siguiente algoritmo, de este modo se obtenemos el conjunto de ítems(C)
LR(0), en base a una gramática aumentada (G’):
Procedimiento ítems(G’):
C :={cierre({[ ]SS' ⋅→ })};
Repetir
Para cada I ∈ C y para cada símbolo X ( )Τ∪Ν∈
tal que goto(I, X) ε∉ y ∉ C.
Añadir goto(I, X) a C
Fin Para
Hasta (que no se puedan añadir más ítems a C)
Retornar
Gramática aumentada (G’):
idF7.
(E)F6.
FΤ5.
FTΤ4.
TΕ3.
TΕΕ2.
'1.
→
→
→
∗→
→
+→
Ε→Ε
59
Figura 11 - Conjunto LR(0) canónico para EE' ⋅→ .
Colección canónica que define los estados del AFD para un SLR:
I0 = EE' ⋅→
TΕΕ +⋅→
TΕ ⋅→
F*TT ⋅→
FT ⋅→
)(F Ε⋅→
idF ⋅→
I5 = goto(I0,id):
idF ⋅→
I6 = goto(I1,+):
TΕΕ ⋅+→
F*TT ⋅→
FT ⋅→
)(F Ε⋅→
idF ⋅→
I1 = goto(I0,E):
⋅→ EE'
TΕΕ +⋅→
I7 = goto(I2,*):
F*TT ⋅→
)(F Ε⋅→
idF ⋅→
I2 = goto(I0,T):
⋅→ TΕ
*FTT ⋅→
I8 = goto(I4,E):
)(F ⋅Ε→
TΕΕ +⋅→
I3 = goto(I0,F):
⋅→ FT
I9 = goto(I6,T):
⋅+→ TΕΕ
*FTT ⋅→
I4 = goto(I0,():
)(F Ε⋅→
TΕΕ +⋅→
TΕ ⋅→
F*TT ⋅→
FT ⋅→
)(F Ε⋅→
idF ⋅→
I10 = goto(I7,F):
⋅→ F*TT
I11 = goto(I8,)):
⋅Ε→ )(F
Fuente: Aho et. al 1990:231
60
Figura 12 - Diagrama AFD para los prefijos viables.
Fuente: Aho et. al 1990:232
Algoritmo para la construcción de la tabla de análisis SLR:
Entrada: Gramática aumentada (G’).
Salida: Funciones acción y goto.
Método.
1. Construir C = {I0, I1,…, In}, colección de estados asociados
a la gramática aumentada (G’).
a I7I0 I1 I6 I9
I2
I3
I4
I5
I8 I1
1
I7 I1
0
E + T *
T * F
(
id
F
(
id
F
(
id
id
E
F
T
)
+
a I3
a I4
a I5
a I4
a I5
a I6
a I2
a I3
(
61
2. El estado i es construido partiendo de Ii, apartir de allí se
determinan las acciones del analizador sintáctico para el
estado i según los siguientes criterios:
a. Si [ ]βαΑ a⋅→ ∈ Ii y goto(Ii, a) = Ij, entonces
acción[i, a] = “desplazar j” , donde a ∈ símbolo
terminal.
b. Si [ ]∈⋅→ αΑ Ii, acción[i, a] = “reducir αΑ → ”.
Para a ∈ SIGUIENTE(A), donde A ≠ S’.
c. Si [ ]∈⋅→ S'S Ii,, acción[i, $] = “aceptar”.
d. Si no coincide con ningún caso ó se generan reglas
contradictorias NO ES SLR.
3. Para todos los símbolos no terminales A construir
transiciones goto al estado i, siguiendo la regla:
Si goto(I, A)= Ij, entonces goto(I, A) = j.
4. Las entradas no reconocidas por las reglas 2 y 3, serán
consideradas “error”.
5. El estado inicial se construye a partir del conjunto de ítems
que contiene [ ]SS' ⋅→ (cierre[ ]SS' ⋅→ ).
Considerando la gramática aumentada (G’) del ejemplo anterior, se
considerará del conjunto de ítems de la serie canónica primeramente se toma I0.
I0:
EE' ⋅→
TΕΕ +⋅→
TΕ ⋅→
F*TT ⋅→
FT ⋅→
)(F Ε⋅→
62
idF ⋅→
Donde:
)(F Ε⋅→ , permite la entrada, acción[0, (] = desplazar 4.
idF ⋅→ , permite la entrada, acción[0, id] = desplazar 5.
I1:
⋅→ EE'
TΕΕ +⋅→
Donde:
⋅→ EE' , permite la entrada, acción[1, $] = aceptar,
TΕΕ +⋅→ , permite la entrada, acción[1, +] = desplazar 6.
I2:
⋅→ TΕ
*FTT ⋅→
Donde:
⋅→ TΕ , Siendo que SIGUIENTE(E)={$, +, (}, permite la entrada,
acción[2, $] = acción[2, +]= acción[2, )] = reducir
TΕ → .
*FTT ⋅→ , permite la entrada, acción[2, *] = desplazar 7.
Siguiendo paso a paso el algoritmo como se demostró en el
ejemplo, se llegará a construir la tabla de acción y goto.
Una gramática SLR(1), puede incurrir en error de desplazar/reducir
(shift/reduce), aún frente a gramáticas no ambiguas, por su parte las gramáticas
LR canónico y LALR proveen mayor tolerancia a este tipo de errores, aun
siempre existe la posibilidad de que alguna gramática no ambigua pueda producir
63
una tabla de acciones con conflictos desplazar/ reducir, más cabe resaltar que
dichas gramáticas son evitables en la construcción de lenguajes.
Construcción de tabla de análisis para un LR canónico ó
LR(1):
En los analizadores SLR: si [ ]∈⋅→ αΑ Ii, acción[i, a] = “reducir
αΑ → ”. Para a ∈ SIGUIENTE(A), donde A ≠ S’.
Sin embargo esto no siempre es posible debido a que
ocasionalmente el estado i podría estar en el tope de pila, quedando el prefijo
viable βα por debajo de dicho estado (i) y en consecuencia βA no puede ir seguida
de una a en una forma sentencial derecha, por lo que la reducción αΑ → no sería
válida para la entrada a.
Para solucionar el problema expuesto anteriormente los
analizadores LR canónicos se valen de un símbolo de anticipación, de ésta forma
se pueden aplicar restricciones a reglas de reducciones no válidas.
Ítem del análisis sintáctico LR(1)4
:
Sea [ ]aβ,αΑ ⋅→ , donde αβ→Α es una producción y a es un
Terminal o el marcador derecho $.
Sea [ ]⋅→ αΑ reducir por αΑ → , si el siguiente símbolo de
entrada es a.
Un ítem LR(1) [ ]aβ,αΑ ⋅→ es válido para un prefijo viable αδ si:
{ { ,βδδΑw*S
pdpd
wa⇒⇒ Donde:
pd = Parte derecha.
w = Terminales.
a = PRIMERO(w) ó w es ε y a es $.
4
LR(1): El “1” significa la longitud del siguiente elemento, símbolo de anticipación.
64
El método de construcción de colección de ítems LR(1), funciona
básicamente de la misma manera que para LR(0), a excepción de la operación de
cierre y goto, considérese:
1. I0 = cierre [ ]( ),$'S S⋅→
2. Si se tiene un ítem [ ]aBβ,αΑ ⋅→ y la producción ηB → ,
incluir cierre [ ]bη,⋅→Β , donde b ∈ PRIMEROS ( )αβ , por
tanto si δα es un prefijo viable valido de [ ]aBβ,αΑ ⋅→
implica que { { ,βδδΑ*S
pdpd
waBw ⇒⇒ donde a ∈ PRIMEROS(w).
Si βδβδ ηB aa ⇒ y b ∈ PRIMEROS ( )aβ , entonces δαη es
prefijo válido.
Construcción de la colección de ítems LR(1) para la gramática G’:
Entrada: Gramática aumentada (G’)
Salida: La colección de ítems LR(1) para (G’).
Procedimiento cierre(I)
Hacer
Para cada ítem[ ]∈⋅→ aBβ,αΑ I,
cada producción ∈→ γB G’
y cada Terminal b en PRIMERO ( )aβ
tal que [ ]∉⋅→ bγ,Β I, añadir [ ]bγ,Β ⋅→ a I
Fin Para
Mientras se puedan añadir más ítems a I
Retornar I
Retornar
65
Procedimiento goto(I, X)
Si J = colección de ítems[ ]aβ,αXΑ ⋅→ , tal que
[ ]∈⋅→ aβ,αXΑ I
retornar cierre(I)
Fin Si
Retornar
Procedimiento Ítems(G’)
C :={cierre({[ ]S,$S' ⋅→ })}
Hacer
Para cada colección I ∈ C y
X tal que goto(I, X) φ≠ y no esté en C
Añadir goto(I, X) a C
Fin Para
Mientras se puedan añadir colecciones de ítems a C
Retornar
Sea la siguiente gramática:
G(S):
d|cCC
CCS
→
→
G’(S’):
d|cCC
CCS
S$S'
→
→
→
Cálculo de primeros:
PRIMERO(S) = {c, d}
PRIMERO(C) = {c, d}
PRIMERO(C$) = {c, d}
Cálculo de colección de ítems cierre:
I0 = cierre [ ]( )S,$S' ⋅→ = {[ ],$'S S⋅→ , [ ],$CCS ⋅→ , [ ]c/dcC,C ⋅→ ,
[ ]c/dd,C ⋅→ }
66
I1 = goto(I0, S) = cierre [ ]( ),$SS' ⋅→ = { [ ],$S'S ⋅→ }
I2 = goto(I0, C) = cierre [ ]( ),$CCS ⋅→ = { [ ],$CCS ⋅→ ,
[ ]C,$cC ⋅→ , [ ],$dC ⋅→ }
I3 = goto(I0, c) = cierre [ ]( )C,$cC ⋅→ = { [ ]c/dC,cC ⋅→ ,
[ ]c/dcC,C ⋅→ , [ ]c/dd,C ⋅→ }
I4 = goto(I0, d) = cierre [ ]( )c/d,dC ⋅→ ={[ ]c/d,dC ⋅→ }
I5 = goto(I2, C) = cierre [ ]( ),$CCS ⋅→ ={[ ],$CCS ⋅→ }
I6 = goto(I2, c) = cierre [ ]( )C,$cC ⋅→ = { [ ]C,$cC ⋅→ ,
[ ]cC,$C ⋅→ , [ ]d,$C ⋅→ }
I7 = goto(I2, d) = cierre [ ]( ),$dC ⋅→ ={[ ],$dC ⋅→ }
I8 = goto(I3,C) = cierre [ ]( )c/d,cCC ⋅→ ={[ ]c/d,cCC ⋅→ }
I9 = goto(I3,c) = cierre [ ]( )c/dC,cC ⋅→ = ¡ YA EXISTE !
I9 = goto(I3,d) = cierre [ ]( )c/d,dC ⋅→ = ¡ YA EXISTE !
I9 = goto(I6,C) = cierre [ ]( ),$cCC ⋅→ ={[ ],$cCC ⋅→ }
I10 = goto(I6,c) = cierre [ ]( )C,$cC ⋅→ = ¡ YA EXISTE !
I10 = goto(I6,d) = cierre [ ]( ),$dC ⋅→ = ¡ YA EXISTE !
Ya no se puede seguir creando ítems y se termina.
67
Algoritmo para la construcción de la tabla de análisis LR canónico.
Entrada: Una gramática aumentada G’.
Salida: Funciones acción y goto de la tabla de análisis para LR
canónico.
Método:
1. Construir C={I1, I2, I3, …, In} la colección de ítems o estados
asociados a la gramática LR(1) para G’.
2. Partiendo de Ii, se construye el estado i. Las acciones a ser
aplicadas se determinan de la siguiente manera:
a. Si [ ]∈⋅→ ba ,αΑ β Ii y goto(I, a)= Ij,
acción[i, a] = “desplazar j”. a debe ser terminal.
b. Si [ ]∈⋅→ a,αΑ Ii, A≠ S’,
acción[i, a] = “reducir αΑ → ”.
c. Si [ ]∈⋅→ ,$SS' Ii, acción[i, $] = “aceptar”.
Si se produce un conflicto, entonces la gramática no es
LR(1) y falla el algoritmo.
3. Construir goto(i, A) para todo no terminal, si goto(Ii, A) = Ii,
entonces goto[i, A] = j.
4. Las entradas en las tablas de análisis sin correspondencia con
las reglas 2 y 3 son etiquetadas como “error”.
5. A partir del ítem [ ],$'S S⋅→ , se construye el estado inicial del
analizador sintáctico.
68
Figura 13 - Grafo de transiciones para los goto de S$S'→ .
Fuente: Aho et. al 1990:241
Tabla 4 - Tabla de análisis LR(1) para S$S'→ .
Fuente: Aho et. al 1990:242
Estado Acción Goto
c d $ S C
0 D3 D4 1 2
1 aceptar
2 D6 D7 5
3 D3 D4 8
4 R3 R3
5 R1
6 D6 D7 9
7 R3
8 R2 R2
9 R2
,$cCC ⋅→
I9
,$dC ⋅→
I7
,$'S S⋅→
,$CCS ⋅→
c/dcC,C ⋅→
c/dd,C ⋅→
I0
,$SS' ⋅→
I1
,$CCS ⋅→
C,$cC ⋅→
,$dC ⋅→
I2
c/d,dC ⋅→
I4
c/d,cCC ⋅→
I8
,$CCC ⋅→
cC,$C ⋅→
,$dC ⋅→
I6
c
dc/,CcC ⋅→
c/dcC,C ⋅→
c/dd,C ⋅→
I3
c
CC,$S' →
I5
69
Construcción de tabla de análisis LALR(1):
La construcción de tablas de análisis sintácticos por medio del
método LALR(1) es menos potente que por el método LR(1), pero más potente
que por el método SLR. Es el método de construcción de tablas de análisis más
utilizada por la facilidad de expresar las sintaxis de los lenguajes de programación
por medio de una gramática LALR, sumado a esto que las tablas de análisis
resultantes son considerablemente más pequeñas que las de LR(1).
Algoritmo para la construcción de la tabla de análisis LALR(1)
Entrada: Gramática Aumentada.
Salida: Acción y Goto.
Método:
1. Construir la colección de ítems C={I0, I1,…, In} y el AFD
correspondiente como en el método LR(1).
2. Se unen todos los estados que estén conformados por los
mismos ítems y solo difieran en el símbolo de anticipación,
esto dará como resultado un AFD simplificado.
3. C’ = {J0, J1,…, Jn} es la colección de ítems LR(1) resultante.
Construir a partir del estado Ji las acciones sintácticas para el
estado i. Si no aparece conflictos en la tabla resultante
LALR(1), la gramática es considerada LALR(1).
4. La construcción del goto se realiza de la siguiente manera; se
considera J como la unión de uno o más ítems LR(1), J= J1 U J2
U Jk3, entonces las producciones de goto(I1,X), goto(I2,X), …,
goto(In, X), son las mismas. Sea K la unión de todas las
colecciones que tienen la misma producción que goto(I1,X), lo
cual da como resultado que goto(J, X) = K.
Resultado de la unión de producciones LR(1):
I36:
I3=goto(I0,c)=cierre [ ]( )C,$cC ⋅→ ={[ ]c/dC,cC ⋅→ ,[ ]c/dcC,C ⋅→
,[ ]c/dd,C ⋅→ }
70
I6=goto(I2,c)=cierre [ ]( )C,$cC ⋅→ ={ [ ]C,$cC ⋅→ , [ ]cC,$C ⋅→ ,
[ ]d,$C ⋅→ }
I47:
I4=goto(I0,d)=cierre [ ]( )c/d,dC ⋅→ ={[ ]c/d,dC ⋅→ }
I7=goto(I2,d)=cierre [ ]( ),$dC ⋅→ ={[ ],$dC ⋅→ }
I89:
I8=goto(I3,C)=cierre [ ]( )c/d,cCC ⋅→ ={[ ]c/d,cCC ⋅→ }
I9 = goto(I6,C) = cierre [ ]( ),$cCC ⋅→ ={[ ],$cCC ⋅→ }
Partiendo de la base del AFD para el método LR(1), construido
anteriormente se muestra un AFD simplificado para LALR(1).
Figura 14 - AFD Simplificado para LALR(1).
Fuente: Revelle 2006:27
I0
I4, I7 I2
I3, I6 I8, I9
I1
I5
C
c
c
c
d
d
C
S
d
C
71
Una construcción de tabla de símbolos LALR(1), no debería
arrojar conflictos de desplazar/reducir, puesto que se parte de la base de que la
gramática fue aceptada como LR(1), para ello primeramente la gramática no tuvo
que haber tenido conflictos de este tipo, ahora bien, pueden aparecer conflictos
reducir/reducir como resultado de la unión de estados del AFD, aún cuando la
gramática fuera LR(1).
Tabla 5 - Tabla de análisis LALR(1) para S$S'→ .
Fuente: Aho et. al 1990:246
Ambigüedad y precedencia:
Toda gramática ambigua no es un LR, aunque existen situaciones
en las que las gramáticas ambiguas son valiosa utilidad en la programación de
lenguajes, como ser en la especificación de expresiones ú optimizaciones de
algunos casos especiales, Sin embargo, estando frente a construcciones con
gramáticas ambiguas se deben especificar construcciones para eliminar dichas
ambigüedades a fin de garantizar un solo árbol sintáctico para cada frase dada.
La precedencia y asociatividad operadores durante la construcción
de lenguajes nos aportan criterios, a fin de eliminar conflictos desplazar/reducir
que podrían presentarse debido a ambigüedades en la gramática.
La precedencia especifica el orden relativo de cada operador con
respecto a los demás operadores. El operador de más precedencia se evalúa antes
que el de menor precedencia.
Estado Acción Goto
c d $ S C
0 D36 D47 1 2
1 aceptar
2 D36 D47 5
36 D36 D47 89
47 R3 R3 R3
5 R1
89 R2 R2 R2
72
Operación: A#B&C
(A#B)&C Si & < #
A#(B&C) Si # > &
La asociatividad de un operador binario define cómo se operan tres
o más operándoos con dicho operador. Existen dos tipos de asociatividad y las
mismas pueden ser por izquierda o por derecha, las cuales van ligadas al tipo de
derivación que se esté utilizando.
Operación: A#B#C#D.
Asociatividad por izquierda: ((A#B)#C)#D.
Asociatividad por derecha: A#(B#(C#D))
Desde el punto de vista de los lenguajes de programación:
Si op1 < op2, entonces en op2 debe aparecer desplazar y en op1
reducir.
Ambigüedad de los else-balanceados:
Esto produce un conflicto desplazar/reducir cuando se dispone de
una gramática una sentencia if-expr--then-sentencia-else-sentencia sin endif, para
lo cual se propone adoptar la asociatividad del else con el ultimo then, de este
modo se pasa el else a la pila y desplaza.
Recuperación de errores:
Los métodos de recuperación de errores en las gramáticas LR se
basan en:
Modo Panic: Descartar los símbolos de la entrada y/o sacar
símbolos de la pila hasta alcanzar una situación válida para continuar el análisis
(elimina el error y, probablemente, más información de entrada).
73
PROBLEMA: Decidir si se saca de la pila o se avanza en la
entrada.
Como solución sería posible la inserción de símbolos de
sincronización. Consiste en saltar símbolos de la entrada hasta alcanzar algún
símbolo de sincronización.
PROBLEMA: Si se omiten algunos de los símbolos de
sincronización se pueden perder demasiados símbolos de entrada.
SOLUCIÓN: Optimizar los símbolos de sincronización en base a
los símbolos seguidores e iniciales de cada símbolo no terminal de la gramática.
Tabla de análisis: Consiste en completar las celdas vacías de la
tabla de análisis con procedimientos para el tratamiento del error y recuperación
del mismo para continuar con el análisis.
Métodos complejos basados en el algoritmo de Graham-
Rhodes: Permite recuperarse ante un error y además, en algunos casos, reparar el
mismo.
El método distingue dos fases:
1. Fase de condensación: donde se acota el contexto del error.
2. Fase de corrección: donde se intenta corregir el error.
Basándose en el análisis de los símbolos próximos en el tope de la
pila y en la entrada, la fase de corrección decide si hay que sacar de la pila o saltar
74
II.1.3. Análisis Semántico
El análisis semántico se encarga de velar que lo que se está
intentando realizar tiene un significado coherente, el mismo se encarga de la
verificación de tipos de datos, compatibilidad de expresiones, ámbitos, flujos de
controles y coherencia en entre parámetros. El analizador semántico trabaja en
estrecha cooperación con el analizador sintáctico a fin de realizar su tarea y en
ocasiones se lo encuentra subordinado al mismo.
El traductor debe tener la suficiente capacidad de realizar
comprobaciones estáticas y dinámicas, las cuales se resumen en:
La comprobación estática se realiza durante la etapa de análisis (A.
Léxico, A. Sintáctico, A. Semántico) y se encarga de la:
- Comprobación de compatibilidad de tipos.
- Comprobación de flujos de control.
- Comprobación de unicidad.
- Comprobaciones relacionadas con el nombre.
En la traducción de una pasada, valiéndose de las gramáticas
atribuidas, desde el analizador sintáctico se realizan las llamadas a las rutinas
semánticas y dichas rutinas son las que finalmente llaman al generador de código,
en cambio en traductores de dos o más pasadas, el análisis semántico se encuentra
separado de la generación de código, comunicándose finalmente con este a través
de un archivo intermedio, cuyo contenido se resume en el árbol sintáctico.
Sea cual sea el número de pasadas las rutinas semánticas hacen uso
de los registros semánticos, el cual tiene una forma de pila (la pila semántica),
almacena información semántica asociada a los operándoos y en ocasiones
también de los operadores.
75
Tabla de símbolos:
La tabla de símbolos es una estructura de datos de alto rendimiento
la cual almacena toda información relevante sobre los identificadores que de
alguna u otra manera intervienen en el análisis de un programa de entrada.
Algunas funciones de las funciones que desempeñan la tabla de
símbolos son:
1. Realizar chequeos semánticos; Verificar que una frase tenga su
antecesor y sucesor correspondiente y en forma correcta.
2. Controlar el ámbito de una variable o sentencia.
Una tabla de símbolos es una estructura de dinámica, la cual
habitualmente permanece en memoria solo en tiempo de ejecución, simplifican
significativamente la tarea del análisis sintáctico, así como asistir al analizador
semántico y en la generación de código, de ahí lo crítico de contar con una tabla
de símbolos construida y manejada de forma eficiente, pues en gran parte de la
compilación interviene la tabla de símbolos.
Fuente: Elaboración propia, basado en Cueva 1998:26
Tabla
de
Símbolos
Análisis Léxico
Análisis Sintáctico
Análisis Semántico
Generación de
Código
Optimización de
Código
Figura 15 - Interacción de etapas con la Tabla de Símbolos.
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C
Traductor Pseudocógido to C

Weitere ähnliche Inhalte

Was ist angesagt?

Algebra lineal problemas_resueltos
Algebra lineal problemas_resueltosAlgebra lineal problemas_resueltos
Algebra lineal problemas_resueltosmathbmc
 
Tabla laplace
Tabla laplaceTabla laplace
Tabla laplaceJORGE
 
Representación en series de Fourier
Representación en series de FourierRepresentación en series de Fourier
Representación en series de Fouriermarianyelimendez
 
Ejercicios jacobi
Ejercicios jacobiEjercicios jacobi
Ejercicios jacobidjp951
 
Probabilidad de error en modulación digital
Probabilidad de error en modulación digitalProbabilidad de error en modulación digital
Probabilidad de error en modulación digitalFrancisco Apablaza
 
Compiladores - Flex y Bison
Compiladores - Flex y BisonCompiladores - Flex y Bison
Compiladores - Flex y BisonSteven Tabango
 
10 transformada fourier
10 transformada fourier10 transformada fourier
10 transformada fourierAlex Jjavier
 
Alg lineal unidad 3
Alg lineal unidad 3Alg lineal unidad 3
Alg lineal unidad 3migwer
 
6 Codificación de fuente.ppsx
6 Codificación de fuente.ppsx6 Codificación de fuente.ppsx
6 Codificación de fuente.ppsxJoseBiafore1
 
Métodos numéricos- Problemario
Métodos numéricos- ProblemarioMétodos numéricos- Problemario
Métodos numéricos- ProblemarioBren MA
 
Unidad 2 ensamblador
Unidad 2   ensambladorUnidad 2   ensamblador
Unidad 2 ensambladoreveTalavera
 
Códigos bch, Reed Solomon, convolucionales
Códigos bch, Reed Solomon, convolucionalesCódigos bch, Reed Solomon, convolucionales
Códigos bch, Reed Solomon, convolucionalesEduardoMedrano19
 
Bus de datos, dirección y control
Bus de datos, dirección y controlBus de datos, dirección y control
Bus de datos, dirección y controlJulian1784
 
Método numérico - regla de simpson
Método numérico  - regla de simpsonMétodo numérico  - regla de simpson
Método numérico - regla de simpsonJoe Arroyo Suárez
 
Serie de fourier. Funciones periodicas, funciones pares e impares.
Serie de fourier. Funciones periodicas, funciones pares e impares.Serie de fourier. Funciones periodicas, funciones pares e impares.
Serie de fourier. Funciones periodicas, funciones pares e impares.Carlos Ramos Pérez
 
Metodos numericos 5
Metodos numericos 5Metodos numericos 5
Metodos numericos 5monica
 

Was ist angesagt? (20)

Algebra lineal problemas_resueltos
Algebra lineal problemas_resueltosAlgebra lineal problemas_resueltos
Algebra lineal problemas_resueltos
 
Tabla laplace
Tabla laplaceTabla laplace
Tabla laplace
 
Representación en series de Fourier
Representación en series de FourierRepresentación en series de Fourier
Representación en series de Fourier
 
Ejercicios jacobi
Ejercicios jacobiEjercicios jacobi
Ejercicios jacobi
 
Probabilidad de error en modulación digital
Probabilidad de error en modulación digitalProbabilidad de error en modulación digital
Probabilidad de error en modulación digital
 
Compiladores - Flex y Bison
Compiladores - Flex y BisonCompiladores - Flex y Bison
Compiladores - Flex y Bison
 
Prueba varianza
Prueba varianzaPrueba varianza
Prueba varianza
 
10 transformada fourier
10 transformada fourier10 transformada fourier
10 transformada fourier
 
Alg lineal unidad 3
Alg lineal unidad 3Alg lineal unidad 3
Alg lineal unidad 3
 
6 Codificación de fuente.ppsx
6 Codificación de fuente.ppsx6 Codificación de fuente.ppsx
6 Codificación de fuente.ppsx
 
Métodos numéricos- Problemario
Métodos numéricos- ProblemarioMétodos numéricos- Problemario
Métodos numéricos- Problemario
 
Simulacion - Algoritmo congruencial cuadratico
Simulacion - Algoritmo congruencial cuadraticoSimulacion - Algoritmo congruencial cuadratico
Simulacion - Algoritmo congruencial cuadratico
 
Funciones -Variable compleja
Funciones -Variable complejaFunciones -Variable compleja
Funciones -Variable compleja
 
Unidad 2 ensamblador
Unidad 2   ensambladorUnidad 2   ensamblador
Unidad 2 ensamblador
 
Metodo quicksort
Metodo quicksortMetodo quicksort
Metodo quicksort
 
Códigos bch, Reed Solomon, convolucionales
Códigos bch, Reed Solomon, convolucionalesCódigos bch, Reed Solomon, convolucionales
Códigos bch, Reed Solomon, convolucionales
 
Bus de datos, dirección y control
Bus de datos, dirección y controlBus de datos, dirección y control
Bus de datos, dirección y control
 
Método numérico - regla de simpson
Método numérico  - regla de simpsonMétodo numérico  - regla de simpson
Método numérico - regla de simpson
 
Serie de fourier. Funciones periodicas, funciones pares e impares.
Serie de fourier. Funciones periodicas, funciones pares e impares.Serie de fourier. Funciones periodicas, funciones pares e impares.
Serie de fourier. Funciones periodicas, funciones pares e impares.
 
Metodos numericos 5
Metodos numericos 5Metodos numericos 5
Metodos numericos 5
 

Ähnlich wie Traductor Pseudocógido to C

5. el-proyecto-de-investigación-aprender-a-investigar-icfes
5. el-proyecto-de-investigación-aprender-a-investigar-icfes5. el-proyecto-de-investigación-aprender-a-investigar-icfes
5. el-proyecto-de-investigación-aprender-a-investigar-icfesFROILAN ALFONSO SARAVIA CAMA
 
Estudio Fesabid: Prospectiva de una profesión en constante evolución
Estudio Fesabid: Prospectiva de una profesión en constante evoluciónEstudio Fesabid: Prospectiva de una profesión en constante evolución
Estudio Fesabid: Prospectiva de una profesión en constante evoluciónFESABID
 
manual-de-preparacin-para-olimpiadas-nacionales-de-mecatrnica
manual-de-preparacin-para-olimpiadas-nacionales-de-mecatrnicamanual-de-preparacin-para-olimpiadas-nacionales-de-mecatrnica
manual-de-preparacin-para-olimpiadas-nacionales-de-mecatrnicaKartel Balder
 
Apunts dintel ligencia_artificial
Apunts dintel ligencia_artificialApunts dintel ligencia_artificial
Apunts dintel ligencia_artificialAndreu Garcia
 
MANUAL DE LENGUAJE C
MANUAL DE LENGUAJE CMANUAL DE LENGUAJE C
MANUAL DE LENGUAJE Cclaudiocj7
 
Aprender a programar, con matlab
Aprender a programar, con matlabAprender a programar, con matlab
Aprender a programar, con matlabCarlos Avila
 
Serie aprender a_investigar,_módulo_3_recolección_de_la_información
Serie aprender a_investigar,_módulo_3_recolección_de_la_informaciónSerie aprender a_investigar,_módulo_3_recolección_de_la_información
Serie aprender a_investigar,_módulo_3_recolección_de_la_informaciónSistemadeEstudiosMed
 
Libro recolectar informacion
Libro  recolectar informacionLibro  recolectar informacion
Libro recolectar informacionmariajulianita
 
Programacion en Phyton desde ce..........................ro
Programacion en Phyton desde ce..........................roProgramacion en Phyton desde ce..........................ro
Programacion en Phyton desde ce..........................roMa Florencia Ferrari
 
Matlab adv esp
Matlab adv espMatlab adv esp
Matlab adv espLuis Maury
 
Bases de-datos
Bases de-datosBases de-datos
Bases de-datossabu47
 
Algoritmo de Reconocimiento de Objetos en Escenas Complejas para Aplicaciones...
Algoritmo de Reconocimiento de Objetos en Escenas Complejas para Aplicaciones...Algoritmo de Reconocimiento de Objetos en Escenas Complejas para Aplicaciones...
Algoritmo de Reconocimiento de Objetos en Escenas Complejas para Aplicaciones...CIMAT
 

Ähnlich wie Traductor Pseudocógido to C (20)

5. el-proyecto-de-investigación-aprender-a-investigar-icfes
5. el-proyecto-de-investigación-aprender-a-investigar-icfes5. el-proyecto-de-investigación-aprender-a-investigar-icfes
5. el-proyecto-de-investigación-aprender-a-investigar-icfes
 
Estudio Fesabid: Prospectiva de una profesión en constante evolución
Estudio Fesabid: Prospectiva de una profesión en constante evoluciónEstudio Fesabid: Prospectiva de una profesión en constante evolución
Estudio Fesabid: Prospectiva de una profesión en constante evolución
 
Proyecto (coordenadas polares)
Proyecto (coordenadas polares)Proyecto (coordenadas polares)
Proyecto (coordenadas polares)
 
manual-de-preparacin-para-olimpiadas-nacionales-de-mecatrnica
manual-de-preparacin-para-olimpiadas-nacionales-de-mecatrnicamanual-de-preparacin-para-olimpiadas-nacionales-de-mecatrnica
manual-de-preparacin-para-olimpiadas-nacionales-de-mecatrnica
 
Apunts dintel ligencia_artificial
Apunts dintel ligencia_artificialApunts dintel ligencia_artificial
Apunts dintel ligencia_artificial
 
Hefesto v2.1
Hefesto v2.1Hefesto v2.1
Hefesto v2.1
 
Proyecto de tesis
Proyecto de tesisProyecto de tesis
Proyecto de tesis
 
MANUAL DE LENGUAJE C
MANUAL DE LENGUAJE CMANUAL DE LENGUAJE C
MANUAL DE LENGUAJE C
 
Aa i modulo 5
Aa i modulo 5Aa i modulo 5
Aa i modulo 5
 
Datos con r
Datos con rDatos con r
Datos con r
 
Aprender a programar, con matlab
Aprender a programar, con matlabAprender a programar, con matlab
Aprender a programar, con matlab
 
Serie aprender a_investigar,_módulo_3_recolección_de_la_información
Serie aprender a_investigar,_módulo_3_recolección_de_la_informaciónSerie aprender a_investigar,_módulo_3_recolección_de_la_información
Serie aprender a_investigar,_módulo_3_recolección_de_la_información
 
Libro recolectar informacion
Libro  recolectar informacionLibro  recolectar informacion
Libro recolectar informacion
 
Aa i modulo 3
Aa i modulo 3Aa i modulo 3
Aa i modulo 3
 
Programacion en Phyton desde ce..........................ro
Programacion en Phyton desde ce..........................roProgramacion en Phyton desde ce..........................ro
Programacion en Phyton desde ce..........................ro
 
Matlab adv esp
Matlab adv espMatlab adv esp
Matlab adv esp
 
Bases de-datos
Bases de-datosBases de-datos
Bases de-datos
 
Bases de-datos
Bases de-datosBases de-datos
Bases de-datos
 
Algoritmo de Reconocimiento de Objetos en Escenas Complejas para Aplicaciones...
Algoritmo de Reconocimiento de Objetos en Escenas Complejas para Aplicaciones...Algoritmo de Reconocimiento de Objetos en Escenas Complejas para Aplicaciones...
Algoritmo de Reconocimiento de Objetos en Escenas Complejas para Aplicaciones...
 
Manual ADOS 2.pdf
Manual ADOS 2.pdfManual ADOS 2.pdf
Manual ADOS 2.pdf
 

Kürzlich hochgeladen

dokumen.tips_36274588-sistema-heui-eui.ppt
dokumen.tips_36274588-sistema-heui-eui.pptdokumen.tips_36274588-sistema-heui-eui.ppt
dokumen.tips_36274588-sistema-heui-eui.pptMiguelAtencio10
 
La era de la educación digital y sus desafios
La era de la educación digital y sus desafiosLa era de la educación digital y sus desafios
La era de la educación digital y sus desafiosFundación YOD YOD
 
GonzalezGonzalez_Karina_M1S3AI6... .pptx
GonzalezGonzalez_Karina_M1S3AI6... .pptxGonzalezGonzalez_Karina_M1S3AI6... .pptx
GonzalezGonzalez_Karina_M1S3AI6... .pptx241523733
 
Arenas Camacho-Practica tarea Sesión 12.pptx
Arenas Camacho-Practica tarea Sesión 12.pptxArenas Camacho-Practica tarea Sesión 12.pptx
Arenas Camacho-Practica tarea Sesión 12.pptxJOSEFERNANDOARENASCA
 
definicion segun autores de matemáticas educativa
definicion segun autores de matemáticas  educativadefinicion segun autores de matemáticas  educativa
definicion segun autores de matemáticas educativaAdrianaMartnez618894
 
Medidas de formas, coeficiente de asimetría y coeficiente de curtosis.pptx
Medidas de formas, coeficiente de asimetría y coeficiente de curtosis.pptxMedidas de formas, coeficiente de asimetría y coeficiente de curtosis.pptx
Medidas de formas, coeficiente de asimetría y coeficiente de curtosis.pptxaylincamaho
 
Crear un recurso multimedia. Maricela_Ponce_DomingoM1S3AI6-1.pptx
Crear un recurso multimedia. Maricela_Ponce_DomingoM1S3AI6-1.pptxCrear un recurso multimedia. Maricela_Ponce_DomingoM1S3AI6-1.pptx
Crear un recurso multimedia. Maricela_Ponce_DomingoM1S3AI6-1.pptxNombre Apellidos
 
El_Blog_como_herramienta_de_publicacion_y_consulta_de_investigacion.pptx
El_Blog_como_herramienta_de_publicacion_y_consulta_de_investigacion.pptxEl_Blog_como_herramienta_de_publicacion_y_consulta_de_investigacion.pptx
El_Blog_como_herramienta_de_publicacion_y_consulta_de_investigacion.pptxAlexander López
 
Hernandez_Hernandez_Practica web de la sesion 11.pptx
Hernandez_Hernandez_Practica web de la sesion 11.pptxHernandez_Hernandez_Practica web de la sesion 11.pptx
Hernandez_Hernandez_Practica web de la sesion 11.pptxJOSEMANUELHERNANDEZH11
 
Google-Meet-como-herramienta-para-realizar-reuniones-virtuales.pptx
Google-Meet-como-herramienta-para-realizar-reuniones-virtuales.pptxGoogle-Meet-como-herramienta-para-realizar-reuniones-virtuales.pptx
Google-Meet-como-herramienta-para-realizar-reuniones-virtuales.pptxAlexander López
 
El uso de las TIC's en la vida cotidiana.
El uso de las TIC's en la vida cotidiana.El uso de las TIC's en la vida cotidiana.
El uso de las TIC's en la vida cotidiana.241514949
 
El uso delas tic en la vida cotidiana MFEL
El uso delas tic en la vida cotidiana MFELEl uso delas tic en la vida cotidiana MFEL
El uso delas tic en la vida cotidiana MFELmaryfer27m
 
LAS_TIC_COMO_HERRAMIENTAS_EN_LA_INVESTIGACIÓN.pptx
LAS_TIC_COMO_HERRAMIENTAS_EN_LA_INVESTIGACIÓN.pptxLAS_TIC_COMO_HERRAMIENTAS_EN_LA_INVESTIGACIÓN.pptx
LAS_TIC_COMO_HERRAMIENTAS_EN_LA_INVESTIGACIÓN.pptxAlexander López
 
FloresMorales_Montserrath_M1S3AI6 (1).pptx
FloresMorales_Montserrath_M1S3AI6 (1).pptxFloresMorales_Montserrath_M1S3AI6 (1).pptx
FloresMorales_Montserrath_M1S3AI6 (1).pptx241522327
 
TEMA 2 PROTOCOLO DE EXTRACCION VEHICULAR.ppt
TEMA 2 PROTOCOLO DE EXTRACCION VEHICULAR.pptTEMA 2 PROTOCOLO DE EXTRACCION VEHICULAR.ppt
TEMA 2 PROTOCOLO DE EXTRACCION VEHICULAR.pptJavierHerrera662252
 
PARTES DE UN OSCILOSCOPIO ANALOGICO .pdf
PARTES DE UN OSCILOSCOPIO ANALOGICO .pdfPARTES DE UN OSCILOSCOPIO ANALOGICO .pdf
PARTES DE UN OSCILOSCOPIO ANALOGICO .pdfSergioMendoza354770
 
R1600G CAT Variables de cargadores en mina
R1600G CAT Variables de cargadores en minaR1600G CAT Variables de cargadores en mina
R1600G CAT Variables de cargadores en minaarkananubis
 
Presentación inteligencia artificial en la actualidad
Presentación inteligencia artificial en la actualidadPresentación inteligencia artificial en la actualidad
Presentación inteligencia artificial en la actualidadMiguelAngelVillanuev48
 
Plan Sarmiento - Netbook del GCBA 2019..
Plan Sarmiento - Netbook del GCBA 2019..Plan Sarmiento - Netbook del GCBA 2019..
Plan Sarmiento - Netbook del GCBA 2019..RobertoGumucio2
 
El uso de las tic en la vida ,lo importante que son
El uso de las tic en la vida ,lo importante  que sonEl uso de las tic en la vida ,lo importante  que son
El uso de las tic en la vida ,lo importante que son241514984
 

Kürzlich hochgeladen (20)

dokumen.tips_36274588-sistema-heui-eui.ppt
dokumen.tips_36274588-sistema-heui-eui.pptdokumen.tips_36274588-sistema-heui-eui.ppt
dokumen.tips_36274588-sistema-heui-eui.ppt
 
La era de la educación digital y sus desafios
La era de la educación digital y sus desafiosLa era de la educación digital y sus desafios
La era de la educación digital y sus desafios
 
GonzalezGonzalez_Karina_M1S3AI6... .pptx
GonzalezGonzalez_Karina_M1S3AI6... .pptxGonzalezGonzalez_Karina_M1S3AI6... .pptx
GonzalezGonzalez_Karina_M1S3AI6... .pptx
 
Arenas Camacho-Practica tarea Sesión 12.pptx
Arenas Camacho-Practica tarea Sesión 12.pptxArenas Camacho-Practica tarea Sesión 12.pptx
Arenas Camacho-Practica tarea Sesión 12.pptx
 
definicion segun autores de matemáticas educativa
definicion segun autores de matemáticas  educativadefinicion segun autores de matemáticas  educativa
definicion segun autores de matemáticas educativa
 
Medidas de formas, coeficiente de asimetría y coeficiente de curtosis.pptx
Medidas de formas, coeficiente de asimetría y coeficiente de curtosis.pptxMedidas de formas, coeficiente de asimetría y coeficiente de curtosis.pptx
Medidas de formas, coeficiente de asimetría y coeficiente de curtosis.pptx
 
Crear un recurso multimedia. Maricela_Ponce_DomingoM1S3AI6-1.pptx
Crear un recurso multimedia. Maricela_Ponce_DomingoM1S3AI6-1.pptxCrear un recurso multimedia. Maricela_Ponce_DomingoM1S3AI6-1.pptx
Crear un recurso multimedia. Maricela_Ponce_DomingoM1S3AI6-1.pptx
 
El_Blog_como_herramienta_de_publicacion_y_consulta_de_investigacion.pptx
El_Blog_como_herramienta_de_publicacion_y_consulta_de_investigacion.pptxEl_Blog_como_herramienta_de_publicacion_y_consulta_de_investigacion.pptx
El_Blog_como_herramienta_de_publicacion_y_consulta_de_investigacion.pptx
 
Hernandez_Hernandez_Practica web de la sesion 11.pptx
Hernandez_Hernandez_Practica web de la sesion 11.pptxHernandez_Hernandez_Practica web de la sesion 11.pptx
Hernandez_Hernandez_Practica web de la sesion 11.pptx
 
Google-Meet-como-herramienta-para-realizar-reuniones-virtuales.pptx
Google-Meet-como-herramienta-para-realizar-reuniones-virtuales.pptxGoogle-Meet-como-herramienta-para-realizar-reuniones-virtuales.pptx
Google-Meet-como-herramienta-para-realizar-reuniones-virtuales.pptx
 
El uso de las TIC's en la vida cotidiana.
El uso de las TIC's en la vida cotidiana.El uso de las TIC's en la vida cotidiana.
El uso de las TIC's en la vida cotidiana.
 
El uso delas tic en la vida cotidiana MFEL
El uso delas tic en la vida cotidiana MFELEl uso delas tic en la vida cotidiana MFEL
El uso delas tic en la vida cotidiana MFEL
 
LAS_TIC_COMO_HERRAMIENTAS_EN_LA_INVESTIGACIÓN.pptx
LAS_TIC_COMO_HERRAMIENTAS_EN_LA_INVESTIGACIÓN.pptxLAS_TIC_COMO_HERRAMIENTAS_EN_LA_INVESTIGACIÓN.pptx
LAS_TIC_COMO_HERRAMIENTAS_EN_LA_INVESTIGACIÓN.pptx
 
FloresMorales_Montserrath_M1S3AI6 (1).pptx
FloresMorales_Montserrath_M1S3AI6 (1).pptxFloresMorales_Montserrath_M1S3AI6 (1).pptx
FloresMorales_Montserrath_M1S3AI6 (1).pptx
 
TEMA 2 PROTOCOLO DE EXTRACCION VEHICULAR.ppt
TEMA 2 PROTOCOLO DE EXTRACCION VEHICULAR.pptTEMA 2 PROTOCOLO DE EXTRACCION VEHICULAR.ppt
TEMA 2 PROTOCOLO DE EXTRACCION VEHICULAR.ppt
 
PARTES DE UN OSCILOSCOPIO ANALOGICO .pdf
PARTES DE UN OSCILOSCOPIO ANALOGICO .pdfPARTES DE UN OSCILOSCOPIO ANALOGICO .pdf
PARTES DE UN OSCILOSCOPIO ANALOGICO .pdf
 
R1600G CAT Variables de cargadores en mina
R1600G CAT Variables de cargadores en minaR1600G CAT Variables de cargadores en mina
R1600G CAT Variables de cargadores en mina
 
Presentación inteligencia artificial en la actualidad
Presentación inteligencia artificial en la actualidadPresentación inteligencia artificial en la actualidad
Presentación inteligencia artificial en la actualidad
 
Plan Sarmiento - Netbook del GCBA 2019..
Plan Sarmiento - Netbook del GCBA 2019..Plan Sarmiento - Netbook del GCBA 2019..
Plan Sarmiento - Netbook del GCBA 2019..
 
El uso de las tic en la vida ,lo importante que son
El uso de las tic en la vida ,lo importante  que sonEl uso de las tic en la vida ,lo importante  que son
El uso de las tic en la vida ,lo importante que son
 

Traductor Pseudocógido to C

  • 1. UNIVERSIDAD DEL CONO SUR DE LAS AMÉRICAS TESIS DE GRADO DE LA CARRERA INGENIERÍA EN INFORMÁTICA TEMA: Creación de un Traductor Pseudocódigo a Lenguaje C (P2C) REALIZADO POR Francisco Javier Gray Benítez Tutor: Prof. Lic. Julio César Suárez Ortega Año 2008 Asunción – Paraguay
  • 2. DEDICATORIA A mis padres y hermanos…
  • 3. AGRADECIMIENTO Al Prof. Lic. Julio Suárez, quien más que un tutor, es un gran maestro y ha estado brindándome su apoyo desde incluso mucho antes de emprender está empresa. .
  • 4. RESUMEN El propósito del presente trabajo de investigación y desarrollo, fue la de crear un traductor de Lenguaje Pseudocódigo (español estructurado) a Lenguaje C. Para la construcción del traductor inicialmente se escribió una gramática libre de contexto, la cual sirvió de base para la construcción de la misma, a través de la utilización de metacompiladores fue generado un código equivalente en Lenguaje C, el cual posteriormente se compiló a fin de producir el traductor final. Al traductor se lo dotó de la capacidad de traducir (del Lenguaje Pseudocódigo al Lenguaje C) tipos de datos primitivos, estructuras condicionales, estructuras de repetición, entradas y salidas de datos, funciones propias del lenguaje y funciones definidas por el usuario, incluyendo las combinaciones posibles que de ellas puedan surgir, aportando de esta manera una herramienta que facilite el proceso de aprendizaje de las técnicas de algorítmicas a todas aquellas personas que se inician en el mundo de la programación.
  • 5. INDICE DE TABLAS Tabla 1 - Tabla de análisis sintáctico LL(1) ...........................................................47 Tabla 2 - Tabla de Análisis LR...............................................................................55 Tabla 3 - Reconocimiento de id*id+id....................................................................56 Tabla 4 - Tabla de análisis LR(1) para S$S'→ ......................................................68 Tabla 5 - Tabla de análisis LALR(1) para S$S'→ .................................................71 Tabla 6 – Operadores aritméticos admitidos por el traductor P2C......................... 148 Tabla 7 - Operadores relacionales admitidos por el traductor P2C........................ 148 Tabla 8 - Operadores lógicos admitidos por el traductor P2C ............................... 148 Tabla 9 – Operador de asignación admitido por el traductor P2C ......................... 149
  • 6. INDICE DE FIGURAS Figura 1 - Evolución de los lenguajes de programación..........................................21 Figura 2 - Esquema Básico de un Traductor...........................................................22 Figura 3 - Entrada y salida de un traductor.............................................................27 Figura 4 - Funcionamiento del enlazador ...............................................................27 Figura 5 - Etapas de un traductor............................................................................28 Figura 6 - Funcionamiento de un analizador léxico ................................................31 Figura 7 - Procesos de las dos fases primeras del análisis.......................................32 Figura 8 - Árbol de análisis sintáctico para E=m*c^2.............................................36 Figura 9 - PRIMEROS y SIGUIENTE...................................................................47 Figura 10 - Esquema Sinóptico de un analizador LR ..............................................54 Figura 11 - Conjunto LR(0) canónico para EE' ⋅→ ...............................................59 Figura 12 - Diagrama AFD para los prefijos viables...............................................60 Figura 13 - Grafo de transiciones para los goto de S$S'→ ....................................68 Figura 14 - AFD Simplificado para LALR(1).........................................................70 Figura 15 - Interacción de etapas con la Tabla de Símbolos....................................75 Figura 16 - Árbol y GDA para char x char → pointer(integer) ...............................81 Figura 17 - Representación de las expresiones de tipos ..........................................85 Figura 18 - Posición del generador de códigos en un traductor...............................88 Figura 19 - Árbol de análisis sintáctico para 4*5+6................................................90 Figura 20 - Árbol de análisis sintáctico con atributo heredados ..............................91 Figura 21 - Grafo de dependencias.........................................................................93 Figura 22 - Grafo de dependencias circular ............................................................95 Figura 23 - Momento en que se procede a traducir un programa P2C................... 162 Figura 24 - Salida de la traducción en lenguaje C por pantalla.............................. 162 Figura 25 - Pantalla de Tc-Lite, previa a la compilación....................................... 164 Figura 26 – Compilación del programa C generado.............................................. 165 Figura 27 – Compilación del programa C generado.............................................. 165 Figura 28 - Ejecución del programa compilado en C(parte 1)............................... 166 Figura 29 - Ejecución del programa compilado en C(parte 2)............................... 166
  • 7. INDICE CAPITULO I - INTRODUCCION ...................................................................... 8 CAPITULO II - MARCO TEORICO................................................................. 21 II.1 TRADUCTORES........................................................................................21 II.1.1. Origen .....................................................................................................21 II.1.2. ¿Qué es un traductor? ..............................................................................22 II.1.3. Tipología de los traductores según su utilización .....................................22 II.1.4. Fases de una traducción compilada..........................................................26 II.1.5. Etapas de la construcción de un traductor ................................................28 II.2 - ETAPA DE ANÁLISIS.............................................................................30 II.2.1. Análisis léxico (Scanner).........................................................................30 II.2.2. Análisis Sintáctico (Parser)......................................................................35 II.1.3. Análisis Semántico..................................................................................74 II.3 - ETAPA DE SINTESIS..............................................................................87 II.3.1 Generación de código ...............................................................................87 II.4 – DEFINICIÓN DIRIGIDA POR SINTAXIS..............................................88 II.4.1. Definición dirigida por sintaxis por medio de gramáticas con atributos....88 II.4.2. Grafos de dependencias ...........................................................................92 II.4.3. Esquemas de traducción...........................................................................96 CAPITULO III - MARCO METODOLOGICO................................................. 97 III.1. Descripción de la profundidad y Diseño de la tesis....................................97 III.2. Cómo se realizó la tesis.............................................................................98 III.3. Descripción de los instrumentos y procedimientos utilizados para la recolección y tratamiento de la información.......................................................99 III.4. Descripción de la muestra utilizada. ..........................................................99 III.5. Adecuación de los métodos a los objetivos de la tesis................................99 III.6. Tratamiento de los datos.......................................................................... 100 CAPITULO IV - RESULTADOS.................................................................... 101 IV.1. Código fuente de los Analizadores Sintáctico y Semántico del traductor P2C.................................................................................................................. 101 IV.2. Tabla de símbolos en Lenguaje C del traductor P2C................................ 135 IV.3. Código fuente del Analizador Léxico del traductor P2C.......................... 142 IV.4. Manual de programador del traductor P2C.............................................. 144 CAPITULO V – CONCLUSIONES ................................................................ 167 CAPITULO VI –RECOMENDACIONES....................................................... 170 BIBLIOGRAFIA............................................................................................. 171
  • 8. 8 CAPITULO I - INTRODUCCION Desde los comienzos de la informática, se ha buscado la manera mas práctica de interactuar con el ordenador, y hacer que éstos realicen las acciones tal y como se desea, con resultados en tiempo y forma. Con el pasar de las generaciones han ido surgiendo técnicas cada vez más sofisticadas, las cuales han dado como resultado toda una nueva rama de la informática, la de los traductores. Así en el año 1952 Grace Murray Hopper (1906-1992), dio origen al primer traductor, cuyo objetivo era el de traducir las instrucciones escritas en inglés al lenguaje máquina de un ordenador. Tal y como el trabajo de Murray Hopper, el propósito de este trabajo de investigación y desarrollo, fue crear un traductor de lenguajes que permita traducir programas escritos en LENGUAJE PSEUDOCODIGO a LENGUAJE C. Pensando en quienes se inician en la codificación de programas para computadoras, han surgido las siguientes cuestiones, ¿existen herramientas sencillas, que permitan realizar comprobaciones de la eficacia de los algoritmos?, ¿responden a estas necesidades los lenguajes actuales del mundo computacional? Exceptuando al Lenguaje Basic en sus versiones iniciales, actualmente no existen otras herramientas fáciles de acceder, como el desarrollado por medio de la presente tesis. Es pues esto, una respuesta a las necesidades de contar con un programa sencillo de utilizar, con un lenguaje de programación próximo al lenguaje natural, el cual simplifica la complejidad del estudio de la algorítmica.
  • 9. 9 El traductor desarrollado facilita la traducción de un lenguaje de uso académico (sin aplicación práctica a otros ámbitos) a un lenguaje de porte científico-comercial de amplia aplicación y utilización mundial, cual es el lenguaje C. El desarrollo fue realizado entre enero de 2006 y febrero de 2007, tiempo en que se ha trabajado en el diseño, programación, pruebas y puesta a punto. Para la realización de la tesis fueron fijados los siguientes objetivos: Objetivo General: Desarrollar un traductor de códigos teniendo como origen el Lenguaje Pseudocódigo y como destino el Lenguaje de C. Objetivos Específicos: 1. Diseñar las construcciones sintácticas de un lenguaje Pseudo-codificado, en español, de fácil utilización para el usuario. 2. Diseñar y desarrollar las analizadores léxicos y sintácticos para la verificación de los programas fuentes. 3. Diseñar y desarrollar un sistema de tipos de datos, para la implementación de un comprobador de tipos, que permita la verificación del programa fuente, en concordancia con la tipificación básica del lenguaje objeto. 4. Diseñar y desarrollar la comprobación estática de unicidad y flujo de control. 5. Diseñar y desarrollar el generador del código objeto. El presente material inicia con el primer capitulo desarrollando una introducción al tema, el segundo capitulo expone paso a paso el marco teórico, el tercer capitulo aborda la metodología seguida para la realización del presente
  • 10. 10 trabajo de tesis, en el capitulo cuatro se exponen los resultados obtenidos, en el quinto capitulo las conclusiones a las que se ha arribado por medio del presente trabajo de tesis, y finalmente, en el capitulo sexto se establecen unas series de recomendaciones para aquellas personas que deseen proseguir el tema abordado por la presente tesis.
  • 11. 11 GLOSARIO DE TÉRMINOS: A ADA: Lenguaje de programación estructurado y fuertemente tipado de forma estática que fue diseñado por Jean Ichbiah de CII Honeywell Bull por encargo del Departamento de Defensa de los Estados Unidos. Ad-hoc: Es una hipótesis creada expresamente para explicar un hecho que contradice una teoría. Alfabeto: También conocida como vocabulario; es un conjunto finito, no-vacío de símbolos (objetos atómicos o indivisibles), se denota con la letra griega Σ (Sigma). Ambigüedad: Es cuando una gramática genera para una misma cadena de entrada dos árboles de análisis distintos. Ámbito: Contexto en cual una declaración tiene validez. P.e., un programa, un trozo de programa. Árbol de derivación: Es una representación gráfica de como se deriva una forma sentencial a partir del símbolo no-terminal inicial. Árbol universal: Es aquel que tiene como raíz al axioma inicial y representa todas las derivaciones posibles a partir de cualquier forma sentencial. Asociatividad: Estable el orden de evaluación de las operaciones (asociatividad por izquierda, asociatividad por derecha). Atributo: Es cada una de las características o propiedades de una entidad o interrelación. Atributo Sintetizado: Es llamado atributo sintetizado a un atributo cuyo valor es determinado por los valores de sus atributos hijos, en un proceso de recorrido del árbol de derivación de abajo para arriba (Bottom-up). Atributo Heredado: Es aquel atributo cuyo valor es calculado a partir de los valores de sus hermanos y su padre. Autómata finito: También conocida como maquina de estado finito; es un modelo matemático de un sistema que recibe una cadena constituida por símbolos de un alfabeto y determina si esa cadena pertenece al lenguaje que el autómata reconoce. Autómata finito de pilas (AFP): Son maquinas abstractas construidas para reconocer los lenguajes regulares.
  • 12. 12 Autómata finito determinista (AFD): Es aquel autómata finito cuyo estado de llegada está unívocamente determinado por el estado inicial y el carácter leído por el autómata. Autómata finito no determinista (AFN): Es aquel que presenta cero, una o más transiciones por el mismo carácter del alfabeto Axioma inicial: Es el no-terminal inicial de la cadena de entrada. B Backpatching: También conocida como relleno de retroceso; es una técnica de generación de código, utiliza banderas para marcar las posiciones para valores desconocidos, para posteriormente completarlas (paching). Base de datos: También conocida como banco de datos; es un conjunto de datos que pertenecen al mismo contexto, almacenados sistemáticamente para su posterior uso. BNF: Acrónimo de Backus Naur Form (en español, Forma Normal de Backus); es una metasintaxis usada para expresar gramáticas independientes del contexto. Es una manera formal de describir lenguajes formales. Buffer: Es una ubicación de la memoria en una computadora, ó en un instrumento digital, reservada para el almacenamiento temporal de información digital mientras que está esperando ser procesada. C C: Es un lenguaje de programación creado en 1969 por Ken Thompson y Dennis M. Ritchie en los Laboratorios Bell como evolución del anterior lenguaje B, a su vez basado en BCPL. Al igual que B, es un lenguaje orientado a la implementación de Sistemas Operativos, concretamente Unix. C es apreciado por la eficiencia del código que produce y es el lenguaje de programación más popular para crear software de sistemas, aunque también se utiliza para crear aplicaciones. Cadena de entrada: Es el conjunto de caracteres proporcionados como entrada de un traductor para su análisis y traducción posterior si fuera el caso. Cierre: Conjunto de todas las posibles cadenas finitas sobre un alfabeto, se denota como Σ*. Clase: También conocida como clausura ó Clausura de Kleene; son declaraciones o abstracciones de objetos, lo que significa, que una clase es la
  • 13. 13 definición de un objeto. Cuando se programa un objeto y se definen sus características y funcionalidades, realmente se programa una clase. Código de máquina: Es una codificación de programas en sistema binario que es el único que puede ser directamente ejecutado por una computadora. Comando: Del inglés command; es una instrucción o mandato que el usuario proporciona al sistema. Compilador: Es un programa informático que traduce un programa escrito en un lenguaje de programación a otro lenguaje de programación, generando un programa equivalente que la máquina será capaz de interpretar. Condición de seguridad: Es una herramienta de la se dota a los lenguajes a fin de realizar una evaluación previa a la ejecución de una aplicación, la cual terminará ejecutándose, si y solo si satisface la condición de seguridad. Constructor de tipos: Método o función propia de un lenguaje computacional, tiene como tarea principal la definición. Contexto: Son las circunstancias bajo las cuales un dispositivo ó programa está siendo utilizado. Corrutina: Es aquella porción de código que funciona subordinada a otra. Costos: Es el tiempo en el cual se incurre durante un proceso dado. P.e.: Costos de CPU. Costos de E/S. CPU: Acrónimo de Central Processing Unit; es la unidad central de procesamiento, ó simplemente, el procesador. En una computadora digital, es el componente que interpreta las instrucciones y procesa los datos contenidos en los programas. Criterios de poda: Se define como todo aquél criterio que permite saber, dada una solución parcial, que no va a ser posible obtener a partir de ellas soluciones válidas. D Derivación: Secuencia de sustituciones de no-terminales que, partiendo de un símbolo inicial (denotado con S), produce un resultado (denotado con ω). Dirección de memoria: Es una colección de casilla ó celdas que almacena datos e instrucciones, identificadas unívocamente por una serie de números.
  • 14. 14 DOS: Acrónimo de Disk Operating System (Sistema Operativo de Disco), creado originalmente para las computadoras IBM PC, las cuales utilizaban procesadores 8086/8088 de 16 bits. E E/S: Acrónimo de entrada/salida, del inglés input/output (I/O); es el conjunto de dispositivos físicos utilizados por el sistema para comunicarse unas con otras, como para comunicar al sistema con las personas. Encapsulamiento: Se denomina encapsulamiento al ocultamiento del estado, es decir, de los datos miembros de un objeto, de manera que sólo se puede cambiar mediante las operaciones definidas para ese objeto. Enlazador: Del inglés, linker; programa que a partir de ficheros objetos generados en los primeros procesos de traducción, quita todos los recursos no precisados y enlaza el código objeto con las bibliotecas, para finalmente producir un código ejecutable ó biblioteca. EOF: Acrónimo de end-of-file; es un indicador o marca de que no hay mas información que recuperar de una fuente de datos. Épsilon (E ε): Es la quinta letra del alfabeto griego. En el sistema de numeración griega tiene un valor de 5. En matemáticas, ε suele designar a pequeñas cantidades, o cantidades que tienden hacia cero, en particular en el estudio de los límites o de la continuidad. Estructura de datos: Conjunto de datos en que sus componentes están relacionados entre sí de una forma particular y sobre los que se pueden realizar ciertas operaciones según sea el tipo de relación que hay entre ellos. F Fichero: También conocido como archivo; es un conjunto de información que se almacena en algún medio de escritura que permita ser leído o accedido por una computadora. Fichero Ejecutable: También conocido como archivo ejecutable; es aquel cuyo contenido se interpreta por el ordenador como un programa. Fichero Fuente: También conocido como archivo fuente; es un conjunto de códigos escritos en texto simple, capaz de ser leído por cualquier editor de texto, en el, están dispuestos todas las tareas que deberá realizar la computadora.
  • 15. 15 Fichero Objeto: También conocido como archivo objeto; contiene al conjunto de caracteres en código de maquina o bytecode, resultado de la compilación del fichero fuente. G Gramática: Ciencia que estudia los elementos de una lengua y sus combinaciones. H Herencia: Es uno de los mecanismos de la programación orientada a objetos, por medio del cual una clase se deriva de otra, llamada entonces superclase, de manera que extiende su funcionalidad. I Instrucciones: Conjunto de reglas o advertencias necesarias para ejecutar una tarea, un trabajo, un juego, etc. Inteligencia Artificial: Ciencia que intenta la creación de programas para máquinas que imiten el comportamiento y la comprensión humana. Interpretes: Es un programa capaz de analizar y ejecutar otros programas escritos en un lenguaje de alto nivel, realizando la traducción a medida que sea necesario, típicamente, instrucción por instrucción, y normalmente no guardan el resultado de dicha traducción, por lo que la ejecución de programas en lenguajes interpretados suelen ser bastante lentos. Invocación: Se denomina invocación al acto de solicitar la ejecución de un programa, rutina o subrutina. J Java: 1- Del Indonesio Jawa; es la isla más poblada del archipiélago indonesio, donde se sitúa la capital, Yakarta, muy conocida también por su café. 2- Tecnología desarrollada por Sun Microsystems para aplicaciones de software independientes de la plataforma (lleva el nombre de Java en honor al café producido en dicha Isla, de ahí que el símbolo de dicho programa sea una taza de café).
  • 16. 16 L Lenguaje: Es cualquier conjunto de cadenas finitas sobre un alfabeto. Lenguaje de alto nivel: Es aquel lenguaje de programación en el que las instrucciones enviadas al ordenador son similares al lenguaje humano. Lenguaje de maquina: Es un lenguaje compuesto por un conjunto de instrucciones que determinan acciones a ser tomadas por la máquina. Un programa de computadora consiste en una cadena de estas instrucciones de lenguaje de máquina (más los datos). Lenguaje ensamblador: Lenguaje de programación que está a un paso del lenguaje de máquina, por ende los programadores deben estar familiarizados con la arquitectura del computador para el cual están programando, siendo los programas en lenguaje ensamblador no documentados, y en consecuencia difíciles de mantener, como el lenguaje ensamblador es dependiente del hardware; hay un lenguaje ensamblador diferente para cada serie de CPU. Lenguaje Natural: El lenguaje natural es el lenguaje hablado y/o escrito por los seres humanos para propósitos generales de comunicación. Lenguaje Orientado a Objetos: Es aquel lenguaje de programación capaz de soportar Programación Orientada a Objetos. Librería: También conocida como biblioteca; conjunto de procedimientos y funciones (subprogramas) agrupadas en un fichero con el fin de que puedan aprovecharlas otros programas. Existen dos tipos de librerías; 1- Estáticas (de enlace estático) y, 2- Compartidas (de enlace dinámico). M Macros: Grupo de comandos de una aplicación, organizados según un determinado juego de instrucciones y cuya ejecución puede ser pedida de una sola vez para realizar una función deseada. Memoria: Del inglés, Memory; es el espacio de trabajo del computador (físicamente suelen ser una colección de chips RAM), determina el tamaño y el número de programas que pueden ejecutarse al mismo tiempo, así como también la cantidad de datos que puede procesarse en cada instante. Módulo: Es un componente de un programa computacional, el cual de las varias tareas que un programa deberá realizar para cumplir su función ú objetivo, el módulo realizará una o varías de estas tareas.
  • 17. 17 N No Terminales: Es aquel símbolo que en una derivación siempre es reemplazada demás, pues no termina en una derivación, algunos autores tal y como es el caso de Kenneth C. Louden, lo llaman como estructuras. Nodos: Es una estructura ó registro que dispone de varios campos, y al menos uno de ellos deberá ser un puntero a otro nodo, es utilizada para la realización de lista enlazada, árbol ó grafo. O Operadores: Son expresiones que permiten manipular los datos que se les pasa, cada uno de los datos que se pasa a un operador se llama operando, y según el número de operandos de que disponga un operador estaremos hablando de un operador unario (un operando), binario (dos operandos), ternario (tres operandos), etc. P Palabras reservadas: Son palabras propias de un lenguaje de programación, las mismas no pueden ser utilizadas como nombre de identificadores. Parámetros: También conocida como argumento, es una información que determina el funcionamiento de un programa informático. Los parámetros pueden tener valores de todo tipo. Por ejemplo: números, textos, expresiones o incluso el nombre de un archivo. Pasadas: Es el acto de leer el código fuente por parte de un traductor. PASCAL: Lenguaje de programación desarrollado por el Profesor Suizo Niklaus Wirth a finales de los años 60, bajo el objetivo crear un lenguaje que facilitara el aprendizaje de la programación a sus alumnos, sin embargo, con el tiempo este se convirtió en una herramienta para desarrollo de aplicaciones de todo tipo. PERL: Acrónimo de Practical Extracting and Reporting Lenguaje; es un Lenguaje de programación de amplia difusión usado para web mediante la programación de CGI. Muy usado para extraer información de archivos de texto y para la generación de informes a partir de ficheros. Pila: Es un tipo de estructura de datos en el que el último dato en llegar es el primero en salir.
  • 18. 18 PL/I: Acrónimo de Programming Language 1 (Lenguaje de Programación 1); fue propuesto por IBM hacia 1970 para responder a las necesidades de las aplicaciones científicas y comerciales, disponible en las novedosas plataformas de utilidad general IBM 360 y más adelante IBM 370. Polimorfismos: Es la capacidad que tienen objetos de diferentes clases de responder al mismo mensaje o evento. Portabilidad: Se define como la dependencia del software respecto a la plataforma del mismo, y es inversamente proporcional a la dependencia de plataforma. A menor dependencia, mayor portabilidad. Precedencia: Especifica la prioridad de cada operador respecto a otro. Programa: Secuencia de instrucciones que realizan una tarea específica. Programa fuente: Código del programa original, el cual es almacenada en un fichero, conocido como fichero o archivo fuente. Programa objeto: Un programa a nivel de lenguaje máquina que resulta de la compilación de un programa fuente. Programación: Es la implementación de un algoritmo en un determinado lenguaje de programación, conformando un programa. Programación Orientada a Objetos (POO): Es un paradigma de programación que usa objetos y las interacciones entre estas para diseñar y desarrollar aplicaciones, se basa en varias técnicas, como la herencia, modularidad, polimorfismo y encapsulamiento. Popularizado recién a principio de la década de los 1990, en la actualidad cuenta con varios lenguajes soportan la orientación a objetos. R Recursividad: Propiedad de algunos lenguajes de programación de permitir que un programa solicite su propia ejecución en el curso de su ejecución. Rutina: Es un conjunto de código, escrito para un propósito particular, que al ser llamado dentro de un programa el código principal de éste se detenga y se dirija al código de la rutina invocada. S Shell: Es un programa informático que actúa como Interfaz entre el usuario y el sistema operativo mediante una ventana que espera comandos textuales
  • 19. 19 ingresados por el usuario en el teclado, los interpreta y los entrega al sistema operativo para su ejecución. Líneas de comandos, Intérprete de mandatos, Intérprete de línea de mandatos, Intérprete de comandos, Terminal, Consola, Shell ó su acrónimo en inglés CLI por Command line interface, son formas comunes de llamarlo. Símbolos inaccesibles: No terminales a los que no se puede llegar por medio de derivación alguna. Símbolos muertos: No terminales incapaces de generar una cadena de terminales, a través de derivaciones sucesivas. Sintaxis: Son las reglas que rigen la formulación de las instrucciones en un programa de computación, en base a una gramática dada. Sistema Operativo: Es un programa o conjunto de programas destinados a permitir una gestión eficaz de recursos, éste empieza a trabar cuando se enciende el computador, es el encargado de gestionar el hardware de la maquina desde los niveles más básicos, permitiendo interacción con el usuario. Sobrecarga: Es la posibilidad de tener dos o más funciones (sobrecarga de funciones) y/u operadores (sobrecarga de operadores) con el mismo nombre pero brindando funcionalidades distintas, el compilador utilizará una ú otra dependiendo de los parámetros enviados. T Terminal: Son símbolos del alfabeto en los cuales termina una derivación. Tiempo de ejecución: Del inglés Runtime; es el intervalo de tiempo en el que un programa de informático se ejecuta en un sistema operativo. Tira nula: Secuencia de vacía de símbolos. U Unix: Registrado oficialmente como UNIX®, es un sistema operativo portable, multitarea y multiusuario; desarrollado, en principio, en 1969 por un grupo de empleados de los laboratorios Bell de AT&T, entre los que figuran Ken Thompson y Dennis Ritchie. Según Dennis Ritchie, «después de treinta años de su creación, UNIX sigue siendo un fenómeno»
  • 20. 20 V Variables: Son estructuras de datos que, como su nombre indica, pueden cambiar de contenido a lo largo de la ejecución de un programa. Una variable corresponde a un área reservada en la memoria principal del computador. W Windows: Microsoft Windows, conocido simplemente como Windows, es un sistema operativo con interfaz gráfica para computadoras personales cuyo propietario es la empresa Microsoft.
  • 21. 21 CAPITULO II - MARCO TEORICO II.1 TRADUCTORES II.1.1. Origen El estudio de los traductores se remonta a los orígenes de la informática misma, ya en 1952 Grace Murray Hopper (1906-1992), en vista a la necesidad que tenía creo un traductor Inglés-Lenguaje Maquina, posterior a esto y hasta nuestros días la ciencia-arte de crear lenguajes ha evolucionado notablemente, empezando por los ensambladores que fueran creados por cada uno de los fabricantes, con sus propias especificaciones para cada procesador, hasta los lenguajes de alto nivel y llegando a los lenguajes orientados a objetos los cuales trabajan a base de acciones o comportamiento acorde a los distintos impulsos externos que reciben. Figura 1 - Evolución de los Lenguajes de Programación. Fuente: CUEVA 1998:10 Al principio, el desarrollo de un traductor era considerado una tarea difícil y de alto consumo de tiempo, por lo que eran escasos los centros e institutos de investigación que dedicaban esfuerzos en su construcción, (GALVEZ
  • 22. 22 et al., 2005), mencionan que, para la implementación del primer compilador FORTRAN (Formula Translator) se necesito lo equivalente a 18 años de trabajo individual. Hoy día gracias a las técnicas avanzadas de desarrollo de traductores y lenguajes, el desarrollo de un traductor puede ser realizado por un pequeño grupo de investigación ó como trabajo de fin de carrera, tal cual el presente estudio, en la década de 1950, tiempo en que se desarrollo el primer compilador FORTRAN esto era toda una utopía. II.1.2. ¿Qué es un traductor? Un traductor se define como un programa que traduce un programa escrito en un lenguaje (lenguaje fuente) a otro programa o lenguaje (lenguaje destino), y como parte de este proceso el traductor debe emitir mensajes de errores al usuario si los hubiere. II.1.3. Tipología de los traductores según su utilización Traductores de idioma: Son programas que se encargan de realizar traducciones de un idioma a otro, este tipo de traductores deben sortear un sin fin de dificultades, como ser: 1. Significado de las palabras según el contexto. Programa Destino Mensajes de Error Figura 2 - Esquema Básico de un Traductor Traductor Programa Fuente Fuente: Suárez 2006:1
  • 23. 23 2. Necesidad de un alto nivel de Inteligencia Artificial: Debido al hecho de que el nivel de inteligencia artificial sigue siendo pobre para lo que se precisa a nivel de traductores de idiomas, esto se presenta como una dificultad complicada de superar en la traducción de frases hechas, al respecto cabe mencionar una anécdota expuesta por (GALVEZ et al., 2005), donde mencionan que durante la guerra fría, en un intento por realizar traducciones en forma automática del ruso al inglés y viceversa, se puso a prueba un prototipo introduciendo un texto en inglés: “El espíritu es fuerte, pero la carne es débil” cuya traducción al ruso se paso de nuevo al inglés para ver si coincidía con el original, y para sorpresa de los desarrolladores el resultado fue: “El vino está bueno, pero la carne está podrida”, esto es debido a que en inglés spirit, posee más de un significado, según se aplique puede significar espíritu ó alcohol. • Intérpretes: Los intérpretes tienen una estructura similar al de los traductores compiladores, en funcionamiento, varían sustancialmente, debido a que los intérpretes, reciben como entrada un programa fuente en lenguaje formal y como salida realiza la ejecución del programa, esto es debido a que el programa fuente es reconocida y ejecutada al mismo tiempo, algunos ejemplos de lenguajes intérpretes son; SNOBOL (StriNg Oriented SimbOlyc Language), LISP (LISt Processing), algunas versiones del BASIC (Beginner´s All-propuse Symbolic Instruction Code). • Preprocesadores: Los preprocesadotes se utilizan con la finalidad de modificar el programa fuente, al respecto (AHO et al., 1990), habla sobre las distintas funciones que estas pueden realizar, las cuales se describen a continuación: i. Procesamiento de macros: Permite al programador-usuario definir macros, las cuales son abreviaturas de construcciones muchos más grandes.
  • 24. 24 ii. Inclusión de ficheros: Inserta ficheros completos dentro del programa en cual aparece en la cabecera, en la misma línea en la que aparece se insertará su contenido antes de que el programa fuente sea compilado, el lenguaje C es un lenguaje que típicamente utiliza este tipo de preprocesamiento, por medio de la cláusula #include <nombre_de_archivo>. iii. Procesadores “racionales”: Utilizados con la finalidad de enriquecer a los lenguajes antiguos con nuevas funcionalidades, como los controles de flujos, nuevos tipos de datos, interfaces con dispositivos de hardware, entre otros. iv. Extensiones a Lenguajes: Tratan de crear posibilidades al lenguaje al cual son insertados, por medio de macros, en este aspecto puede considerarse el lenguaje Equel STONEBRAKER et al. (1976, citado de AHO, 1990), el cual es un lenguaje de consultas a base de datos integrado al lenguaje C. Las proposiciones que inician con ##, son tomadas por el preprocesador, sin que ellas pasen por el lenguaje C y las traduce directamente a llamadas a la base de datos. • Intérpretes de comandos: Un intérprete de comandos traduce sentencias1 simple s a invocaciones de programas, ejemplos de estos son las shell de los sistemas operativos de la familia UNIX, o la línea de comando MS-DOS de los sistemas MS-Windows. • Ensambladores: En los albores de la informática, tiempos en los que todos los programas eran escritos en lenguaje de maquina, aparecieron como el primer gran salto hacia los lenguajes de altos niveles como los que hoy poseemos. 1 Sentencia(s): Secuencia de expresiones (terminales) que especifica una o varias operaciones.
  • 25. 25 Un ensamblador es un traductor sencillo, el lenguaje fuente tiene una estructura, en la que una sentencia se traduce de manera única a una instrucción en código de maquina, por lo que la correspondencia ensamblador- código de maquina es una a una. • Traductores fuente-fuente: Permite obtener mayor portabilidad del código fuente escrito, debido a que traduce códigos fuentes escritos para un lenguaje a otro. El presente trabajo de investigación y desarrollo tiene como fin desarrollar un traductor fuente-fuente. En estos tipos de traductores, es habitual que los programas fuentes resultantes requieran de retoques manuales debidos a diversos motivos (Gálvez Rojas et al., 2005): i. En situaciones en las que el lenguaje destino carece de importantes características, de las que dispone el lenguaje origen, por ejemplo una conversión Java a C, podría precisar retoques, debido a que el lenguaje C no dispone del recolector de basuras (Garbage Collector). ii. En situaciones en las que la traducción no es inteligente y los programas destinos son ineficientes. Un ejemplo de traductor fuente-fuente podría ser como ya se ha mencionado anteriormente el traductor realizado por Murray Hopper en 1952, así como el POLARIS el cual recibe como entrada programas escritos en FORTRAN77 y los traduce en programas eficientes a fin de ser utilizados en computación paralela, cuya salida nuevamente es FORTRAN77, aunque con directivas explicitas que provee paralelismo. • traductor cruzado: Es aquel que genera un código objeto ejecutable para una maquina distinta a la que se escribe y/o realiza la compilación.
  • 26. 26 • Traductores de circuitos de silicios: Posee un lenguaje fuente similar o idéntico que un lenguaje de programación convencional, marcando diferencia, en el hecho de que las variables no representan ubicaciones en memoria, sino señales lógicas (0 y 1) o grupos de señales de conmutación, produciendo como salida el diseño del circuito impreso mediante técnicas fotográficas. • Conversores de formatos: Son programas que aplican las técnicas de traductores de lenguajes para convertir ficheros escritos en un formato dado a otro. • Reconocimiento del habla: Los sistemas de reconocimientos del habla realizan un análisis detallado del sonido, por medio de las técnicas de traductores. II.1.4. Fases de una traducción compilada Todo traductor tipo compilador se dota de tres fases básicas, las cuales son traducción, enlace (enlazador) y carga ó ejecución. Traducción, Generalmente un traductor tipo compilador no produce un programa o fichero ejecutable, en su lugar emite como salida programa ó fichero objeto, que contiene informaciones relativas al código, así como una tabla de símbolos con las variables y estructuras de tipos utilizadas en el programa fuente.
  • 27. 27 Enlace, El enlazador se encarga de enlazar todos los componentes generados en la etapa de traducción (referencias cruzadas), generar la estructura de bloque de memoria para almacenar las variables en tiempo de ejecución y el programa ó fichero ejecutable, incorporando rutinas adicionales precedentes de las librerías, como las instrucciones de entrada/salida, funciones matemáticas y de sistemas, entre otros. Figura 4 - Funcionamiento del enlazador Fuente: Elaboración propia, basado en Vélez 2003:4. Cargador, Esta fase del compilador tiene como tarea, la de cargar el fichero ejecutable (.exe), en otras palabras, lo que hace el cargador es, tomar el Traductor Entrada: Fichero.prg Salida: Fichero.obj Figura 3 - Entrada y salida de un traductor Fuente: Elaboración propia, basado en Suárez 2006:3.
  • 28. 28 código de maquina relocalizable producida por el enlazador, modificar las direcciones de memorias relocalizables y ubicar las instrucciones y los datos modificados en las posiciones apropiadas de la memoria. Dependiendo del microprocesador, la estructura de memoria difiere, aunque lo más usual es que el fichero .exe, esté divido en segmentos de códigos, datos, pilas, etc., al comienzo del módulo de carga se le asigna la dirección de memoria relativa 0, siendo las demás referencias dentro del modulo apuntadas al comienzo del mismo. Teniendo todas las referencias a memoria en código relocalizable, la tarea de carga se convierte en una tarea sencilla, pues sabiendo la dirección de memoria relativa en la que será cargada el modulo (dirección x), bastará sumar dicha dirección (x) a medida que se vaya produciendo la carga del módulo en memoria, a fin de obtener las posiciones de memorias relativas de cada componente. II.1.5. Etapas de la construcción de un traductor El traductor puede ser dividido inicialmente en dos grandes etapas, la etapa inicial ó de análisis y la etapa final ó de síntesis, la primera se refiere a todo aquello que tenga que ver con el programa fuente e independiente del programa objeto, en ella se incluyen los análisis léxicos, sintácticos y semánticos, la construcción de tablas de símbolos, la generación de código intermedio y en cierta medida la optimización de código, siendo la etapa final todo lo concerniente a las partes del traductor que tienen directa relación con la maquina para la que se construye, siendo completamente independientes del lenguaje fuente, en está etapa se incluyen todo lo referente a la generación y optimización de código, manejo de errores y operaciones con las tablas de símbolos. Figura 5 - Etapas de un traductor Fuente: Gálvez et. al 2005:12 AnálisisProg. Fuente Errores en el programa fuente Síntesis Prog. Destino Errores en la generación de código
  • 29. 29 El traductor como parte importante de su función, debe leer el programa fuente, existen situaciones en las cuales, el leer una sola vez el programa fuente, no resulta ser suficiente, como en el caso de la recursividad, suponiendo que se tiene una función X, que hace referencia a una función Y, y viceversa, en este caso hipotético, al leer la función X no se sabe si la función Y está declarada ó si es que se ha hecho de forma correcta, sin embargo al leer la función Y ya se sabrá que de antemano que X está y que ya ha cumplido con todos los parámetros necesarios para ser utilizado, siendo la única alternativa de solución para la invocación de la función Y desde X, el volver a pasar una vez más(releer el código) a fin de rellenar los datos faltantes y corregir los errores, a este proceso se lo conoce con el nombre de pasadas. Lo deseable es tener la menor cantidad de pasadas, a fin de reducir los costos (tiempos) de e/s, para ello se han creado bastas alternativas, como ser el lenguaje PL/I, solo permite utilizar variables previamente declaradas y no se puede realizar representaciones de código objeto para las construcciones de las que se desconozcan los tipos de variables involucradas, del mismo modo otros lenguajes permiten la utilización de construcciones GOTO a fin de saltar adelante en el código para verificar la existencia de las referencias o variables utilizadas, otros lenguajes utilizan la técnica backpatching (relleno de retroceso), dicha técnica consiste que al encontrar un salto hacia delante (GOTO destino), generar la estructura de una instrucción, con el código de operación para el GOTO y dejar espacios en blancos para la dirección, guardando todas las instrucciones con espacios en blanco para la dirección destino en una lista asociada con la entrada de destino en la tabla de símbolos, los cuales son rellenados una vez que se encuentra la instrucción (destino: MOV algo, R1).
  • 30. 30 II.2 - ETAPA DE ANÁLISIS La etapa de análisis está formada por los siguientes tres componentes: 1. Análisis léxico ó scanner, se encarga de recorrer el programa fuente, identificar y agrupar los caracteres en tokens. 2. Análisis sintáctico ó parser, agrupa los componentes léxicos de forma jerárquica, construyendo frases gramaticales los cuales son utilizados por el compilador en la fase de síntesis. 3. Análisis semántico, verifica el programa fuente a fin de asegurarse que no haya errores de significado y realiza la comprobación de tipos. II.2.1. Análisis léxico (Scanner) El análisis léxico es la primera fase de todo traductor, su labor principal consiste en leer una cadena de entrada (lexema) del programa fuente a analizar de izquierda a derecha y agruparlos en componentes léxicos (tokens), los cuales poseen un significado propio, para realizar dicho proceso debe existir una regla o un conjunto de reglas previamente predefinidos (patrón). Cada lexema o cadena del programa fuente es comparada con el conjunto de patrones (expresiones regulares). Ejemplo de una cadena de entrada: E = m * c ^ 2 De esta forma los componentes léxicos se agruparían de la siguiente manera: 1. El identificador ‘E’. 2. El símbolo de asignación ‘=’. 3. El identificador ‘m’. 4. El signo de multiplicación ‘*’. 5. El identificador ‘c’. 6. El signo exponencial ‘^’. 7. El número 2.
  • 31. 31 Figura 6 - Funcionamiento de un analizador léxico E = m * c ^ 2 <ID> <ASIG> <ID> <*> <ID> <^> <NUM>.2 Fuente: Elaboración propia, basado en Gálvez et. al 2005:18 Otras tareas desarrolladas por el analizador léxico incluyen: • Procesar directivas del traductor. • Introducir información preliminar en la tabla de símbolos. • Eliminar comentarios, espacios en blancos, tabulaciones, retorno de carro y todo aquello que no tenga significado según las reglas del lenguaje. • Sustituir macros. • Llevar la cuenta de números de líneas, a fin de que al momento de un error pueda informar en que línea se ha producido y facilitar la recuperación. • Informar de errores léxicos encontrados, como por ejemplo cuando se encuentra un ‘;’ y este no forma parte del lenguaje. • Cuando algunos componentes léxicos requieren algo más que una identificación, el analizador léxico primero identifica el componente léxico, y luego lo evalúa. • Si el lenguaje a analizar es complejo en su sintaxis, el analizador léxico realiza primeramente una fase de examen, y posteriormente realiza la fase de análisis propiamente dicha. E m c Punteros a tabla de Símbolos. Asociación de valor al terminal NUM Tabla de Símbolos
  • 32. 32 Los componentes léxicos más habituales suelen ser; las palabras reservadas, identificadores, operadores, constantes, símbolos de puntuación y caracteres especiales. El analizador léxico aparte del token, también debe devolver información adicional sobre el mismo, como ser el valor, si se tratará de una constante ó la identificación si fuera un operador. Toda información adicional necesaria es enviada a través de atributos, asociados a los símbolos de la gramática que define la sintaxis del lenguaje fuente. El analizador léxico reconoce palabras conforme a una gramática regular en el cual se define un conjunto de caracteres con los que el mismo puede trabajar, conocidos con el nombre de alfabeto, así el analizador léxico se encuentra sometido al analizador sintáctico, actuando como una subrutina o corrutina del mismo. Figura 7 - Procesos de las dos fases primeras del análisis. Donde: N: Conjunto de No Terminales. T: Conjunto de Terminales. P: Reglas de producción de la gramática. S: Axioma o símbolo inicial. Fuente: Gálvez et. al 2005:24 Analizador Léxico (scanner) Secuencia de caracteres. Analizador sintáctico (parser) Secuencia de terminales. Árbol sintáctico. El control viene dado por una gramática (N, T, P, S)
  • 33. 33 La necesidad de construir un analizador Léxico: 1. Simplicidad del diseño: Facilita el diseño y el mantenimiento primeramente del analizador léxico y a la larga del traductor. El límite entre ambos analizadores es una línea muy delgada, se podría considerar que todo aquello que se vaya reconociendo carácter a carácter sin poseer un significado semántico por si solo forma parte del analizador léxico, por ejemplo: Analizador léxico Analizador Sintáctico 0. A:=0; 123 Suma+=123 printf printf(“Hola mundo”); 2. Eficiencia: Debido a que gran parte del tiempo de la traducción se invierte en la etapa de reconocimiento de componentes léxicos, el diseño e implementación de un analizador léxico independiente mejora notablemente el funcionamiento de un traductor, pues se le puede aplicar técnicas de manejo de buffer para lectura de caracteres de entrada y procesamiento de patrones. 3. Portabilidad: Al poseer un analizador léxico independiente se puede limitar los problemas de compatibilidad de dispositivos, peculiaridades del alfabeto de partida y juego de caracteres bases, lo cual hace que el resto del sistema sea ajeno a estos problemas y como consecuencia facilita la portabilidad entre plataformas. 4. Facilidad en reconocimiento de patrones complejos: Una razón más por la que se suele realizar el análisis léxico por separado es el reconocimiento de patrones complejos. Por ejemplo si se
  • 34. 34 requiriere realizar un proceso de prebúsqueda2 , la complejidad del diseño y de la implementación se dispararía por lo que tratarlo dentro del analizador sintáctico sería algo prácticamente imposible. Se podría considerar que la implementación de un analizador léxico posee la estructura: (Expresión regular1) {Acción a ejecutar1} (Expresión regular2) {Acción a ejecutar2} . . . . . . (Expresión regularn) {Acción a ejecutarn} Donde cada acción a ejecutar describe que acción deberá realizar el analizador léxico cuando una cadena de entrada coincida con la expresión regular definida, finalizando casi siempre con la devolución de una categoría léxica asociada, siendo los siguientes conceptos de fundamental importancia al momento de estudiar la construcción de analizadores léxicos. • Patrón: Es una expresión regular. • Token: Categoría léxica, es directamente asociada a cada patrón, cada token es convertida en número ó identificador único en todo el traductor, siendo en ocasiones asociada al mismo, valores adicionales necesarios para las fases posteriores. Es lo que el terminal para el analizador sintáctico. • Lexema: Es una secuencia de caracteres que encaja con un patrón. 2 Prebúsqueda (en ingles, Look Ahead): Es una técnica utilizada en la construcción de lenguajes y traductores de lenguajes, el cual permite entre otras cosas saber con anticipación que valores siguen al que se está analizando.
  • 35. 35 II.2.2. Análisis Sintáctico (Parser) Un analizador sintáctico puede escribirse/diseñarse por medio de una gramática libre de contexto (en adelante, GLC) ó por medio de la forma normal de Backus (Backus-Naur Form, en adelante BNF), su tarea consiste en agrupar los tokens en forma jerárquica y construir las frases gramaticales, si estas frases son reconocidas como parte del lenguaje definido en analizador sintáctico construye el árbol sintáctico (ver Figura 8), el cual es el punto de partida para el analizador semántico. El hecho de previamente formalizar la escritura del analizador sintáctico ofrece la ventaja de poder encontrar ambigüedades, facilita la ampliación y/o modificación del lenguaje, y al proporcionar una estructura de lenguaje facilita la generación de código y detección de errores. El analizador sintáctico debe informar de los errores sintácticos que vaya encontrando e intentar recuperarse de ellos a fin de continuar la traducción, controlar el flujo de tokens reconocidos por la fase anterior de análisis y en ocasiones incorpora acciones semántica, con lo cual el proceso se convierte en una traducción dirigida por sintaxis.
  • 36. 36 De las tres fases de análisis, es en el analizador sintáctico en quien recae la dura misión de manejar los errores. Considerar el manejo de errores desde el momento de que se comienza a pensar en construir un traductor es lo más sensato, pues caso contrario en algún momento de la construcción del mismo se tendrá que hacer una vuelta atrás para incorporarlo. Los errores habituales pueden englobarse en los siguientes puntos: • Léxicos, mala escritura de identificador, palabra clave u operador. Proposición de asignación = Identificador E Expresión Expresión Identificador * ^ m Identificador c Numero 2 Expresión Expresión Expresión Fuente: Gálvez et. al 2005:57 Figura 8 - Árbol de análisis sintáctico para E=m*c^2.
  • 37. 37 • Sintácticos, expresión aritmética ó paréntesis no equilibrados, fin de sentencia ‘;’. • Semánticos, operador aplicado a un operando incompatible. • Lógicos, como ser una llamada infinitamente recursiva. • De corrección, el programa no hace lo que se esperaba. De todos los errores citados los errores de corrección y lógicos resultan imposibles de detectar por el traductor, el primero por ser una concepción de la abstracción del programador y en lo que respecta a errores lógicos por el exceso de esfuerzo computacional a fin de seguir los flujos del programa en ejecución para averiguar en que punto donde se generó la llamada recursiva, lo cual resulta no solo altamente costoso, si no que habitualmente imposible. Se desea que al momento de detectar algún tipo de error un analizador sintáctico sea capaz de: • Informar la naturaleza del error y su localización lo más exactamente posible. • Una vez corregido el error continuar construyendo el árbol sintáctico desde el punto en que se detuvo. • Distinguir entre errores y advertencias. • No debe ralentizar en forma significativa al compilador. Existen diversos métodos o forma para el menaje de errores a continuación se exponen algunos de ellos, como ser: Ignorar el problema (en inglés, panic mode); Consiste en ignorar todas las entradas hasta encontrar una condición de seguridad, como fin de sentencia, a partir del mismo se prosigue con el análisis en forma normal y todos los tokens desde que se produjo el error hasta la condición de seguridad quedan descartados, así como los consumidos antes del mismo.
  • 38. 38 Recuperación a nivel de frase; Cada vez que encuentra un error intenta corregirlo, por ejemplo si fuera el caso de una sentencia al que le falta un punto y coma (‘;’), el traductor agrega dicho carácter y prosigue, el problema con esto es que puede dar lugar a recuperaciones infinitas, pues al intentar corregir el error podría producir un nuevo error y luego intentaría corregir el error que produjo y así sucesivamente. Reglas de producción adicionales; Se añade a la gramática del lenguaje reglas de producción para reconocer errores comunes, lo cual da mayor control y permite emitir finalmente mensajes de advertencia, en lugar de un mensaje de error, pues permite detectar un error, corregirlo y proseguir. Corrección global, Para este método al momento de detectar un error, el analizador sintáctico solicita toda la secuencia de tokens al analizador léxico, a fin de producir una secuencia y su correspondiente árbol sintáctico, de forma que sea lo más parecido posible al original, pero sin errores. Gramática utilizada por los analizadores sintácticos. Desde el punto de vista de la escritura de un analizador sintáctico, se puede considerar la gramática de libre contexto, si bien existen construcciones sintácticas que no pueden generarse mediante una gramática de libre contexto, si no por medio de una de nivel superior, como ser las construcciones del lenguaje PERL. Una gramática G se define por medio de los elementos (N, T, P, S), en donde: N: Conjunto de No Terminales. T: Conjunto de Terminales. P: Conjunto de reglas de Producción. S: Axioma o símbolo inicial de la gramática.
  • 39. 39 Ejemplo: gramática no ambigua que reconoce las operaciones aritméticas (suma y multiplicación). E E + T | T T T * F | F F id | num | ( E ) Siguiendo con la definición formal de lo que es una gramática tenemos que: N = {E, T, F} T = {+, *, id, num, (, )} P = Son las 7 reglas de la gramática. S = E (símbolo inicial). Una regla de producción es una regla de reescritura, en donde el lado izquierdo de la regla (el no terminal) es sustituido por su parte derecha (secuencia de terminales y/o no terminales), por ende se dice que una cadena α deriva en β, y se denota βα ⇒ , cuando: *T)(Ne ∪,⇒ βαβα con , y siendo: δAσα ≡ δτσβ ≡ Dentro de una α puede haber varios A, lo cual da lugar a varias derivaciones posibles, surgiendo los siguientes conceptos: • Derivación por izquierda: Aquella en la que la reescritura se realiza en la terminal de más a la izquierda. ( ) ΡτΑ*ΤΝδττσ,ΝΑdonde ∈→∧∪∈∧∈
  • 40. 40 • Derivación por derecha: Aquella en la que reescritura se realizar en la terminal de más a la derecha. Ejemplos de derivaciones, sobre la base de la siguiente gramática ambigua: ( )Ε| id| num| ΕΕ ΕΕΕ ∗ +→ Derivación por izquierda: ↑↑↑↑↑ +∗⇒+∗⇒+∗⇒+∗⇒+⇒ idididΕididΕΕidΕΕΕΕΕΕ 321211 Derivación por derecha: ↑↑↑↑↑ +∗⇒+∗⇒+∗⇒+∗⇒+⇒ idididΕididΕΕidΕΕΕΕΕΕ 321211 Tanto en caso de la derivación por izquierda como en la derivación por derecha, el símbolo a derivar se encuentra señalado con una flecha apuntando al mismo (↑ ), la cadena resultante de una derivación recibe el nombre de formas senténciales3 . De este modo al analizar una sentencia del lenguaje se puede obtener su árbol de análisis sintáctico, el cual a su vez obedece a una gramática y es la representación utilizada para describir la derivación la misma. Se parte del axioma inicial como raíz del árbol y conforme se vaya necesitando se grafica las hojas hasta llegar a la cima o fondo del mismo. 3 Forma Sentencial: Cualquier secuencia de terminales y no terminales obtenida mediante derivaciones a partir del axioma inicial.
  • 41. 41 El árbol debe poseer una sola raíz, el cual es el axioma inicial y conforme se va avanzando las hojas del árbol se construyen, las cuales constituyen la forma sentencial, si por alguna circunstancia la sentencia a reconocer no perteneciera al lenguaje generado a partir de la gramática definida, será imposible obtener su árbol de análisis sintáctico, en cambio si a partir de la sentencia se pudiera construir más de un árbol para reconocerlo, nos encontramos frente a un caso de gramática con ambigüedades o ambigua. Partiendo de la generación del árbol sintáctico se podría decir que existen dos tipos de analizadores. Tipos de analizadores sintácticos: 1. Descendentes: Parten del axioma inicial, realizando derivaciones sucesivas hasta llegar a reconocer la sentencia, estos pueden ser. a. Con retroceso. b. Con funciones recursivas. c. Predictivos con gramáticas LL(1). 2. Ascendentes: Parten de la sentencia de entrada, aplicando derivaciones inversas hasta el axioma inicial, estos pueden ser: a. Con retroceso. b. Gramáticas LR(k). Análisis descendente con retroceso; se parte del axioma inicial y se aplican todas las reglas de posibles al no terminal de más a la izquierda de la forma sentencial. Todas las sentencias de un lenguaje representan nodos del árbol (árbol universal), el cual es producto de todas las posibles derivaciones desde el axioma inicial, por ende cuando comprueba la pertenencia de una sentencia al lenguaje, el árbol sintáctico de la misma está compuesto por los nodos desde el punto actual a la raíz. Es recomendable utilizar una cláusula de cancelación
  • 42. 42 (criterios de poda) para los casos de búsquedas infructuosas, a fin para evitar que el árbol se vuelva infinito en el intento de reconocer alguna sentencia no válida. Con este método lo que se desea es buscar por medio del árbol universal, recorriendo rama a rama, en caso de que una forma sentencial no coincida con la sentencia analizada, la rama es desechada, en caso de coincidir, la sentencia es aceptada y se retorna su árbol y por ultimo, si por medio de ninguna rama se reconoce la sentencia, la misma debe ser rechazada. El análisis descendente con retroceso no suele ser utilizado, si quiera para el lenguaje natural, debido a que el retroceso es altamente ineficiente, sumado a esto la existencia de varias otras técnicas de análisis más potentes y basados en diferentes técnicas de análisis lenguajes. Algoritmo de análisis descendente con retroceso: Precondición: FormaSentencial= ( )** ΤΝδyΝ,Α,Τμconδ,Αμ ∪∈∈∈ AnálisisDescendenteConRetroceso(formasentencial,entrada) Si ≡μ ParteIzquierda(entrada) retornar Fin Si ParaCada ∈ip i αΑp/Pp ii →≡∈ FormaSentencial ( )Τ∪Ν∈ Ν∈ΑΤ∈Α≡≡ ∗ 'y ,','con,'''δαμ i δ µδµ Si ≡'µ entrada ¡Sentencia reconocida ! Retornar Fin Si AnalisisDescendenteConRetroceso(FormaSentencial´, entrada) Si ¡Sentencia reconocida ! Retornar Fin Si Fin Para Fin AnálisisDescendeteConRetroceso Para la llamada deberíamos hacerlo de la siguiente manera:
  • 43. 43 AnálisisDescendenteConRetroceso(AxiomaInicial, CadenaAReconocer) Si no ¡Sentencia reconocida ! ¡¡ SENTENCIA NO RECONOCIDA !! Fin Si Análisis descendente con funciones recursivas; Unas de las formas más sencillas de crear un análisis descendente con funciones recursivas son las gramáticas en notación BNF, pues la misma permite utilizar palabras, símbolos y sus sencillas reglas de escrituras a fin de escribir analizadores sintácticos suficientemente potentes. Análisis descendentes predictivos con gramáticas LL(1); En el análisis descendente predictivo para cada símbolo no-terminal a ser analizada existe únicamente una regla de producción que se puede aplicar. Este tipo de análisis está basada en gramáticas LL(1), donde LL(1) significa: L (Left-to-Right): Se lee la cadena de izquierda a derecha. L (Left-most): Derivación por izquierda. 1: Símbolo de anticipación. Construir un analizador sintáctico predictivo no recursivo por medio de una pila es posible; el problema durante el análisis predictivo radica en determinar la producción a aplicar a un no-terminal, partiendo del símbolo analizado actualmente y el siguiente símbolo de entrada, para ello el analizador sintáctico no recursivo busca en la tabla de análisis sintáctico que regla de producción debe aplicar. Pasos para reconocer una cadena por medio de LL(1): • La cadena a reconocer, junto con el apuntador que indica el token de pre-búsqueda actual, denotaremos por a al token
  • 44. 44 • Una pila de símbolos, el cual contendrá a los terminales y no terminales, considérese X como la cima de la pila. • Una tabla de chequeo de sintaxis, asociada de forma univoca a la gramática, denotaremos por M a la tabla, el cual tiene la configuración siguiente: M[N x T U {$}]. La cadena de entrada debe acabar con EOF (fin de cadena, en adelante $), a fin de reconocer una cadena aplicando el presente algoritmo se debe consultar de forma reiterada la tabla M hasta que se logre aceptar o rechazar la sentencia, para el efecto se inicia con una pila que en su base contendrá a $ y el axioma inicial S de la gramática sobre la misma. Los pasos a seguir durante la consulta son excluyentes y se exponen a continuación: 1. Si X = a = $ entonces ACEPTAR. 2. Si X = a ≠ $ entonces - Se desapila X, y - Se avanza el apuntador. 3. Si X ∈ T y X ≠ a entonces RECHAZAR. 4. Si X ∈ N entonces consultar la entrada M[X, a] de la tabla, y: - Sí M[X, a] está vacía: Rechazar. - Sí M[X, a] no está vacía, se reemplaza X de la pila e insertar el consecuente (parte derecha) en orden inverso. Ejemplo; SI M[X, a] = {X → LMN}, quitar X de la pila e insertar LMN en orden inverso, ó sea NML. Una vez aplicada la regla, ya no se puede volver a desaplicar como sucede con las gramáticas con retroceso. La principal dificultad de este tipo de análisis radica en que existe un número reducido de gramáticas LL(1), aunque si bien es cierto que existe un algoritmo para convertir gramáticas de contexto libres a su equivalentes LL(1).
  • 45. 45 Operaciones para convertir una GLC en una Gramática LL(1): 1. Limpiar la gramática, Eliminar símbolos muertos y símbolos inaccesibles. 2. Factorizar por izquierda. 3. Eliminar recursividad por izquierda. Gramática de partida G(E): F^TQ F-PL id|(E)F numLP F|FT T|ΕΕΕ → → → ÷→ ∗→Τ +→ Limpiando la gramática. Eliminando símbolos muertos: F^TQ id|(E)F F|FT T|ΕΕΕ → → ∗→Τ +→ Limpiando la gramática. Eliminando símbolos inaccesibles: id|(E)F F|FT T|ΕΕΕ → ∗→Τ +→ Factorizado quedaría (Gramática equivalente): id|(E)F |FT'T' FT' |TE'E' E'TΕ → ∗→ →Τ +→ → ε ε
  • 46. 46 Construcción de la tabla de símbolos: Para la construcción de la tabla de símbolos se utilizan las funciones: PRIMERO (α). SIGUIENTE (A). PRIMERO (α): Si α es una cadena de símbolos gramaticales, entonces, PRIMERO (α) es el conjunto de terminales que inician las cadenas derivada de α. SI εα ∗⇒ entonces, ( )αPRIMEROε ∈ Algoritmo para calcular PRIMERO(x): - Si x es terminal entonces, PRIMERO(x) = {x}. - Si ε→x es una producción añadir ε a PRIMERO(x). - Si kyyyx ,...,, 21→ es una producción. - Si a ∈ PRIMERO(Yi) y ε está en todos los PRIMERO(Yi)… PRIMERO(Yi-1) entonces añadir a a PRIMERO(x). - Si ε está en PRIMERO(Yj), para todo j = 1, 2, …, k entonces añadir ε a PRIMERO(x). - Ejecutar hasta que no se puedan añadir más elementos a PRIMERO(x). Algoritmo para calcular SIGUIENTE(A): Aplicar a cada no-terminal de la gramática, hasta que no se puedan añadir más elementos. - Para el símbolo distinguido añádase $, a SIGUIENTE(A), si A es axioma. - Si hay una regla de producción αΒβΑ → , añádase al conjunto PRIMERO( β ) a SIGUIENTE(B), excepto ε . - Si αΒΑ → ó αΒβΑ → son reglas de producciones, tal que PRIMERO( β ) contenga ( )εβε *⇒ . Entonces añadir el conjunto SIGUIENTE(A) a SIGUIENTE(B).
  • 47. 47 Figura 9 - PRIMEROS y SIGUIENTE. Ejemplo: Sea una producción ETΕ → : T E’ … … PRIMEROS(T) PRIMEROS(E’) SIGUIENTES(T) Fuente: Revelle 2006:10 Calculando PRIMEROS y SIGUIENTES, para gramática (E): Primero(E): {(, id} Primero(E’): {+, ε} Primero(T): {(, id} Primero(T’): {*, ε} Primero(F): {(, id} Siguiente(E): { $, ) } Siguiente(E’): {$, )} Siguiente(T): {+, $} Siguiente(T’): {+, $, ) } Siguiente(F): {*, +, $, )} Tabla 1 - Tabla de análisis sintáctico LL(1). T N id + * ( ) $ E E'TΕ → E'TΕ → E’ E'TΕ' +→ ε→E' ε→E' T FT'T → FT'T → T’ ε→T' FT'*T → ε→T' ε→T' F idF → (E)F → Fuente: Gálvez et. al 2005:71 La tabla viene asociada exclusivamente a la gramática, y representa su autómata finito con pila, este método es mucho más eficiente que el análisis con retroceso e igual de eficiente que el análisis con funciones recursivas. Una ventaja sobre el método con funciones recursivas es que se puede automatizar la generación de las tablas, por ende una modificación en la gramática de entrada no
  • 48. 48 es fuente de grandes problemas desde el punto de vista de la construcción de una nueva tabla. Recuperación de errores: Una cadena de entrada es errónea desde el punto de vista sintáctico, sí se llega a una celda vacía en la tabla de símbolos, en este punto no solo es útil detectar el error, si no buscar los mecanismos para tratarlos, para ello el traductor debe: 1. Anunciar el error a fin de facilitar su localización. 2. Recuperarse y seguir analizando el resto de la cadena de entrada. Métodos de manejos de errores: 1. A nivel de frase (Ad-Hoc): Llenar todas las celdas vacías con marcas que permitan seleccionar el ó los procedimientos más adecuados para el tratamiento del error, para lo cual es necesario estudiar las posibles causas en cada celda dada y las acciones correctivas para el error. 2. Método Panic(Sincronización): Este método consiste en saltar tantos símbolos de la secuencia de cadena de entrada que sean necesarios, hasta llegar a un símbolo situado al tope de la pila que permita la selección de una celda no vacía. El proceso de recuperación por el método Panic, permite ser optimizado para lo cual será necesario realizar una delimitación de los conjuntos de símbolos de sincronización. Delimitación de símbolos de sincronización: Consiste en seleccionar los tipos de errores que se desean tratar y para cada caso en particular seleccionar los símbolos de sincronización.
  • 49. 49 Casos más comunes: 1. Para cada símbolo no terminal A, incluir como símbolos de sincronización los SIGUIENTES(A). 2. Si el lenguaje utiliza delimitadores de bloques o sentencias y se omite el delimitador de fin, incluir como símbolos de sincronización PRIMEROS del bloque o sentencia. 3. Si un no terminal A se deriva en una cadena vacía, resulta imposible detectar el error. 4. Si un símbolo terminal ‘a’ aparece en el tope de la pila y no parea con el símbolo de entrada, se puede optar por quitar el símbolo terminal ‘a’ de la pila y emitir un mensaje indicando que el mismo ha sido omitido. Análisis ascendentes; este tipo de análisis tiene como finalidad la construcción del árbol de análisis sintáctico, partiendo del token hasta llegar al axioma inicial, disminuyendo la posibilidad de reglas mal aplicadas y ampliando la gama de gramáticas con posibilidades de ser analizadas. Esté o no presente retroceso como parte de la definición de la gramática, la cadena de entrada estará divida en dos partes, βα y : β : Secuencias de tokens por consumir ( ∗ Τ∈β ) y siempre coincidirá con algún trozo de la parte derecha de la cadena de entrada. Inicialmente β coincide con toda la cadena a reconocer, incluido $. α : Resto de la cadena de entrada, trozo de la cadena de entrada al que se ha aplicado algunas de reglas de producción, en sentido inverso ( ( )∗ Τ∪Ν∈α ), representando al árbol sintáctico visto desde arriba, la cual se forma conforme se van aplicando las reglas
  • 50. 50 de producción a β , en otras palabras α es una forma sentencial ascendente. La mecánica de todo análisis ascendente consiste en ir aplicando operaciones, partiendo de una configuración inicial (entiendase configuración por el par βα − ), las cuales permiten transitar de una configuración de origen a una de destino, finalizando una vez consumidos todos los tokens de entrada en β y la configuración de destino sea en la que α represente al árbol sintáctico por completo. Las operaciones disponibles son cuatro, las cuales se exponen a continuación: 1. ACEPTAR: Se acepta la cadena ≡β $ y ≡α S. 2. RECHAZAR: Cuando la cadena de entrada no es válida. 3. Reducir: Consiste en aplicar una regla de producción hacía atrás a algunos elementos situados a la derecha de α . 4. Desplazar: Consiste en quitar el terminal más a la izquierda de β y ponerlo a la derecha de α . Una cadena de entrada es aceptada cuando, α es el axioma inicial, β es una tira nula y solo contiene $, en caso de que β no sea una tira nula, α no sea el axioma inicial y no puede aplicarse ninguna regla, la cadena se rechaza. Antes de llegar a aceptar o rechazar cadena alguna, primeramente se debe aplicar todas las reducciones a α y luego realizar los desplazamientos. Análisis ascendentes con retroceso; Este tipo de análisis al igual que su par del descendente intenta probar todas operaciones posibles de reducir y desplazar, por medio de la fuerza bruta, hasta llegar a construir el árbol sintáctico o agotar todas las opciones, en cuyo caso rechaza la cadena de entrada.
  • 51. 51 Precondición: ( )∗ Τ∪Ν∈α y ∗ Τ∈β AnálisisAscendenteConRetroceso ),( βα Para cada pi Ρ∈ hacer Si consecuente(pi) ≡ cola(α ) '' βα − = reducir βα − por la regla pi Si ≡α S and εβ ≡ ¡ Sentencia reconocida ! (ACEPTAR) si no AnálisisAscendenteConRetroceso )','( βα Fin si Fin si Fin Para Si εβ ∗ '' βα − = desplazar βα − AnálisisAscendenteConRetroceso )','( βα Fin si Fin AnálisisAscendenteConRetroceso Y la llamada al algoritmo: AnálisisAscendenteConRetroceso( ,ε CadenaAReconocer) Si NOT ¡ Sentencia reconocida ! ¡ Sentencia no reconocida ! (RECHAZAR) Fin si Ante gramáticas que posean reglas con épsilon (ε), el presente algoritmo termina por producir recursividad infinita, debido a que dicha regla puede aplicarse hacia atrás produciendo una rama infinita hacia arriba. El método de análisis ascendente con retroceso consume más rápidamente los tokens de entrada que el análisis descendente con retroceso, lo que le permite tomar decisiones con mayor cantidad de información y de forma
  • 52. 52 más eficiente, aunque de igual modo resulta inviable para aplicaciones prácticas, pues su ineficiencia frente a otros métodos sigue siendo notoria. Análisis ascendente de gramáticas LR (k); Es el método más difundido, conocida como gramática LR(k), permite el análisis de una amplia gama gramáticas de libre contexto, en donde LR(k) significa: L: Consume los símbolos de izquierda a derecha (en inglés, Left- to-rigth). R: Por reducir por la derecha (en inglés, Rightmost derivation). k: Cantidad de símbolos anticipación que se utiliza para tomar las decisiones, por defecto 1, razón por la cual es comúnmente conocida como LR(1). El análisis LR resulta atractivo debido a diversas razones: • Reconoce prácticamente todos los lenguajes derivados de las gramáticas de contexto libres. • Es el método de análisis sin retroceso más difundido, por su fácil implementación. • Realiza la detección de errores conforme se realiza el análisis y realiza la recuperación de las mismas de forma controlada. Unas de las desventajas de este método radica en el hecho de que construir manualmente un analizador sintáctico LR para una gramática de contexto libre lleva mucho trabajo, afortunadamente existen las herramientas generadoras, en las que proveyendo una gramática de contexto libre, se puede generar el analizador sintáctico para la misma, si la gramática que se proporciona al generador posee ambigüedades o construcciones difíciles de entender, emite un mensaje al diseñador especificando la posible regla de producción, a fin de que el mismo pueda verificarlo y corregirlo.
  • 53. 53 Los analizadores LR basan su funcionamiento en la existencia de una tabla de análisis (acciones-transiciones) asociada a la gramática siendo en este caso similar a los analizadores LL(1). La confección de tabla de análisis podría resultar una tarea hasta si se quiere algo compleja, si bien dependiendo del método seleccionado para hacer la tabla la misma puede terminar siendo una tarea mecánica. Existen tres métodos principales para la construcción de tablas de análisis: 1. LR Sencillo (en inglés, SLR): El más fácil de aplicar aunque el menos potente. 2. LR canónico ó LR(1): De los tres el más potente, y el más complejo. 3. LALR (LR con examen de anticipación - en inglés, Look Ahead LR): Es el método intermedio y el más difundido de las tres, se implementa sin mucho esfuerzo de forma eficiente. Un método es más potente que el otro en relación a la cantidad de gramática admitidas, aunque a mayor número de gramáticas mayor complejidad. La pila del autómata de pila utilizado para el análisis LR está formado por parejas de símbolos (terminales y no terminales) y estados. 1. Si acción [Sm, ai] = Desplazar, el analizador inserta en la pila el símbolo de entrada ai y el siguiente estado S, obtenido gracias a la evaluación Goto [Sm, ai]. Ahora, ai+1 pasa a ser el símbolo de la entrada a leer y el analizador se encuentra ahora en el estado S. 2. Si acción [Sm, ai] = Reducir, entonces el analizador ejecuta una reducción utilizando la regla A β. 3. Si acción [Sm, ai] = Aceptar, el análisis se ha terminado con éxito.
  • 54. 54 4. Si acción [Sm, ai] = Error, se ha descubierto un error. El analizar llama al procedimiento de recuperación de error. Figura 10 - Esquema Sinóptico de un analizador LR Pila Sm Xm Sm-1 Xm-1 … S0 Fuente: Gálvez et. al 2005:78 Algoritmo para el Analizador LR: Hacer que p apunte al primer símbolo de w$; Mientras .t. Sea S el estado situado en la parte superior de la pila; Sea a el símbolo al que apunta p; si acción[S, a] = Desplazar S’ Cargar a y luego S’ en la pila; Hacer que p apunte al siguiente símbolo; sino si acción[S, a] = Reducir A β Sacar de la pila 2|β| símbolos pila; Sea S’ el estado situado en el tope de la pila; Cargar A y el resultado de goto[S’, A] en la pila; Ejecutar las acciones semánticas de A β; sino si acción [S, a] = Aceptar Entrada a1 a2 a3 … an $ Tabla de Análisis Estado Acción Goto Analizador LR (Programa de proceso) Salida de Análisis
  • 55. 55 retornar; sino error(); fin si fin mientras Ejemplo de aplicación del algoritmo: Evolución del análisis con gramática LR para el reconocimiento de la siguiente secuencia de entrada id * id + id: Sea la gramática: idF6. (E)F5. FΤ4. FTΤ3. TΕ2. ΕΕΕ1. → → → ∗→ → +→ Tabla 2 - Tabla de Análisis LR. Fuente: Aho et. al 1990:225 Estado Acción Goto Di: Desplazar hasta i. Rj: Reducir por j. acp: Aceptar. id + * ( ) $ E T F 0 D5 D4 1 2 3 1 D6 acp 2 R2 D7 R2 R2 3 R4 R4 R4 R4 4 D5 D4 8 2 3 5 R6 R6 R6 R6 6 D5 D4 9 3 7 D5 D4 1 8 D6 D1 9 R1 D7 R1 R1 10 R3 R3 R3 R3 11 R3 R3 R3 R3
  • 56. 56 Tabla 3 - Reconocimiento de id*id+id. Pila Entrada Acción-Goto 0 id * id + id$ Desplazar 0 id 5 * id + id$ Reducir: F -> id 0 F 3 * id + id$ Reducir: T -> F 0 T 2 * id + id$ Desplazar 0 T 2 * 7 id + id$ Desplazar 0 T 2 * 7 id 5 + id$ Reducir: F -> id 0 T 2 * 7 F 10 + id$ Reducir: T -> T * F 0 T 2 + id$ Reducir: E -> T 0 E 1 + id$ Desplazar 0 E 1 + 6 id$ Desplazar 0 E 1 + 6 id 5 $ Reducir: F -> id 0 E 1 * 6 F 3 $ Reducir: T -> F 0 E 1 + 6 T 9 $ Reducir: E -> E + T 0 E 1 $ Aceptar Fuente: Aho et. al 1990:226 Construcción de tablas de análisis para un SLR (LR (0)): Pivote (mango, asa): Es una subcadena que coincide con par parte derecha de una producción (regla). Prefijo viable: Secuencia de símbolos que se pueden formar de izquierda a derecha hasta un símbolo t (terminal) Ítems: Una producción representada entre paréntesis cuadrados “[”, “]” y un punto “.” en la parte derecha de la producción. El método SLR tiene como premisa construir un autómata finito de determinista (en adelante, AFD) a partir de la gramática para reconocer los prefijos viables. Los estados del AFD se obtienen gracias al agrupamiento de los ítems asociados con cada producción de la gramática. Construcción de una AFD para un analizador SLR: 1. Gramática aumentada: Sea S el símbolo inicial de una gramática G, la cual se reescribe a fin de obtener una
  • 57. 57 gramática aumentada (G’) en la que S’ es el nuevo símbolo inicial quedando SS' → . 2. Operación de Cierre o Clausura: Siendo I el conjunto de ítems, la operación de cierre para I, denotaremos como cierre(I), la cual será obtenida aplicando las siguientes reglas. a. I ∈ cierre(I). b. Si Ββ].α[Α → ∈ cierre(I) y existe la producción →Β µ, entonces [ →Β .µ] ∈ cierre(I). c. Repetir paso b hasta que no se añada nuevo ítem a cierre(I). Sea I = { EE' ⋅→ } Cierre(I)={[ EE' ⋅→ ], [ TΕΕ +⋅→ ], [ TΕ ⋅→ ], [ F*TT ⋅→ ], [ FT ⋅→ ], [ )(F Ε⋅→ ], [ idF ⋅→ ]} 3. Función GOTO: Función de transición de estados a partir de uno dado y según el símbolo de entrada en cuestión. Goto(I, X), donde I es el conjunto de ítems y X es un símbolo de la gramática. Se define Goto(I, X), como el cierre del conjunto de todos los ítems [ ] [ ] Iα.XβΑquetalαX.βΑ ∈→→ . Gramática(G): idF6. (E)F5. FΤ4. FTΤ3. TΕ2. TΕΕ1. → → → ∗→ → +→ Gramática aumentada(G’): idF7. (E)F6. FΤ5. FTΤ4. TΕ3. TΕΕ2. '1. → → → ∗→ → +→ Ε→Ε
  • 58. 58 En base a la gramática anterior (G’) y dado el siguiente conjunto de ítems: [ ] [ ]}TΕΕ,ΕΕ'{I +⋅→⋅→= Entonces: [ ] [ ]}TΕΕ{I,goto +⋅→=+ Función cierre( [ ]+I,goto ): Cierre([ ]TΕΕ +⋅→ ) = {[ ]TΕΕ ⋅+→ , [ ]F*TT ⋅→ , [ ]FT ⋅→ , [ ])(F Ε⋅→ , [ ]idF ⋅→ } Para obtener los estados asociados a una gramática SLR se debe aplicar el siguiente algoritmo, de este modo se obtenemos el conjunto de ítems(C) LR(0), en base a una gramática aumentada (G’): Procedimiento ítems(G’): C :={cierre({[ ]SS' ⋅→ })}; Repetir Para cada I ∈ C y para cada símbolo X ( )Τ∪Ν∈ tal que goto(I, X) ε∉ y ∉ C. Añadir goto(I, X) a C Fin Para Hasta (que no se puedan añadir más ítems a C) Retornar Gramática aumentada (G’): idF7. (E)F6. FΤ5. FTΤ4. TΕ3. TΕΕ2. '1. → → → ∗→ → +→ Ε→Ε
  • 59. 59 Figura 11 - Conjunto LR(0) canónico para EE' ⋅→ . Colección canónica que define los estados del AFD para un SLR: I0 = EE' ⋅→ TΕΕ +⋅→ TΕ ⋅→ F*TT ⋅→ FT ⋅→ )(F Ε⋅→ idF ⋅→ I5 = goto(I0,id): idF ⋅→ I6 = goto(I1,+): TΕΕ ⋅+→ F*TT ⋅→ FT ⋅→ )(F Ε⋅→ idF ⋅→ I1 = goto(I0,E): ⋅→ EE' TΕΕ +⋅→ I7 = goto(I2,*): F*TT ⋅→ )(F Ε⋅→ idF ⋅→ I2 = goto(I0,T): ⋅→ TΕ *FTT ⋅→ I8 = goto(I4,E): )(F ⋅Ε→ TΕΕ +⋅→ I3 = goto(I0,F): ⋅→ FT I9 = goto(I6,T): ⋅+→ TΕΕ *FTT ⋅→ I4 = goto(I0,(): )(F Ε⋅→ TΕΕ +⋅→ TΕ ⋅→ F*TT ⋅→ FT ⋅→ )(F Ε⋅→ idF ⋅→ I10 = goto(I7,F): ⋅→ F*TT I11 = goto(I8,)): ⋅Ε→ )(F Fuente: Aho et. al 1990:231
  • 60. 60 Figura 12 - Diagrama AFD para los prefijos viables. Fuente: Aho et. al 1990:232 Algoritmo para la construcción de la tabla de análisis SLR: Entrada: Gramática aumentada (G’). Salida: Funciones acción y goto. Método. 1. Construir C = {I0, I1,…, In}, colección de estados asociados a la gramática aumentada (G’). a I7I0 I1 I6 I9 I2 I3 I4 I5 I8 I1 1 I7 I1 0 E + T * T * F ( id F ( id F ( id id E F T ) + a I3 a I4 a I5 a I4 a I5 a I6 a I2 a I3 (
  • 61. 61 2. El estado i es construido partiendo de Ii, apartir de allí se determinan las acciones del analizador sintáctico para el estado i según los siguientes criterios: a. Si [ ]βαΑ a⋅→ ∈ Ii y goto(Ii, a) = Ij, entonces acción[i, a] = “desplazar j” , donde a ∈ símbolo terminal. b. Si [ ]∈⋅→ αΑ Ii, acción[i, a] = “reducir αΑ → ”. Para a ∈ SIGUIENTE(A), donde A ≠ S’. c. Si [ ]∈⋅→ S'S Ii,, acción[i, $] = “aceptar”. d. Si no coincide con ningún caso ó se generan reglas contradictorias NO ES SLR. 3. Para todos los símbolos no terminales A construir transiciones goto al estado i, siguiendo la regla: Si goto(I, A)= Ij, entonces goto(I, A) = j. 4. Las entradas no reconocidas por las reglas 2 y 3, serán consideradas “error”. 5. El estado inicial se construye a partir del conjunto de ítems que contiene [ ]SS' ⋅→ (cierre[ ]SS' ⋅→ ). Considerando la gramática aumentada (G’) del ejemplo anterior, se considerará del conjunto de ítems de la serie canónica primeramente se toma I0. I0: EE' ⋅→ TΕΕ +⋅→ TΕ ⋅→ F*TT ⋅→ FT ⋅→ )(F Ε⋅→
  • 62. 62 idF ⋅→ Donde: )(F Ε⋅→ , permite la entrada, acción[0, (] = desplazar 4. idF ⋅→ , permite la entrada, acción[0, id] = desplazar 5. I1: ⋅→ EE' TΕΕ +⋅→ Donde: ⋅→ EE' , permite la entrada, acción[1, $] = aceptar, TΕΕ +⋅→ , permite la entrada, acción[1, +] = desplazar 6. I2: ⋅→ TΕ *FTT ⋅→ Donde: ⋅→ TΕ , Siendo que SIGUIENTE(E)={$, +, (}, permite la entrada, acción[2, $] = acción[2, +]= acción[2, )] = reducir TΕ → . *FTT ⋅→ , permite la entrada, acción[2, *] = desplazar 7. Siguiendo paso a paso el algoritmo como se demostró en el ejemplo, se llegará a construir la tabla de acción y goto. Una gramática SLR(1), puede incurrir en error de desplazar/reducir (shift/reduce), aún frente a gramáticas no ambiguas, por su parte las gramáticas LR canónico y LALR proveen mayor tolerancia a este tipo de errores, aun siempre existe la posibilidad de que alguna gramática no ambigua pueda producir
  • 63. 63 una tabla de acciones con conflictos desplazar/ reducir, más cabe resaltar que dichas gramáticas son evitables en la construcción de lenguajes. Construcción de tabla de análisis para un LR canónico ó LR(1): En los analizadores SLR: si [ ]∈⋅→ αΑ Ii, acción[i, a] = “reducir αΑ → ”. Para a ∈ SIGUIENTE(A), donde A ≠ S’. Sin embargo esto no siempre es posible debido a que ocasionalmente el estado i podría estar en el tope de pila, quedando el prefijo viable βα por debajo de dicho estado (i) y en consecuencia βA no puede ir seguida de una a en una forma sentencial derecha, por lo que la reducción αΑ → no sería válida para la entrada a. Para solucionar el problema expuesto anteriormente los analizadores LR canónicos se valen de un símbolo de anticipación, de ésta forma se pueden aplicar restricciones a reglas de reducciones no válidas. Ítem del análisis sintáctico LR(1)4 : Sea [ ]aβ,αΑ ⋅→ , donde αβ→Α es una producción y a es un Terminal o el marcador derecho $. Sea [ ]⋅→ αΑ reducir por αΑ → , si el siguiente símbolo de entrada es a. Un ítem LR(1) [ ]aβ,αΑ ⋅→ es válido para un prefijo viable αδ si: { { ,βδδΑw*S pdpd wa⇒⇒ Donde: pd = Parte derecha. w = Terminales. a = PRIMERO(w) ó w es ε y a es $. 4 LR(1): El “1” significa la longitud del siguiente elemento, símbolo de anticipación.
  • 64. 64 El método de construcción de colección de ítems LR(1), funciona básicamente de la misma manera que para LR(0), a excepción de la operación de cierre y goto, considérese: 1. I0 = cierre [ ]( ),$'S S⋅→ 2. Si se tiene un ítem [ ]aBβ,αΑ ⋅→ y la producción ηB → , incluir cierre [ ]bη,⋅→Β , donde b ∈ PRIMEROS ( )αβ , por tanto si δα es un prefijo viable valido de [ ]aBβ,αΑ ⋅→ implica que { { ,βδδΑ*S pdpd waBw ⇒⇒ donde a ∈ PRIMEROS(w). Si βδβδ ηB aa ⇒ y b ∈ PRIMEROS ( )aβ , entonces δαη es prefijo válido. Construcción de la colección de ítems LR(1) para la gramática G’: Entrada: Gramática aumentada (G’) Salida: La colección de ítems LR(1) para (G’). Procedimiento cierre(I) Hacer Para cada ítem[ ]∈⋅→ aBβ,αΑ I, cada producción ∈→ γB G’ y cada Terminal b en PRIMERO ( )aβ tal que [ ]∉⋅→ bγ,Β I, añadir [ ]bγ,Β ⋅→ a I Fin Para Mientras se puedan añadir más ítems a I Retornar I Retornar
  • 65. 65 Procedimiento goto(I, X) Si J = colección de ítems[ ]aβ,αXΑ ⋅→ , tal que [ ]∈⋅→ aβ,αXΑ I retornar cierre(I) Fin Si Retornar Procedimiento Ítems(G’) C :={cierre({[ ]S,$S' ⋅→ })} Hacer Para cada colección I ∈ C y X tal que goto(I, X) φ≠ y no esté en C Añadir goto(I, X) a C Fin Para Mientras se puedan añadir colecciones de ítems a C Retornar Sea la siguiente gramática: G(S): d|cCC CCS → → G’(S’): d|cCC CCS S$S' → → → Cálculo de primeros: PRIMERO(S) = {c, d} PRIMERO(C) = {c, d} PRIMERO(C$) = {c, d} Cálculo de colección de ítems cierre: I0 = cierre [ ]( )S,$S' ⋅→ = {[ ],$'S S⋅→ , [ ],$CCS ⋅→ , [ ]c/dcC,C ⋅→ , [ ]c/dd,C ⋅→ }
  • 66. 66 I1 = goto(I0, S) = cierre [ ]( ),$SS' ⋅→ = { [ ],$S'S ⋅→ } I2 = goto(I0, C) = cierre [ ]( ),$CCS ⋅→ = { [ ],$CCS ⋅→ , [ ]C,$cC ⋅→ , [ ],$dC ⋅→ } I3 = goto(I0, c) = cierre [ ]( )C,$cC ⋅→ = { [ ]c/dC,cC ⋅→ , [ ]c/dcC,C ⋅→ , [ ]c/dd,C ⋅→ } I4 = goto(I0, d) = cierre [ ]( )c/d,dC ⋅→ ={[ ]c/d,dC ⋅→ } I5 = goto(I2, C) = cierre [ ]( ),$CCS ⋅→ ={[ ],$CCS ⋅→ } I6 = goto(I2, c) = cierre [ ]( )C,$cC ⋅→ = { [ ]C,$cC ⋅→ , [ ]cC,$C ⋅→ , [ ]d,$C ⋅→ } I7 = goto(I2, d) = cierre [ ]( ),$dC ⋅→ ={[ ],$dC ⋅→ } I8 = goto(I3,C) = cierre [ ]( )c/d,cCC ⋅→ ={[ ]c/d,cCC ⋅→ } I9 = goto(I3,c) = cierre [ ]( )c/dC,cC ⋅→ = ¡ YA EXISTE ! I9 = goto(I3,d) = cierre [ ]( )c/d,dC ⋅→ = ¡ YA EXISTE ! I9 = goto(I6,C) = cierre [ ]( ),$cCC ⋅→ ={[ ],$cCC ⋅→ } I10 = goto(I6,c) = cierre [ ]( )C,$cC ⋅→ = ¡ YA EXISTE ! I10 = goto(I6,d) = cierre [ ]( ),$dC ⋅→ = ¡ YA EXISTE ! Ya no se puede seguir creando ítems y se termina.
  • 67. 67 Algoritmo para la construcción de la tabla de análisis LR canónico. Entrada: Una gramática aumentada G’. Salida: Funciones acción y goto de la tabla de análisis para LR canónico. Método: 1. Construir C={I1, I2, I3, …, In} la colección de ítems o estados asociados a la gramática LR(1) para G’. 2. Partiendo de Ii, se construye el estado i. Las acciones a ser aplicadas se determinan de la siguiente manera: a. Si [ ]∈⋅→ ba ,αΑ β Ii y goto(I, a)= Ij, acción[i, a] = “desplazar j”. a debe ser terminal. b. Si [ ]∈⋅→ a,αΑ Ii, A≠ S’, acción[i, a] = “reducir αΑ → ”. c. Si [ ]∈⋅→ ,$SS' Ii, acción[i, $] = “aceptar”. Si se produce un conflicto, entonces la gramática no es LR(1) y falla el algoritmo. 3. Construir goto(i, A) para todo no terminal, si goto(Ii, A) = Ii, entonces goto[i, A] = j. 4. Las entradas en las tablas de análisis sin correspondencia con las reglas 2 y 3 son etiquetadas como “error”. 5. A partir del ítem [ ],$'S S⋅→ , se construye el estado inicial del analizador sintáctico.
  • 68. 68 Figura 13 - Grafo de transiciones para los goto de S$S'→ . Fuente: Aho et. al 1990:241 Tabla 4 - Tabla de análisis LR(1) para S$S'→ . Fuente: Aho et. al 1990:242 Estado Acción Goto c d $ S C 0 D3 D4 1 2 1 aceptar 2 D6 D7 5 3 D3 D4 8 4 R3 R3 5 R1 6 D6 D7 9 7 R3 8 R2 R2 9 R2 ,$cCC ⋅→ I9 ,$dC ⋅→ I7 ,$'S S⋅→ ,$CCS ⋅→ c/dcC,C ⋅→ c/dd,C ⋅→ I0 ,$SS' ⋅→ I1 ,$CCS ⋅→ C,$cC ⋅→ ,$dC ⋅→ I2 c/d,dC ⋅→ I4 c/d,cCC ⋅→ I8 ,$CCC ⋅→ cC,$C ⋅→ ,$dC ⋅→ I6 c dc/,CcC ⋅→ c/dcC,C ⋅→ c/dd,C ⋅→ I3 c CC,$S' → I5
  • 69. 69 Construcción de tabla de análisis LALR(1): La construcción de tablas de análisis sintácticos por medio del método LALR(1) es menos potente que por el método LR(1), pero más potente que por el método SLR. Es el método de construcción de tablas de análisis más utilizada por la facilidad de expresar las sintaxis de los lenguajes de programación por medio de una gramática LALR, sumado a esto que las tablas de análisis resultantes son considerablemente más pequeñas que las de LR(1). Algoritmo para la construcción de la tabla de análisis LALR(1) Entrada: Gramática Aumentada. Salida: Acción y Goto. Método: 1. Construir la colección de ítems C={I0, I1,…, In} y el AFD correspondiente como en el método LR(1). 2. Se unen todos los estados que estén conformados por los mismos ítems y solo difieran en el símbolo de anticipación, esto dará como resultado un AFD simplificado. 3. C’ = {J0, J1,…, Jn} es la colección de ítems LR(1) resultante. Construir a partir del estado Ji las acciones sintácticas para el estado i. Si no aparece conflictos en la tabla resultante LALR(1), la gramática es considerada LALR(1). 4. La construcción del goto se realiza de la siguiente manera; se considera J como la unión de uno o más ítems LR(1), J= J1 U J2 U Jk3, entonces las producciones de goto(I1,X), goto(I2,X), …, goto(In, X), son las mismas. Sea K la unión de todas las colecciones que tienen la misma producción que goto(I1,X), lo cual da como resultado que goto(J, X) = K. Resultado de la unión de producciones LR(1): I36: I3=goto(I0,c)=cierre [ ]( )C,$cC ⋅→ ={[ ]c/dC,cC ⋅→ ,[ ]c/dcC,C ⋅→ ,[ ]c/dd,C ⋅→ }
  • 70. 70 I6=goto(I2,c)=cierre [ ]( )C,$cC ⋅→ ={ [ ]C,$cC ⋅→ , [ ]cC,$C ⋅→ , [ ]d,$C ⋅→ } I47: I4=goto(I0,d)=cierre [ ]( )c/d,dC ⋅→ ={[ ]c/d,dC ⋅→ } I7=goto(I2,d)=cierre [ ]( ),$dC ⋅→ ={[ ],$dC ⋅→ } I89: I8=goto(I3,C)=cierre [ ]( )c/d,cCC ⋅→ ={[ ]c/d,cCC ⋅→ } I9 = goto(I6,C) = cierre [ ]( ),$cCC ⋅→ ={[ ],$cCC ⋅→ } Partiendo de la base del AFD para el método LR(1), construido anteriormente se muestra un AFD simplificado para LALR(1). Figura 14 - AFD Simplificado para LALR(1). Fuente: Revelle 2006:27 I0 I4, I7 I2 I3, I6 I8, I9 I1 I5 C c c c d d C S d C
  • 71. 71 Una construcción de tabla de símbolos LALR(1), no debería arrojar conflictos de desplazar/reducir, puesto que se parte de la base de que la gramática fue aceptada como LR(1), para ello primeramente la gramática no tuvo que haber tenido conflictos de este tipo, ahora bien, pueden aparecer conflictos reducir/reducir como resultado de la unión de estados del AFD, aún cuando la gramática fuera LR(1). Tabla 5 - Tabla de análisis LALR(1) para S$S'→ . Fuente: Aho et. al 1990:246 Ambigüedad y precedencia: Toda gramática ambigua no es un LR, aunque existen situaciones en las que las gramáticas ambiguas son valiosa utilidad en la programación de lenguajes, como ser en la especificación de expresiones ú optimizaciones de algunos casos especiales, Sin embargo, estando frente a construcciones con gramáticas ambiguas se deben especificar construcciones para eliminar dichas ambigüedades a fin de garantizar un solo árbol sintáctico para cada frase dada. La precedencia y asociatividad operadores durante la construcción de lenguajes nos aportan criterios, a fin de eliminar conflictos desplazar/reducir que podrían presentarse debido a ambigüedades en la gramática. La precedencia especifica el orden relativo de cada operador con respecto a los demás operadores. El operador de más precedencia se evalúa antes que el de menor precedencia. Estado Acción Goto c d $ S C 0 D36 D47 1 2 1 aceptar 2 D36 D47 5 36 D36 D47 89 47 R3 R3 R3 5 R1 89 R2 R2 R2
  • 72. 72 Operación: A#B&C (A#B)&C Si & < # A#(B&C) Si # > & La asociatividad de un operador binario define cómo se operan tres o más operándoos con dicho operador. Existen dos tipos de asociatividad y las mismas pueden ser por izquierda o por derecha, las cuales van ligadas al tipo de derivación que se esté utilizando. Operación: A#B#C#D. Asociatividad por izquierda: ((A#B)#C)#D. Asociatividad por derecha: A#(B#(C#D)) Desde el punto de vista de los lenguajes de programación: Si op1 < op2, entonces en op2 debe aparecer desplazar y en op1 reducir. Ambigüedad de los else-balanceados: Esto produce un conflicto desplazar/reducir cuando se dispone de una gramática una sentencia if-expr--then-sentencia-else-sentencia sin endif, para lo cual se propone adoptar la asociatividad del else con el ultimo then, de este modo se pasa el else a la pila y desplaza. Recuperación de errores: Los métodos de recuperación de errores en las gramáticas LR se basan en: Modo Panic: Descartar los símbolos de la entrada y/o sacar símbolos de la pila hasta alcanzar una situación válida para continuar el análisis (elimina el error y, probablemente, más información de entrada).
  • 73. 73 PROBLEMA: Decidir si se saca de la pila o se avanza en la entrada. Como solución sería posible la inserción de símbolos de sincronización. Consiste en saltar símbolos de la entrada hasta alcanzar algún símbolo de sincronización. PROBLEMA: Si se omiten algunos de los símbolos de sincronización se pueden perder demasiados símbolos de entrada. SOLUCIÓN: Optimizar los símbolos de sincronización en base a los símbolos seguidores e iniciales de cada símbolo no terminal de la gramática. Tabla de análisis: Consiste en completar las celdas vacías de la tabla de análisis con procedimientos para el tratamiento del error y recuperación del mismo para continuar con el análisis. Métodos complejos basados en el algoritmo de Graham- Rhodes: Permite recuperarse ante un error y además, en algunos casos, reparar el mismo. El método distingue dos fases: 1. Fase de condensación: donde se acota el contexto del error. 2. Fase de corrección: donde se intenta corregir el error. Basándose en el análisis de los símbolos próximos en el tope de la pila y en la entrada, la fase de corrección decide si hay que sacar de la pila o saltar
  • 74. 74 II.1.3. Análisis Semántico El análisis semántico se encarga de velar que lo que se está intentando realizar tiene un significado coherente, el mismo se encarga de la verificación de tipos de datos, compatibilidad de expresiones, ámbitos, flujos de controles y coherencia en entre parámetros. El analizador semántico trabaja en estrecha cooperación con el analizador sintáctico a fin de realizar su tarea y en ocasiones se lo encuentra subordinado al mismo. El traductor debe tener la suficiente capacidad de realizar comprobaciones estáticas y dinámicas, las cuales se resumen en: La comprobación estática se realiza durante la etapa de análisis (A. Léxico, A. Sintáctico, A. Semántico) y se encarga de la: - Comprobación de compatibilidad de tipos. - Comprobación de flujos de control. - Comprobación de unicidad. - Comprobaciones relacionadas con el nombre. En la traducción de una pasada, valiéndose de las gramáticas atribuidas, desde el analizador sintáctico se realizan las llamadas a las rutinas semánticas y dichas rutinas son las que finalmente llaman al generador de código, en cambio en traductores de dos o más pasadas, el análisis semántico se encuentra separado de la generación de código, comunicándose finalmente con este a través de un archivo intermedio, cuyo contenido se resume en el árbol sintáctico. Sea cual sea el número de pasadas las rutinas semánticas hacen uso de los registros semánticos, el cual tiene una forma de pila (la pila semántica), almacena información semántica asociada a los operándoos y en ocasiones también de los operadores.
  • 75. 75 Tabla de símbolos: La tabla de símbolos es una estructura de datos de alto rendimiento la cual almacena toda información relevante sobre los identificadores que de alguna u otra manera intervienen en el análisis de un programa de entrada. Algunas funciones de las funciones que desempeñan la tabla de símbolos son: 1. Realizar chequeos semánticos; Verificar que una frase tenga su antecesor y sucesor correspondiente y en forma correcta. 2. Controlar el ámbito de una variable o sentencia. Una tabla de símbolos es una estructura de dinámica, la cual habitualmente permanece en memoria solo en tiempo de ejecución, simplifican significativamente la tarea del análisis sintáctico, así como asistir al analizador semántico y en la generación de código, de ahí lo crítico de contar con una tabla de símbolos construida y manejada de forma eficiente, pues en gran parte de la compilación interviene la tabla de símbolos. Fuente: Elaboración propia, basado en Cueva 1998:26 Tabla de Símbolos Análisis Léxico Análisis Sintáctico Análisis Semántico Generación de Código Optimización de Código Figura 15 - Interacción de etapas con la Tabla de Símbolos.