Compiladores universidad educación ciencia tecnología
1. NIVERSIDAD METROPOLITANA DE EDUCACION, CIENCIA Y TECNOLOGÍA
MATERIA: COMPILADORES
1. COMPILADOR
Un compilador no es más que un traductor, es decir, un programa que nos permite
pasar información de un lenguaje a otro. Por ejemplo, un compilador de C nos permite
traducir ficheros escritos en lenguaje C a un lenguaje legible para la máquina
(ensambladora).
En este proceso de compilación no siempre es directo. Esto quiere decir que nos
permite que un lenguaje A lo queremos traducir a un lenguaje B, no siempre será
recomendable traducir directamente de A a B, si no que puede ser conveniente usar un
lenguaje intermedio (que llamaremos X) y entonces diseñaremos un traductor de A a X, y
otro de X a B. En este caso, el primer traductor recibe el nombre de front-end mientras
que el segundo se llama back-end. El lenguaje A es el lenguaje fuente y el lenguaje B es
el lenguaje objeto o destino.
En el caso de que el lenguaje fuente sea un lenguaje de programación de alto nivel y
el objeto sea un lenguaje de bajo nivel (ensamblador o código de máquina), a dicho
traductor se le denomina compilador. Un ensamblador es un compilador cuyo lenguaje
fuente es el lenguaje ensamblador. Un intérprete no genera un programa equivalente, sino
que toma una sentencia del programa fuente en un lenguaje de alto nivel y la traduce al
código equivalente y al mismo tiempo lo ejecuta, con la escasez de memoria de los
primeros ordenadores, se puso de moda el uso de intérpretes frente a los compiladores,
pues el programa fuente sin traducir y el intérprete juntos daban una ocupación de
memoria menor que la resultante de los compiladores.
Por ello los primeros ordenadores personales iban siempre acompañados de un
intérprete de BASIC (Spectrum, Commodore VIC-20, PC XT de IBM, etc.). La mejor
información sobre los errores por parte del compilador así como una mayor velocidad de
ejecución del código resultante hizo que poco a poco se impusieran los compiladores. Hoy
en día, y con los problemas de las memorias prácticamente resueltas, se puede hablar de
un gran predominio de los compiladores frente a los intérpretes, aunque intérpretes como
los incluidos a los navegadores de Internet para interpretar el código JVM de Java son la
gran excepción.
Ventajas de compilar frente a interpretar:
• Se compila una vez, se ejecutan varias veces.
• En bucles, la compilación genera código equivalente al bucle, pero interpretándolo se
traduce tantas veces una línea como veces se repite el bucle.
• El compilador tiene una visión global del programa, por lo que la información de
mensajes de error es más detallada.
• Ventajas del intérprete frente al compilador.
• Un intérprete necesita menos memoria que un compilador. En principio eran más
abundantes dado que los ordenadores tenían poca memoria.
• Permiten una mayor interactividad con el código en tiempo de desarrollo.
Un compilador no es un programa que funciona de manera aislada, sino que necesita
de otros programas para conseguir su objetivo, para obtener un programa ejecutable a
partir de un programa fuente en un lenguaje de alto nivel. Algunos de esos programas son
preprocesados, el linker, el depurador y el ensamblador. El preprocesado se ocupa
dependiendo del lenguaje de incluir ficheros, expandir macros, eliminar comentarios, y
otras tareas similares.
CISNERO, ADRIAN 1
JUSTINIANI, ALIKI
REINA DAVID
VON CHONG, ROLANDO
2. NIVERSIDAD METROPOLITANA DE EDUCACION, CIENCIA Y TECNOLOGÍA
MATERIA: COMPILADORES
Los linker se encargan de construir el fichero ejecutable añadiendo al fichero objeto
generado por el compilador las cabeceras necesarias y las funciones de librería utilizadas
por el programa fuente. Algunos se encargan de depurador permitiendo asi el compilador
ha generado adecuadamente el programa objeto, seguir paso a paso la ejecución de un
programa. Finalmente, muchos compiladores, en vez de generar código objeto, generan
un programa en lenguaje ensamblador que debe después convertirse en un ejecutable
mediante un programa ensamblador.
1.1. CARACTERÍSTICAS DE LOS COMPILADOR
Generalmente un compilador se divide en dos partes:
* Front End: parte que analiza el código fuente, comprueba su validez, genera el árbol de
derivación y rellena los valores de la tabla de símbolos. Parte que suele ser independiente
de las plataformas sistema operativo para el que funcionará.
* Back End: parte en donde se genera el código máquina exclusivo para una plataforma a
partir de lo analizado en el front end.
Por lo general el resultado del back end no puede ser ejecutado directamente, se
necesita pasar por un proceso de enlazado (linker).
Existen varios tipos de compiladores: Compiladores cruzados, Compiladores
optimizadores, Compiladores de una sola pasada, Compiladores de varias pasadas,
Compiladores JIT.
Para el uso de la informática como herramienta de ayuda a la medicina es una
realidad en auge. Desde hace algún tiempo muchas de las actividades humanas se han
basado en la repetición, en algunos cálculos y del mismo modo que se inventaron
operaciones matemáticas básicas para simplificarlas, surgió la necesidad de mejorar las
limitadas prestaciones que ofrece la mente del hombre para calcular, a medida que las
diversas ciencias se hicieron más complejas.
CISNERO, ADRIAN 2
JUSTINIANI, ALIKI
REINA DAVID
VON CHONG, ROLANDO
3. NIVERSIDAD METROPOLITANA DE EDUCACION, CIENCIA Y TECNOLOGÍA
MATERIA: COMPILADORES
Las primeras aplicaciones desarrolladas fueron dirigidas a resolver tareas (task-
oriented) centradas en ciertos servicios o departamentos, siendo sus típicos usuarios
secretarias o técnicos empleados para la introducción de datos, usándose sobre todo con
fines administrativos, análisis financieros, manejo de bases de datos, sistemas de
información de laboratorios o servicios de radiología.
Estas aplicaciones han tenido un gran impacto desde el punto de vista
administrativo o de organización, pero no están dirigidos a las tareas propiamente
médicas, aunque han contribuido a reducir el tiempo dedicado a acceder a este tipo de
datos.
Posteriormente el mayor beneficio para las actividades propiamente médica ha
sido la posibilidad de acceder a la bibliografía médica de una forma rápida mediante la
aparición de las bases de datos bibliográficos inicialmente en cederrón y actualmente a
través de INTERNET y en los últimos años, se intenta realizar aplicaciones informáticas
que vayan dirigidas a resolver problemas (problem-based), más cercanas a la práctica
médica, pero encontramos algunos problemas basados fundamentalmente en la ausencia
de comprensión de cual es el proceso que seguimos los médicos para tomar una decisión
y en la forma de representar el conocimiento y el proceso de razonamiento dentro del
ordenador.
En estos últimos años se han añadido nuevas aplicaciones informáticas a la
medicina, como son las publicaciones electrónicas biomédicas, la telemedicina, impulsada
con un gran auge de INTERNET y su World Wide Web y los registros informáticos de la
historia clínica. Hemos agregado sobre todo en sistemas privados por el pago por
servicio, que obliga a un gran detalle de las actuaciones médicas para identificar el costo
de beneficio, precisando crear registros informáticos de alta calidad.
La telemedicina es quizá, la que más ventajas aporta y con la que se puede
obtener reducción de costos, al poder establecer comunicación con lugares lejanos del
mundo, gracias a INTERNET y a los satélites de comunicación, evitando desplazamientos
innecesarios y comunicaciones rápidas entre médicos, otros miembros del sistema
sanitario e incluso los propios pacientes.
En la informática se ha introducido una nueva dimensión en el pensamiento
humano, haciendo reales los sueños. No obstante, las aplicaciones informáticas para el
manejo clínico del paciente siguen estando en el campo de los proyectos, con honrosas
excepciones, existiendo pocos sistemas que sean operativos de una forma generalizada.
Los avances en esta tecnología y en el desarrollo de redes de información van a
permitir una reducción de costos y un aumento en la calidad del cuidado de nuestros
pacientes. El cambio en la enseñanza de las ciencias médicas permitirá entender al
ordenador como una importante herramienta de trabajo, que además permitirá cambiar la
actual forma memorística e intuitiva de la actuación médica a una forma basada en una
CISNERO, ADRIAN 3
JUSTINIANI, ALIKI
REINA DAVID
VON CHONG, ROLANDO
4. NIVERSIDAD METROPOLITANA DE EDUCACION, CIENCIA Y TECNOLOGÍA
MATERIA: COMPILADORES
estructura con una mayor base de conocimientos, un proceso analítico de los mismos y
una mayor eficacia en la toma de decisiones.
El médico no solo quedará apoyado por el ordenador sino que se mantendrá liberado
de sus tareas más repetitivas y tediosas, pasará a ser el eje sobre el que se apoye todo el
soporte informático, al actuar como selector y controlador de los datos de entrada, creador
de los protocolos y algoritmos que éste seguirá, interpretando, además, sus resultados y
por lo tanto siendo el máximo responsable del sistema.
1.2. FASES DE LOS COMPILADOR
En el llamado análisis léxico, el compilador revisa y controla que las palabras estén
bien escritas y pertenezcan a algún tipo de token definido dentro del lenguaje, como por
ejemplo que sea algún tipo de palabra reservada, o si es el nombre de una variable que
este escrita de acuerdo a las pautas definidas del lenguaje. En esta etapa se crea la tabla
de símbolos, la cual contiene las variables y el tipo de dato al que pertenece, las
constantes literales, el nombre de funciones y los argumentos que reciben.
El análisis semántico se encarga de revisar que cada agrupación o conjunto de
token tenga sentido, y no sean muy absurdo. En esta etapa se reúne la información sobre
los tipos para la fase posterior, en la otra etapa se utilizara la estructura jerárquica de la
etapa anterior y así poder determinar los operadores, y operando de expresiones y
preposiciones.
En el análisis sintáctico como su nombre lo indica se encarga de revisar que los
tokens estén ubicados y agrupados de acuerdo a la definición del lenguaje. Dicho esto de
otra manera, que los tokens pertenezcan a frases gramaticales validas, que el compilador
utiliza para sintetizar la salida. Por lo general las frases gramaticales son representadas
por estructuras jerárquicas, por medio de árboles de análisis sintáctico. En esta se
completa la tabla de símbolos con la dimensión de los identificadores y los atributos
necesarios.
Generación de código intermedio, aunque algunos compiladores no la tienen, es
bueno saber que existen, en esta etapa se lleva el código del programa fuente a un
código interno para poder trabajar más fácilmente sobre él. Esta representación interna
debe tener dos propiedades, primero debe ser fácil de representar y segundo debe ser
fácil de traducir al código objeto.
Optimización de código, se busca obtener el código más corto y rápido posible,
utilizando distintos algoritmos de optimización.
Etapa de generación de código, se lleva el código intermedio final a código
maquina o código objeto, que por lo general consiste en un código maquina re localizable
o código ensamblador. Se selecciona las posiciones de memoria para los datos variables
y se traduce cada una de las instrucciones intermedias a una secuencia de instrucciones
de maquina puro.
CISNERO, ADRIAN 4
JUSTINIANI, ALIKI
REINA DAVID
VON CHONG, ROLANDO
5. NIVERSIDAD METROPOLITANA DE EDUCACION, CIENCIA Y TECNOLOGÍA
MATERIA: COMPILADORES
La tabla de símbolos no es una etapa del proceso de compilación, sino que una
tarea, una función que debe realizar el proceso de compilación. En ella se almacenan los
identificadores que aparecen en el código fuente puro, como así también los atributos de
los mismos, su tipo, su ámbito y en el caso de los procedimientos el número de
argumentos el tipo del mismo. En otras palabras una tabla de símbolos es una estructura
de datos, que contiene un registro por cada identificador, y sus atributos. La tabla de
símbolo es accedida tanto para escritura como parar lectura por todas las etapas.
Los detectores de errores o manejador de errores, al igual que la tabla de símbolos
no es una etapa del proceso de compilación, si no que es una función, muy importante,
pues al ocurrir un error esta función debe tratar de alguna forma el error para así seguir
con el proceso de compilación la mayoría de errores son detectados en las etapas de
análisis léxico, análisis sintáctico, análisis semántico.
1.3.
ESTRUCTURACION DE LOS COMPILADORES
La estructura de un compilador, está dividida en cuatro grandes módulos, cada
uno independiente del otro, se podría decir que un compilador está formado por cuatros
módulos más a su vez.
El primero de ellos es el preprocesador, es el
encargado de transformar el código fuente de entrada original
en el código fuente puro. Es decir en expandir las macros,
CISNERO, ADRIAN 5
JUSTINIANI, ALIKI
REINA DAVID
VON CHONG, ROLANDO
6. NIVERSIDAD METROPOLITANA DE EDUCACION, CIENCIA Y TECNOLOGÍA
MATERIA: COMPILADORES
incluir las librerías, realizar un preprocesado racional con la capacidad de enriquecer a
un lenguaje antiguo con recursos más modernos, extender el lenguaje y todo aquello
que en el código de entrada sea representativo de una abreviatura para facilitar la
escritura del mismo.
En el segundo modulo encontramos compilación que recibe el código fuente puro, este
es él modulo principal de un compilador, pues si ocurriera algún error en esta etapa el
compilador no podría avanzar. En esta etapa se somete al código fuente puro de
entrada a un análisis léxico gráfico, a un análisis sintáctico, a un análisis semántico, que
construyen la tabla de símbolos, se genera un código intermedio al cual se optimiza
para así poder producir un código de salida generalmente en algún lenguaje
ensamblador.
Para este tercer modulo es el llamado modulo de ensamblado, este modulo no
es ni más mi menos que otro compilador pues recibe un código fuente de entrada
escrito en ensamblador, y produce otro código de salida, llamado código binario no
enlazado. Si por un momento viéramos a este modulo como un programa
independiente, veríamos que en este caso los términos programa compilador y proceso
de compilación son los mismos.
Pues este modulo no es mas que un compilador, que en su interior realiza como
su antecesor un análisis léxico gráfico, un análisis sintáctico, un análisis semántico,
crea una tabla de símbolos, genera un código intermedio lo optimiza y produce un
código de salida llamado código binario no enlazado, y a todo este conjunto de tares se
los denomina proceso de compilación.
Como se puede ver este compilador (llamado ensamblador) a diferencia de los
demás compiladores no realiza una expansión del código fuente original (código fuente
de entrada), tiene solamente un proceso de compilación y por supuesto no enlaza el
código fuente. Es un compilador que carece de los módulos de preprocesado y
enlazado, y donde los módulos de compilación y ensamblado son los mismos.
CISNERO, ADRIAN 6
JUSTINIANI, ALIKI
REINA DAVID
VON CHONG, ROLANDO
7. NIVERSIDAD METROPOLITANA DE EDUCACION, CIENCIA Y TECNOLOGÍA
MATERIA: COMPILADORES
El cuarto y ultimo modulo es el encargado de realizar el enlazado del código de fuente
de entrada (código maquina re localizable) con las librerías que necesita, como así
también de proveer al código de las rutinas necesarias para poder ejecutarse y
cargarse a la hora de llamarlo para su ejecución, modifica las direcciones re
localizables y ubica los datos en las posiciones apropiadas de la memoria.
Este ultimo modulo es el que produce como salida el código binario enlazado.
Ya sea dinámico o estático, al decir dinámico se refiere a que el código producido utiliza
librerías dinámicas (librerías ya cargadas en el sistema), esto implica que se obtendrá
un código más corto y que se actualizara automáticamente si aparece alguna nueva
versión de las librerías, mientras que el estático se refiere al echo que no se realiza
enlace con ninguna librería y por lo tanto se obtendrá un código mas largo con una
copia de las rutinas de librería que necesita.
2. ELEMENTOS DE UN COMPILADOR
Elementos primarios
Elemento Descripción
Elemento Elemento raíz de cada archivo de configuración usado por las
<configuration> aplicaciones de Common Language Runtime y .NET Framework.
<system.codedom> Especifica las opciones de configuración del compilador para los
(Elemento) proveedores de lenguaje disponibles.
Elemento <compilers> Contenedor de elementos de configuración del compilador; contiene
CISNERO, ADRIAN 7
JUSTINIANI, ALIKI
REINA DAVID
VON CHONG, ROLANDO
8. NIVERSIDAD METROPOLITANA DE EDUCACION, CIENCIA Y TECNOLOGÍA
MATERIA: COMPILADORES
el cero o más elementos <compiler>.
Elementos secundarios
Elemento Descripción
<providerOption> Especifica los atributos de versión del compilador para un proveedor
(Elemento) de lenguaje.
2.1. TIPOS DE COMPILADORES
Esta taxonomía de los tipos de compiladores no es excluyente, por lo que puede
haber compiladores que se adscriban a varias categorías:
Compiladores cruzados: generan código para un sistema distinto del que están
funcionando.
Compiladores optimizadores: realizan cambios en el código para mejorar su eficiencia,
pero manteniendo la funcionalidad del programa original.
Compiladores de una sola pasada: generan el código máquina a partir de una única
lectura del código fuente.
Compiladores de varias pasadas: necesitan leer el código fuente varias veces antes de
poder producir el código máquina.
Compiladores JIT (Just In Time): forman parte de un intérprete y compilan partes del
código según se necesitan.
Pauta de creación de un compilador: En las primeras épocas de la informática, el
software de los compiladores era considerado como uno de los más complejos existentes.
Los primeros compiladores se realizaron programándolos directamente en
lenguaje máquina o en ensamblador. Una vez que se dispone de un compilador, se
pueden escribir nuevas versiones del compilador (u otros compiladores distintos) en el
lenguaje que compila ese compilador.
Actualmente existen herramientas que facilitan la tarea de escribir compiladores ó
intérpretes informáticos. Estas herramientas permiten generar el esqueleto del analizador
sintáctico a partir de una definición formal del lenguaje de partida, especificada
normalmente mediante una gramática formal y barata, dejando únicamente al
programador del compilador la tarea de programar las acciones semánticas asociadas
3. EMSAMBLADOR
CISNERO, ADRIAN 8
JUSTINIANI, ALIKI
REINA DAVID
VON CHONG, ROLANDO
9. NIVERSIDAD METROPOLITANA DE EDUCACION, CIENCIA Y TECNOLOGÍA
MATERIA: COMPILADORES
El ensamblador se refiere a un tipo de programa, informático que se encarga de
traducir un fichero fuente escrito en un lenguaje ensamblador, a un fichero objeto que
contiene código máquina ejecutable directamente por la máquina para la que se ha
generado.
Los lenguajes ensambladores fueron desarrollados hace muchos años atrás,
cuando fueron referidos como lenguajes de programación de segunda generación. Por
ejemplo, el SOAP (Symbolic Optimal Assembly Program) era un lenguaje ensamblador
para el computador IBM 650. Los lenguajes ensambladores eliminaron mucha de la
propensión a errores y del consumo de tiempo de la programación de los lenguajes de
primera generación que se necesitaban con los primeros computadores, liberando a los
programadores como recordar códigos numéricos y cálculo de direcciones.
Una vez fueron ampliamente usados para todo tipo de programación. Sin
embargo, los microcomputadores se usaban en gran parte suplantado por los lenguajes
de alto nivel, en la búsqueda de una mejorada productividad en programación. Hoy en
día, aunque el lenguaje ensamblador es casi siempre manejado y generado por los
compiladores, todavía se usa para la manipulación directa del hardware, acceso a
instrucciones especializadas del procesador, o para resolver problemas de desempeño
crítico. Los usos típicos son drivers de dispositivo, sistemas embebidos de bajo nivel, y
sistemas de tiempo real.
Un gran número de programas han sido escritos enteramente en lenguaje
ensamblador. Los sistemas operativos fueron casi exclusivamente escritos en lenguaje
ensamblador hasta la aceptación amplia del lenguaje de programación C. También,
muchas aplicaciones comerciales fueron escritas en lenguaje ensamblador, incluyendo
una gran cantidad del software escrito por grandes corporaciones para mainframes de
IBM. Los lenguajes COBOL y FORTRAN eventualmente desplazaron mucho de este
trabajo, aunque un número de organizaciones grandes conservaran las infraestructuras
de aplicaciones en lenguaje ensamblador.
La mayoría de los primeros microcomputadores confiaron en el lenguaje
ensamblador codificado a mano, incluyendo la mayoría de los sistemas operativos y de
las aplicaciones grandes. Esto era porque estos sistemas tenían limitaciones severas
de recursos, impusieron idiosincráticas arquitecturas de memoria y de pantalla que
proporcionaron servicios de sistema limitados con errores. Quizás más importante era
la falta de compiladores de primera clase de lenguajes de alto nivel adecuados para el
uso en el microcomputador.
En un contexto más comercial, las más grandes razones para usar el lenguaje
ensamblador era hacer programas con mínimo tamaño, mínima sobrecarga, mayor
velocidad y confiabilidad.
Los típicos ejemplos de programas grandes en lenguaje ensamblador de ese
tiempo son los sistemas operativos IBM PC DOS y aplicaciones tempranas tales como
la hoja de cálculo Lotus 1-2-3, y casi todos los juegos populares para la familia Atari
800 de computadores personales. La mayoría de los videojuegos de consola fueron
escritos en ensamblador, incluyendo la mayoría de los juegos para la Mega
Drive/Genesis y el Super Nintendo Entertainment System.
CISNERO, ADRIAN 9
JUSTINIANI, ALIKI
REINA DAVID
VON CHONG, ROLANDO
10. NIVERSIDAD METROPOLITANA DE EDUCACION, CIENCIA Y TECNOLOGÍA
MATERIA: COMPILADORES
Según algunos insiders de la industria, el lenguaje ensamblador era el mejor
lenguaje de programación a usar para obtener el mejor desempeño del Sega Saturn,
una consola para la cual era notoriamente desafiante desarrollar y programar juegos. El
popular juego de arcade NBA Jam es otro ejemplo. El ensamblador ha sido por largo
trecho, el lenguaje de desarrollo primario en los computadores hogareños Commodore
64, Atari ST, así como el ZX Spectrum.
Esto fue así en gran parte porque los dialectos del BASIC en estos sistemas
ofrecieron insuficiente velocidad de ejecución, así como insuficientes características
para aprovechar completamente el hardware disponible. Algunos sistemas, más
notablemente el Amigo, incluso tienen IDEs con características de depuración y macros
altamente avanzados, tales como el freeware ASM-One assembler, comparable a las
del Microsoft Visual Studio (el ASM-Uno precede al Microsoft Visual Studio).
El ensamblador para el VIC-20 fue escrito por Don French y publicado por
French Silk. Con 1639 bytes de longitud, se cree que es el más pequeño ensamblador
simbólico jamás escrito. El ensamblador soportaba el direccionamiento simbólico usual
y la definición de cadenas de caracteres o cadenas hexadecimales. También permitía
expresiones de direcciones que podían combinarse con las operaciones de adición,
substracción, multiplicación, división, AND lógico, OR lógico, y exponenciación.
Han habido siempre debates sobre la utilidad y el desempeño del lenguaje
ensamblador relativo a lenguajes de alto nivel. El lenguaje ensamblador tiene algunas
especificaciones donde son importante. Pero, en general, los modernos compiladores
de optimización para traducir lenguajes de alto nivel en el código que puede correr tan
rápidamente como el lenguaje ensamblador escrito a mano, a pesar de los contra
ejemplos que pueden ser encontrados. La complejidad de los procesadores modernos y
del subsistema de memoria hace la optimización efectiva cada vez más difícil para los
compiladores, así como para los programadores en ensamblador. Adicionalmente, y
para la consternación de los amantes de la eficiencia, el desempeño cada vez mayor
del procesador ha significado que la mayoría de los CPU estén desocupados la mayor
parte del tiempo, con retardos causados por embotellamientos predecibles tales como
operaciones de entrada/salida y paginación de memoria. Esto ha hecho la velocidad de
ejecución cruda del código un problema para muchos programadores.
Hay algunas situaciones en las cuales los profesionales pudieran elegir utilizar el
lenguaje ensamblador. Por ejemplo cuando:
• es requerido un ejecutable binario independiente (stand-alone), es decir uno que
deba ejecutarse sin recursos a componentes de tiempo de ejecución o a
bibliotecas asociadas con un lenguaje de alto nivel; ésta es quizás la situación más
común. Son programas empotrados que solo almacenan una pequeña cantidad de
memoria y el dispositivo está dirigido para hacer tareas para un simple propósito.
Ejemplos consisten en teléfonos, sistemas de combustible e ignición para
automóviles, sistemas de control del aire acondicionado, sistemas de seguridad, y
sensores.
• interactuando directamente con el hardware, por ejemplo en drivers de dispositivo
y manejadores de interrupción.
CISNERO, ADRIAN 10
JUSTINIANI, ALIKI
REINA DAVID
VON CHONG, ROLANDO
11. NIVERSIDAD METROPOLITANA DE EDUCACION, CIENCIA Y TECNOLOGÍA
MATERIA: COMPILADORES
• usando instrucciones específicas del procesador no explotadas o disponibles por
el compilador. Un ejemplo común es la instrucción de rotación bit wise en el núcleo
de muchos algoritmos de cifrado.
• creando funciones vectorizadas para programas en lenguajes de alto nivel como
C. En el lenguaje de alto nivel esto es a veces ayudado por funciones intrínsecas
del compilador que mapean directamente a los mnemónicos del SIMD, pero sin
embargo resulta en una conversión de ensamblador de uno a uno para un
procesador de vector asociado.
• es requerida la optimización extrema, en un bucle interno en un algoritmo intensivo
en el uso del procesador. Los programadores de juegos toman ventaja de las
habilidades de las características del hardware en los sistemas, permitiendo a los
juegos correr más rápidamente. También las grandes simulaciones científicas
requieren algoritmos altamente optimizados, ej, álgebra lineal con BLAS o la
transformada de coseno discreta la versión SIMD en ensamblador del x264, una
biblioteca para codificar streams de video.
• un sistema con severas limitaciones de recursos un sistema empotrado debe ser
codificado a mano para maximizar el uso de los limitados recursos; pero esto está
llegando a ser menos común a medida que el precio del procesador decrece y el
desempeño mejora.
• no existe ningún lenguaje de alto nivel, en un procesador nuevo o especializado.
• escribiendo programas de tiempo real que necesitan sincronización y respuestas
precisas, tales como sistemas de navegación de vuelo, y equipo médico. En un
sistema fly-by-wire, vuelo por mandos eléctricos, la telemetría debe ser
interpretada y hay que actuar dentro de limitaciones estrictas de tiempo. Tales
sistemas deben eliminar fuentes de retrasos impredecibles, que pueden ser
creados para algunos lenguajes interpretados, recolección de basura automática,
operaciones de paginación, o multitarea preventiva. Sin embargo, algunos
lenguajes de alto nivel incorporan componentes de tiempo de ejecución e
interfaces de sistema operativo que pueden introducir tales retrasos. Elegir el
ensamblador o lenguajes de bajo nivel para tales sistemas da a los programadores
mayor visibilidad y control sobre el proceso de los detalles.
• es requerido control total sobre el ambiente, en situaciones de seguridad
extremadamente alta donde nada puede darse por sentado.
• se escriben virus de computadora, bootloaders, ciertos drivers de dispositivo, u
otros elementos muy cerca del hardware o al sistema operativo de bajo nivel
• se escriben simuladores del conjunto de instrucciones para monitoreo, trazado y
depuración de errores donde la sobrecarga adicional es mantenida al mínimo
• se hace ingeniería inversa en binarios existentes que pueden o no haber sido
escritos originalmente en un lenguaje de alto nivel, por ejemplo al crackear la
protección anticopia del software propietario.
• se hace ingeniería inversa y modificación de video juegos, también denominado
ROM hacking, que es posible por medio de varios métodos. El más ampliamente
implementado es alterando el código del programa a nivel de lenguaje
ensamblador
• se escribe código automodificable, algo para lo que el lenguaje ensamblador se
presta bien
• se escriben juegos y otros softwares para calculadoras gráficas
• se escribe software compilador que genera código ensamblador, y por lo tanto los
desarrolladores deben ser programadores de lenguaje ensamblador.
• se escriben algoritmos criptográficos que siempre deben tomar estrictamente el
mismo tiempo para ejecutar, previniendo ataques de tiempo.
CISNERO, ADRIAN 11
JUSTINIANI, ALIKI
REINA DAVID
VON CHONG, ROLANDO
12. NIVERSIDAD METROPOLITANA DE EDUCACION, CIENCIA Y TECNOLOGÍA
MATERIA: COMPILADORES
Sin embargo, el lenguaje ensamblador es todavía enseñado en la mayoría de los
programas de ciencias de la computación e ingeniería electrónica. Aunque hoy en día,
pocos programadores trabajan regularmente con el lenguaje ensamblador como una
herramienta, los conceptos fundamentales continúan siendo muy importantes.
Tales tópicos fundamentales, como aritmética binaria, asignación de memoria,
procesamiento del stack, codificación de conjunto de caracteres, procesamiento de
interrupciones, y diseño de compiladores, serían duros de estudiar en detalle sin la
comprensión de cómo el computador opera a nivel del hardware. Puesto que el
comportamiento del computador es fundamentalmente definido por su conjunto de
instrucciones, la manera lógica de aprender tales conceptos es estudiar un lenguaje
ensamblador.
La mayoría de los computadores modernos tienen un conjunto de instrucciones
similares. Por lo tanto, estudiar un solo lenguaje ensamblador es suficiente para aprender:
i) los conceptos básicos; ii) reconocer situaciones donde el uso de lenguaje ensamblador
puede ser apropiado; y iii) ver cómo el código ejecutable eficiente puede ser creado por
los lenguajes de alto nivel18
El lenguaje ensamblador hard-coded es típicamente usado en el ROM de arranque
del sistema (BIOS en los sistemas compatible IBM PC). Este código de bajo nivel es
usado, entre otras cosas, para inicializar y probar el hardware del sistema antes de cargar
el sistema operativo, y está almacenado en el ROM. Una vez que ha tomado lugar un
cierto nivel de inicialización del hardware, la ejecución se transfiere a otro código,
típicamente escrito en lenguajes de alto nivel; pero el código corriendo inmediatamente
después de que es aplicada la energía usualmente está escrito en lenguaje ensamblador.
Lo mismo es cierto para los boot loaders.
Muchos compiladores traducen lenguajes de alto nivel a lenguaje ensamblador
primero, antes de la compilación completa, permitiendo que el código en ensamblador sea
visto para propósitos de depuración y optimización. Lenguajes de relativo bajo nivel, como
C, con frecuencia proveen sintaxis especial para empotrar lenguaje ensamblador en cada
plataforma de hardware. El código portable del sistema entonces puede usar estos
componentes específicos a un procesador a través de una interface uniforme.
El lenguaje ensamblador también es valioso en ingeniería inversa, puesto que
muchos programas solamente son distribuidos en una forma de código de máquina. El
código de máquina es usualmente fácil de trasladar hacia lenguaje ensamblador para
luego ser cuidadosamente examinado en esta forma, pero es muy difícil de trasladar hacia
un lenguaje de alto nivel. Herramientas como Interactive Disassembler, hacen uso
extenso del desensamblador para tales propósitos.
Un nicho que hace uso del lenguaje ensamblador es el demoscene. Ciertas
competiciones requieren a los concursantes restringir sus creaciones a un muy pequeño
tamaño 256 bytes, 1 KB, 4 KB ó 64 KB, y el lenguaje ensamblador es el lenguaje de
preferencia para alcanzar este objetivo.
Cuando los recursos son una preocupación, es una necesidad la codificación en
ensamblador, especialmente en sistemas constreñidos por el procesamiento del CPU,
como los primeros modelos del Amiga, y el Commodore 64. El código optimizado en
CISNERO, ADRIAN 12
JUSTINIANI, ALIKI
REINA DAVID
VON CHONG, ROLANDO
13. NIVERSIDAD METROPOLITANA DE EDUCACION, CIENCIA Y TECNOLOGÍA
MATERIA: COMPILADORES
ensamblador es escrito "a mano" por los programadores en un intento de minimizar el
número de ciclos de CPU usados. Las limitaciones del CPU son tan grandes que cada
ciclo cuenta. Usar tales métodos ha habilitado, a sistemas como el Commodore 64, para
producir gráficos en 3D en tiempo real con efectos avanzados, una hazaña que puede ser
considerada improbable o incluso imposible para un sistema con un procesador de 0.99
MHz.
4. ÁRBOLES PARTE SEMANTICAS.
Hemos visto que las deducciones pueden hacerse atendiendo a los problemas de
derivación, realizándose esta última a través de la aplicación de las reglas básicas o
derivadas.
Pero también podemos utilizar otro criterio: el semántico, según el cual, y
suponiendo que la deducción sea correcta, no podemos obtener una conclusión falsa de
premisas verdaderas. La semántica atiende por una parte al hecho al que se refiere la
proposición y, por otra parte, a su valor de verdad.
El método de las tablas semánticas supone una búsqueda de contraejemplos que
invaliden el argumento. Es una especie de reducción al absurdo, en la que se supone la
verdad de la negación de la conclusión y, a partir de ella, se llega a una contradicción.
CISNERO, ADRIAN 13
JUSTINIANI, ALIKI
REINA DAVID
VON CHONG, ROLANDO
14. NIVERSIDAD METROPOLITANA DE EDUCACION, CIENCIA Y TECNOLOGÍA
MATERIA: COMPILADORES
CISNERO, ADRIAN 14
JUSTINIANI, ALIKI
REINA DAVID
VON CHONG, ROLANDO
15. NIVERSIDAD METROPOLITANA DE EDUCACION, CIENCIA Y TECNOLOGÍA
MATERIA: COMPILADORES
4.1. COMO SE IDENTIFICAN
El analizador semántico verificara que en este caso cada operador tenga los
operadores permitidos.
=
/
id1 +
/
id2 +
/
id3 tipo_ent
|
10
5. GRAMATICA BNF
Una especificación de BNF es un sistema de reglas de derivación, escrito como:
<simbolo> ::= <expresión con símbolos>
donde <símbolo> es un no terminal, y la expresión consiste en secuencias de símbolos o
secuencias separadas por la barra vertical, '|', indicando una opción, el conjunto es una
posible substitución para el símbolo a la izquierda. Los símbolos que nunca aparecen en
un lado izquierdo son terminales.
Ejemplo
Como ejemplo, considere este BNF para una dirección postal de los EE.UU.
<dirección postal> ::= <nombre> <dirección> <apartado postal>
<personal> ::= <primer nombre> | <inicial> "."
<nombre> ::= <personal> <apellido> [<trato>] <EOL>
| <personal> <nombre>
<dirección> ::= [<dpto>] <número de la casa> <nombre de la calle> <EOL>
<apartado postal> ::= <ciudad> "," <código estado> <código postal> <EOL>
Esto se traduce a español como:
• Una dirección postal consiste en un nombre, seguido por una dirección, seguida
por un apartado postal.
• Una parte "personal" consiste en un nombre o una inicial seguido(a) por un punto.
• Un nombre consiste de: una parte personal seguida por un apellido seguido
opcionalmente por una jerarquía o el trato que se la da a la persona (Jr., Sr., o
número dinástico) y un salto de línea (end-of-line), o bien una parte personal
seguida por un nombre (esta regla ilustra el uso de la repetición en BNFs,
cubriendo el caso de la gente que utiliza múltiples nombres y los nombres medios
o las iniciales).
CISNERO, ADRIAN 15
JUSTINIANI, ALIKI
REINA DAVID
VON CHONG, ROLANDO
16. NIVERSIDAD METROPOLITANA DE EDUCACION, CIENCIA Y TECNOLOGÍA
MATERIA: COMPILADORES
• Una dirección consiste de una especificación opcional del departamento, seguido
de un número de casa, seguido por el nombre de la calle, seguido por un salto de
línea (end-of-line).
• Un apartado postal consiste de una ciudad, seguida por una coma, seguida por un
código del estado (recuerde que es un ejemplo que ocurre en EE.UU.), seguido
por un código postal y este seguido por un salto de línea (end-of-line).
Observe que muchas cosas (tales como el formato de una parte personal, de una
especificación del apartamento, o código postal) están dejadas sin especificar aquí. Si es
necesario, pueden ser descritas usando reglas adicionales de BNF, o dejadas como
abstracción si es inaplicable para el propósito actual.
Otros ejemplos
Bastante interesante, la sintaxis de BNF se puede representar en BNF como sigue:
<syntax> ::= <rule> [<syntax>]
<rule> ::= <whitespace> "<" <rule-name> ">" <whitespace> "::="
<expression> <whitespace> <line-end>
<expression> ::= <whitespace> <or-expression>
<or-expression> ::= <whitespace> <list-expression> [ "|"
<or-expression> ]
<list-expression> ::= <whitespace> ("<" <rule-name> ">"
| <QUOTE> <text> <QUOTE> | "(" <expression> ")" | "[" <expression>
"]") [<list-expression>]
<whitespace> ::= [" " <whitespace>]
<line-end> ::= [<whitespace>] <EOL> [<line-end>]
Esto asume que no hay Whitespace necesario para la interpretación apropiada de
la regla. El <QUOTE> se presume para ser el carácter ", y el <EOL> para ser el fin de
línea apropiado especificado (en ASCII, retorno de carro o línea nueva, dependiendo del
sistema operativo). El <rule-name> y el <text> deben ser substituidos con nombre/etiqueta
o el texto literal de una regla declarada, respectivamente.
Hay muchas variantes y extensiones de BNF, posiblemente conteniendo algunos o
todos los comodines de expresiones regulares como un "*" o "+". El Extended Backus-
Naur form (EBNF) es una variante común. De hecho el ejemplo anterior no es la forma
pura inventada para el informe del ALGOL 60. La notación de los corchetes "[ ]" fue
introducida algunos años más tarde en la definición de PL/I de la IBM pero ahora se
reconoce universal. La ABNF es otra extensión usada comúnmente para describir
protocolos del IETF.
Las expresiones gramaticales de analizadores sintácticos construidas en BNF y las
notaciones de expresión regular para formar una clase alternativa de la gramática formal,
que es esencialmente analítica más que generativa en carácter.
Muchas especificaciones de BNF disponibles en línea tienen como propósito ser
legibles a simple vista y no son especificaciones formales. Estas incluyen con frecuencia
algunas de estas reglas sintácticas y extensiones:
CISNERO, ADRIAN 16
JUSTINIANI, ALIKI
REINA DAVID
VON CHONG, ROLANDO
17. NIVERSIDAD METROPOLITANA DE EDUCACION, CIENCIA Y TECNOLOGÍA
MATERIA: COMPILADORES
• Elementos opcionales son presentados entre paréntesis cuadrados. Por ejemplo
[<elemento-x>]
• Los elementos que se repiten 0 o más veces son presentados entre paréntesis de
llave o terminados con un asterisco. Por ejemplo <palabra> ::= <letra> {<letra>}
• Los elementos que se repiten 1 o más veces son terminados con un '+'
• Los terminales pueden aparecer en negrillas y los no-terminales en texto normal
en lugar de utilizar itálicas o paréntesis de ángulo
• Alternativas opcionales son separadas por el símbolo '|'
Cuando se requiere agrupar varios elementos, se hace con paréntesis simple
5.1. SU DEFINICION
La notación de Backus-Naur, también conocida por sus denominaciones inglesas
Backus-Naur form (BNF), Backus-Naur formalism o Backus normal form, es una
metasintaxis usada para expresar gramáticas libres de contexto: es decir, una manera
formal de describir lenguajes formales.
El BNF se utiliza extensamente como notación para las gramáticas de los
lenguajes de programación de la computadora, de los sistemas de comando y de los
protocolos de comunicación, así como una notación para representar partes de las
gramáticas de la lengua natural (por ejemplo, el metro en la poesía de Venpa). La mayoría
de los libros de textos para la teoría o la semántica del lenguaje de programación
documentan el lenguaje de programación en BNF.
Algunas variantes, tales como la Augmented Backus-Naur form (ABNF) y la
Extended Backus–Naur Form (EBNF), tienen su propia documentación.
Con la aparición de la notación BNF - desarrollada en primer lugar por Backus
en 1960 cuando trabajaba en un borrador del ALGOL 60, modificada en 1963 por Naur
y formalizada por Knuth en 1964 - se tiene una guía para el desarrollo del análisis
sintáctico. Los diversos métodos de parsers ascendentes y descendentes se
desarrollan durante la década de los 60. En 1959, Sheridan describe un método de
parsing de FORTRAN que introducía paréntesis adicionales alrededor de los operandos
para ser capaz de analizar las expresiones. Más adelante, Floyd introduce la técnica de
la precedencia de operador y el uso de las funciones de precedencia.
A mitad de la década de los 60, Knuth define las gramáticas LR y describe la
construcción de una tabla canónica de parser LR. Por otra parte, el uso por primera vez
de un parsing descendente recursivo tuvo lugar en el año 1961. En el año 1968 se
estudian y definen las gramáticas LL así como los parsers predictivos. También se
estudia la eliminación de la recursión a la izquierda de producciones que contienen
acciones semánticas sin afectar a los valores de los atributos.
En los primeros años de la década de los 70, se describen los métodos SLR y
LALR de parser LR. Debido a su sencillez y a su capacidad de análisis para una gran
variedad de lenguajes, la técnica de parsing LR va a ser la elegida para los
generadores automáticos de parsers. A mediados de los 70, Johnson crea el generador
CISNERO, ADRIAN 17
JUSTINIANI, ALIKI
REINA DAVID
VON CHONG, ROLANDO
18. NIVERSIDAD METROPOLITANA DE EDUCACION, CIENCIA Y TECNOLOGÍA
MATERIA: COMPILADORES
de analizadores sintácticos YACC para funcionar bajo un entorno UNIX . Junto al
análisis sintáctico, también se fue desarrollando el análisis semántico.
La idea de transcribir la estructura del lenguaje con reglas de reescritura se
remontan cuando menos al trabajo del gramático indio Panini (hacia el 460 a. C.), que la
utilizó en su descripción de la estructura de palabras del idioma sánscrito (algunos incluso
han sugerido renombrar BNF a Forma Panini-Backus). Lingüïstas estadounidenses como
Leonard Bloomfield y Zellig Harris llevaron esta idea un paso más adelante al tratar de
formalizar el lenguaje y su estudio en términos de definiciones formales y procedimientos
(1920-1960).
Noam Chomsky, maestro de lingüística de alumnos de teoría de la información del
MIT, combinó la lingüística y las matemáticas, tomando esencialmente el formalismo de
Axel Thue como la base de su descripción de la sintaxis del lenguaje natural. También
introdujo una clara distinción entre reglas generativas (de la gramática libre de contexto) y
reglas transformativas (1956).
John Backus, un diseñanor de lenguajes de programación de IBM, adoptó las
reglas generativas de Chomsky para describir la sintaxis del nuevo lenguaje de
programación IAL, conocido en la actualidad como ALGOL 58 (1959), presentando en el
primer Congreso de Computación Mundial (World Computer Congress) el artículo "The
syntax and semantics of the proposed international algebraic language of the Zurich ACM-
GAMM Conference".
Peter Naur, en su reporte sobre ALGOL 60 de 1963, identificó la notación de
Backus como la Forma Normal de Backus (Backus Normal Form), y la simplificó para
usar un conjunto de símbolos menor, pero a sugerencia de Donald Knuth, su apellido fue
agregado en reconocimiento a su contribución, reemplazando la palabra "Normal" por
Naur, dado que no se trata de una forma normal en ningún sentido, a diferencia, por
ejemplo de la Forma Normal de Chomsky.
6. LAS MAQUINAS DE TURING
Una máquina de Turing (MT) es un modelo computacional que realiza una lectura/
escritura de manera automática sobre una entrada llamada cinta, generando una salida
en esta misma.
Este modelo está formado por un alfabeto de entrada y uno de salida, un símbolo
especial llamado blanco (normalmente b, Δ o 0), un conjunto de estados finitos y un
conjunto de transiciones entre dichos estados. Su funcionamiento se basa en una función
de transición, que recibe un estado inicial y una cadena de caracteres (la cinta, la cual
puede ser infinita) pertenecientes al alfabeto de entrada.
La máquina va leyendo una celda de la cinta en cada paso, borrando el símbolo
en el que se encuentra posicionado su cabezal y escribiendo un nuevo símbolo
perteneciente al alfabeto de salida, para luego desplazar el cabezal a la izquierda o a la
derecha (solo una celda a la vez). Esto se repite según se indique en la función de
transición, para finalmente detenerse en un estado final o de aceptación, representando
así la salida.
CISNERO, ADRIAN 18
JUSTINIANI, ALIKI
REINA DAVID
VON CHONG, ROLANDO
19. NIVERSIDAD METROPOLITANA DE EDUCACION, CIENCIA Y TECNOLOGÍA
MATERIA: COMPILADORES
La entrada de una máquina de Turing viene determinada por el estado actual y el
símbolo leído, un par (estado, símbolo), siendo el cambio de estado, la escritura de un
nuevo símbolo y el movimiento del cabezal, las acciones a tomar en función de una
entrada. En el caso de que para cada par (estado, símbolo) posible exista a lo sumo una
posibilidad de ejecución, se dirá que es una máquina de Turing determinista, mientras que
en el caso de que exista al menos un par (estado, símbolo) con más de una posible
combinación de actuaciones se dirá que se trata de una máquina de Turing no
determinista.
La función de transición δ en el caso no determinista, queda definida como sigue:
¿Cómo sabe una máquina no determinista qué acción tomar de las varias
posibles? Hay dos formas de verlo: una es decir que la máquina es "el mejor adivino
posible", esto es, que siempre elige la transición que finalmente la llevará a un estado final
de aceptación. La otra es imaginarse que la máquina se "clona", bifurcándose en varias
copias, cada una de las cuales sigue una de las posibles transiciones. Mientras que una
máquina determinista sigue un único "camino computacional", una máquina no
determinista tiene un "árbol computacional". Si cualquiera de las ramas del árbol finaliza
en un estado de aceptación, se dice que la máquina acepta la entrada.
La capacidad de cómputo de ambas versiones es equivalente; se puede demostrar
que dada una máquina de Turing no determinista existe otra máquina de Turing
determinista equivalente, en el sentido de que reconoce el mismo lenguaje, y viceversa.
No obstante, la velocidad de ejecución de ambos formalismos no es la misma, pues si una
máquina no determinista M reconoce una cierta palabra de tamaño
n en un tiempo , la máquina determinista equivalente reconocerá la palabra en
un tiempo . Es decir, el no determinismo permitirá reducir la complejidad de la
solución de los problemas, permitiendo resolver, por ejemplo, problemas de complejidad
exponencial en un tiempo polinómico.
Una máquina de Turing con una sola cinta puede definirse como una 7-tupla
donde:
CISNERO, ADRIAN 19
JUSTINIANI, ALIKI
REINA DAVID
VON CHONG, ROLANDO
20. NIVERSIDAD METROPOLITANA DE EDUCACION, CIENCIA Y TECNOLOGÍA
MATERIA: COMPILADORES
• es un conjunto finito de estados.
• es un conjunto finito de símbolos distinto del espacio en blanco, denominado
alfabeto de máquina o de entrada.
• es un conjunto finito de símbolos de cinta, denominado alfabeto de cinta (
).
• es el estado inicial.
• es un símbolo denominado blanco, y es el único símbolo que se puede
repetir un número infinito de veces.
• es el conjunto de estados finales de aceptación.
• es una función parcial denominada función de
transición, donde es un movimiento a la izquierda y es el movimiento a la
derecha.
Existen en la literatura un abundante número de definiciones alternativas, pero todas
ellas tienen el mismo poder computacional, por ejemplo se puede añadir el símbolo
como símbolo de "no movimiento" en un paso de cómputo.
7. ANALISIS SINTACTICO.
Como ya sabemos la sintaxis en lógica es la forma correcta de escribir una fórmula
y la semántica es lo que significa. Como en lógica solamente tenemos dos valores una
fórmula solamente puede ser verdadera o falsa. Para determinar su valor seguimos las
reglas simples que dimos en las definiciones básicas de acuerdo a su tabla de verdad.
Esto lo hacemos mediante interpretaciones. Una interpretación de una fórmula es un
conjunto de valores que se les asignan a sus proposiciones atómicas.
Al interpretar una fórmula lo que finalmente vamos a obtener es un valor de
verdad, bien sea verdadero o falso. Pero para poder encontrarlo muchas veces el proceso
en laborioso porque puede estar formada por varias proposiciones atómicas.
Primeramente se le asignan valores de verdad a los átomos y se puede encontrar el valor
de la expresión.
Si deseamos hacerlo en general, debemos analizar todas las posibilidades, esto se
puede hacer construyendo una tabla de verdad. Para fines prácticos cuando se tienen
varios átomos las tablas de verdad no resultan prácticas por lo que analizaremos
solamente expresiones con tres átomos como máximo.
Por supuesto que se puede construir una tabla para un número mayor de átomos,
pero notemos que por cada átomo que se aumente el número de renglones se duplica.
CISNERO, ADRIAN 20
JUSTINIANI, ALIKI
REINA DAVID
VON CHONG, ROLANDO
21. NIVERSIDAD METROPOLITANA DE EDUCACION, CIENCIA Y TECNOLOGÍA
MATERIA: COMPILADORES
Esto es, para un átomos son dos renglones, para dos átomos son cuatro, para tres
átomos son ocho, para cuatro dieciséis, etc.
Algoritmo para construir una tabla de verdad de una fórmula en lógica de
proposiciones.
Escribir la fórmula con un número arriba de cada operador que indique su
jerarquía. Se escriben los enteros positivos en orden, donde el número 1 corresponde al
operador de mayor jerarquía. Cuando dos operadores tengan la misma jerarquía, se le
asigna el número menor al de la izquierda.
Construir el árbol sintáctico empezando con la fórmula en la raíz y utilizando en
cada caso el operador de menor jerarquía. O sea, del número mayor al menor.
Numerar las ramas del árbol en forma secuencial empezando por las hojas hacia
la raíz, con la única condición de que una rama se puede numerar hasta que estén
numerados los hijos. Para empezar con la numeración de las hojas es buena idea hacerlo
en orden alfabético, así todos obtienen los renglones de la tabla en el mismo orden para
poder comparar resultados.
Escribir los encabezados de la tabla las fórmulas siguiendo la numeración que se
le dió a las ramas en el árbol sintáctico.
Al asignarle a los átomos, las hojas del árbol, todos los posibles valores de verdad
de acuerdo al orden establecido. Por supuesto que el orden es arbitrario, pero como el
número de permutaciones es n!, conviene establecer un orden para poder comparar
resultados fácilmente.
Asignar valor de verdad a cada una de las columnas restantes de acuerdo al
operador indicado en el árbol sintáctico utilizando la tabla de verdad correspondiente,
conviene aprenderse de memoria las tablas de los operadores, al principio pueden tener
un resumen con todas las tablas mientras se memorizan.
La última columna, correspondiente a la fórmula original, es la que indica los
valores de verdad posibles de la fórmula para cada caso.
Ejemplo. Construya la tabla de verdad de las siguientes expresiones lógicas:
i) (p → ¬q) v (¬p v r)
ii) p → (q ^ r)
CISNERO, ADRIAN 21
JUSTINIANI, ALIKI
REINA DAVID
VON CHONG, ROLANDO
22. NIVERSIDAD METROPOLITANA DE EDUCACION, CIENCIA Y TECNOLOGÍA
MATERIA: COMPILADORES
iii) (p → ¬ r) ↔ (q v p)
iv) ¬(p ¬ q) → ¬ r
v) (¬p ^ q) → ¬(q v ¬r)
Solución:
i) Seguimos los pasos del algoritmo con la fórmula (p → ¬q) v (¬p v r)
1. Vemos que los operadores de los paréntesis tienen mayor jerarquía, empezamos por el
paréntesis izquierdo por lo que la fórmula con jerarquías marcadas sería:
7.1. LIMITACION
El análisis semántico toma su nombre por requerir información relativa al significado del
lenguaje, fuera del alcance del análisis sintáctico.
Mediante el empleo de gramáticas sensibles al contexto tipo 1 se podrían comprobar
características del lenguaje. Ejemplo: la utilización de una variable requiere que esté
declarada previamente.
Un autómata de tipo 1 es complejo de implementar e ineficiente.
Solución: Análisis semántico a partir de gramáticas y autómatas de tipo 2 sintáctico e
implementación de reglas semánticas del lenguaje.
Se emplean estructuras de datos auxiliares como las tablas de símbolos.
El análisis semántico realiza todas las comprobaciones del lenguaje necesarias y no
llevadas a cabo por el sintáctico
7.2. COMO SE UTILIZA
El analizador sintáctico impone una estructura jerárquica a la cadena de
componentes léxicos, generada por el analizador léxico, que es representada en forma de
un árbol sintáctico.
=
/
id1 +
/
id2 +
/
id3 10
CISNERO, ADRIAN 22
JUSTINIANI, ALIKI
REINA DAVID
VON CHONG, ROLANDO
23. NIVERSIDAD METROPOLITANA DE EDUCACION, CIENCIA Y TECNOLOGÍA
MATERIA: COMPILADORES
7.3. DESCENDENTE Y ASCENDENTE
El estudio de gramáticas atribuidas se basa en la distinción de atributos heredados y
sintetizados.
Determinará cómo podremos implementarlas.
Sintetizados: se calculan ascendentemente en el árbol sintáctico.
Heredados: cálculo descendente.
8. ANALISIS LEXICO
Analizador léxico (scanner): lee la secuencia de caracteres del programa fuente,
carácter a carácter, y los agrupa para formar unidades con significado propio, los
componentes léxicos (tokens en ingles). Estos componentes léxicos representan:
palabras reservadas: if, while, do, . . .identificadores: asociados a variables, nombres de
funciones, tipos definidos por el usuario, etiquetas,... Por ejemplo: posición, velocidad,
tiempo, . . . operadores: = * + - / == > < & ! = . . . símbolos especiales: ; ( ) [ ] f g ...
constantes numéricas: literales que representan valores enteros, en coma dotante, etc,
982, 0xF678, -83.2E+2,... constantes de caracteres: literales que representan cadenas
concretas de caracteres, hola mundo",...
El analizador léxico opera bajo petición del analizador sintáctico devolviendo un
componente léxico conforme el analizador sintáctico lo va necesitando para avanzar en la
gramática. Los componentes léxicos son los símbolos terminales de la gramática.
Suele implementarse como una subrutina del analizador sintáctico. Cuando recibe
la orden obtiene el siguiente componente léxico, el analizador léxico lee los caracteres de
entrada hasta identificar el siguiente componente lexico.analizador
El analizador léxico lee los caracteres del programa fuente, y verifica que
correspondan a una secuencia lógica (identificador, palabra reservada etc.). Esta
CISNERO, ADRIAN 23
JUSTINIANI, ALIKI
REINA DAVID
VON CHONG, ROLANDO
24. NIVERSIDAD METROPOLITANA DE EDUCACION, CIENCIA Y TECNOLOGÍA
MATERIA: COMPILADORES
secuencia de caracteres recibe el nombre componente léxico o lexema. En este caso el
analizador léxico verifica si el identificador id1 (nombre interno para "suma") encontrado
se halla en la tabla de símbolos, si no esta produce un error porque todavía no fue
declarado, si la preposición hubiese sido la declaración del identificador "suma" en
lenguajes C, C++ (int suma;) el analizador léxico agregaría un identificador en la tabla de
símbolos, y así sucesivamente con todos los componentes léxicos que aparezcan.
id1= id2+ id3 * 10
8.1. BUFFER DE ENTRADA
El proceso de lectura de los caracteres de la entrada y formar los componentes
léxicos es lo mas costoso en tiempo en el proceso de traducción. Es importante
implementarlo eficientemente.
Se utiliza un buffer dividido en dos mitades de tamaño N caracteres, donde N es
un bloque de disco (1024, 4096). Se leen N caracteres de la entrada en cada mitad del
buffer con una única orden de lectura. Se mantienen dos apuntadores. Uno marca el inicio
del lexema y el otro el carácter actual que se mueve hasta encontrar una subcadena que
corresponde con un patrón. Una vez reconocido un componente léxico ambos
apuntadores se colocan en la misma posición y justo detrás del lexema reconocido.
buffer de entrada
if avanza est´a final primera mitad then
recargar segunda mitad;
avanza = avanza+1;
else if avanza est´a final segunda mitad then
recargar primera mitad;
pasar avanza a principio primera mitad;
else avanza = avanza+1;
8.2. LAS EXPRESIONES REGULARES
Expresiones Regulares:
Los componentes léxicos se especifican haciendo uso de expresiones regulares. Además
de las tres operaciones básicas: concatenación, repetición (*) y alternativas (j) vamos a
usar los siguientes meta símbolos:
Una o mas repeticiones +
r+ indica una o mas repeticiones de r
(0j1)+ = (0j1)(0j1)*
Cualquier carácter.
.*b.* indica cualquier cadena que contiene una letra b
Un rango de caracteres [ ] (clase)
[a-z] indica cualquier carácter entre la a y z minúsculas
[a-zA-Z] indica cualquier letra del abecedario minúscula o mayúscula
[0-9] indica cualquier digito de 0 a 9
[abc] indica ajbjc
Cualquier caracter excepto un conjunto dado ~
CISNERO, ADRIAN 24
JUSTINIANI, ALIKI
REINA DAVID
VON CHONG, ROLANDO
25. NIVERSIDAD METROPOLITANA DE EDUCACION, CIENCIA Y TECNOLOGÍA
MATERIA: COMPILADORES
~ (ajb) indica cualquier caracter que no sea una a o b
Opcionalidad ?
r? indica que la expresion r puede aparecer o no. En el caso de
que aparezca solo lo haria una vez.
Cuando queremos usar estos simbolos con su significado ten-
emos que usar la barra de escape, [an-z] significar a cualquier letra
que sea a, guion o z.
Algunos ejemplos:
Numeros
nat = [0-9]+
signedNat = ( + j -)? nat
number = signedNat(‘‘.’’nat)? (E signedNat)?
Identi¯cadores
letter = [a-zA-Z]
digit = [0-9]
8.3. LAS EXPRESIONES DE MAQUINAS FINITAS.
Los AFD se pueden utilizar para reconocer las expresiones regulares asociadas a los
componentes léxicos.
Identificadores
identifier = letter (letter j digit)*
Números naturales y reales
nat = [0-9]+
signedNat = (-)? nat
number = signedNat(‘‘.’’nat)? (E signedNat)?
CISNERO, ADRIAN 25
JUSTINIANI, ALIKI
REINA DAVID
VON CHONG, ROLANDO