Vous êtes sur la page 1sur 47

INSTITUTO TECNOLOGICO SUPERIOR

DE ALVARADO

INGENIERÍA EN SISTEMAS COMPUTACIONALES

MATERIA:

LENGUAJES DE AUTÓMATAS II

SEMESTRE-GRUPO:

NOVENO SEMESTRE

PRODUCTO ACADÉMICO:

ANTOLOGÍA LENGUAJES DE AUTÓMATAS II

PRESENTA:

RAMON PRIETO CANO

DOCENTE:

ALFONSO ROSAS ESCOBEDO

H. Y G. ALVARADO, VER. ENE-JUN 2018


ANTOLOGIA
Instituto Tecnológico Superior de Alvarado

INDICE

INTRODUCCION.......................................................................................................................1

PROPOSITO Y CONTENIDO..................................................................................................2

OBJETIVO.................................................................................................................................4

COMPETENCIAS PREVIAS Y RELACION CON CURSOS ANTERIORES Y

POSTERIORES.........................................................................................................................4

CONTENIDO..............................................................................................................................4

COMPETENCIAS A ALCANZAR EN EL CURSO...............................................................6

UNIDAD 1 ANÁLISIS SEMÁNTICO..............................................................................7

UNIDAD 2 GENERACIÓN DE CÓDIGO INTERMEDIO.......................................18

UNIDAD 3 OPTIMIZACIÓN...................................................................................................26

UNIDAD 4 GENERACIÓN DEL CÓDIGO OBJETO..........................................................35

BIBLIOGRAFIA.......................................................................................................................44
ANTOLOGIA
Instituto Tecnológico Superior de Alvarado

INTRODUCCION

El desarrollo de software se puede dividir en 2 grandes categorías. El


software comercial y el software científico. La presente materia proporciona las
bases para crear software científico. Es común pensar que el estudiante normal
nunca tendrá acceso a desarrollar software científico, sin embargo, esto es un
error ya que cada día las necesidades se van ampliando y todo va siendo más
accesible. Tenemos el clásico ejemplo de la robótica. Actualmente ya hay
tiendas donde venden accesorios para hacer un pequeño robot de juguete. Eso
antes no se veía y ahora ya es común.

Por lo mismo, es importante proporcionarle al alumno las bases para que él se


introduzca con profundidad en el mundo de los compiladores. Esta materia
abre horizontes impresionantes ya que se conoce a fondo las etapas por las
que atraviesa la creación de un lenguaje de computación. Desde la etapa léxica
hasta la etapa de generación de código, el estudiante debe profundizar en
conocimientos que colindan con la parte electrónica de la computadora, el
lenguaje ensamblador, el lenguaje máquina.

Esta materia es una aventura racional. Algunos pensarán que es un tormento


cerebral, pero los inteligentes sabrán apreciar todas las competencias que se
desarrollan en esta materia.

Cabe mencionar que esta materia es la 2ª. Parte de la materia Lenguajes y


autómatas, por lo tanto, se debe dedicar cierto tiempo a dar un repaso práctico
a la 1ª. Parte de la materia que consistió en las 2 primeras fases de los
compiladores: fase léxica y fase sintáctica.

Si no se da este repaso se corre el peligro de que el alumno no entienda ésta


segunda parte ya que van muy ligadas.

PROGRAMACION LENGUAJE Y AUTOMATAS II 1


ANTOLOGIA
Instituto Tecnológico Superior de Alvarado

Es muy recomendable utilizar un compilador didáctico. Se recomienda


ampliamente el compilador desarrollado por Kenneth Louden. En la bibliografía
al final de este documento se encuentra con la referencia número 3 y número
12. Esto debido a que el alumno debe conocer un compilador ya hecho para
así entender al 100% todos los conceptos.

PROPOSITO Y CONTENIDO

En esta asignatura se debe desarrollar el análisis semántico, la generación de


código, la optimización y la generación del código objeto para obtener el

PROGRAMACION LENGUAJE Y AUTOMATAS II 2


ANTOLOGIA
Instituto Tecnológico Superior de Alvarado

funcionamiento de un compilador. Esta asignatura busca proveer al estudiante


de herramientas, conocimientos y habilidades necesarias para desarrollar un
compilador con base en los conocimientos previos de la asignatura lenguajes y
autómatas I. La aportación de esta materia es relevante en el ámbito del
desarrollo de software de sistemas.

Es indispensable distinguir que la carrera de Ingeniería en Sistemas


Computacionales se basa no sólo en el desarrollo de software comercial y
administrativo, sino también en el desarrollo de software científico. Esta
materia se ubica en la segunda categoría y es indispensable desarrollar
software en estos campos para preparar a los egresados y tengan la
posibilidad de cursar posgrados de alto nivel.

La asignatura trata de concretar un traductor iniciado en la materia previa para


que el estudiante comprenda que es capaz, mediante técnicas bien definidas,
de crear su propio lenguaje de programación. La aportación de la asignatura al
perfil del egresado será específicamente la siguiente:

 Desarrollar, implementar y administrar software de sistemas o de


aplicación que cumpla con los estándares de calidad buscando
como finalidad apoyar la productividad y competitividad de las
organizaciones.

 Integrar soluciones computacionales con diferentes tecnologías,


plataformas o dispositivos.

 Diseñar e implementar interfaces hombre – máquina y máquina –


máquina para la automatización de sistemas.

 Identificar y comprender las tecnologías de hardware para proponer,


desarrollar y mantener aplicaciones eficientes.

PROGRAMACION LENGUAJE Y AUTOMATAS II 3


ANTOLOGIA
Instituto Tecnológico Superior de Alvarado

OBJETIVO

Desarrollar software de base: traductor, intérprete o compilador.

COMPETENCIAS PREVIAS Y RELACION CON CURSOS ANTERIORES Y


POSTERIORES

 Definir, diseñar, construir y programar las fases del analizador


léxico y sintáctico de un traductor o compilador.

 Su relación con materias anteriores: Fundamentos de


programación, Tópicos avanzados de programación, Fundamentos
de Ingeniería de Software, Lenguajes y autómatas I.

 Su relación con materias posteriores:


Sistemas programables

 Las competencias logradas en esta materia son: razonamiento


deductivo e inductivo, análisis – síntesis.

CONTENIDO

UNIDAD 1 Análisis semántico

1.1. Arboles de expresiones.

1.2. Acciones semánticas de un analizador sintáctico.

1.3. Comprobaciones de tipos en expresiones.

PROGRAMACION LENGUAJE Y AUTOMATAS II 4


ANTOLOGIA
Instituto Tecnológico Superior de Alvarado

1.4. Pila semántica en un analizador sintáctico.

1.5. Esquema de traducción.

1.6. Generación de la tabla de símbolo y de direcciones.

1.7. Manejo de errores semánticos.

UNIDAD 2 Generación de código intermedio.

2.1 Notaciones

2.1.1 Prefija

2.1.2 Infija

2.1.3 Postfija

2.2 Representaciones de código Intermedio.

2.2.1 Notación Polaca

2.2.2 Código P

2.2.3 Triplos

2.2.4 Cuádruplos.

2.3 Esquema de generación.

2.3.1 Variables y constantes.

2.3.2 Expresiones.

2.3.3 Instrucción de asignación.

2.3.4 Instrucciones de control.

PROGRAMACION LENGUAJE Y AUTOMATAS II 5


ANTOLOGIA
Instituto Tecnológico Superior de Alvarado

2.3.5 Funciones

2.3.6 Estructuras

UNIDAD 3 Optimización

3.1 Tipos de optimización.

3.1.1 Locales.

3.1.2 Ciclos.

3.1.3 Globales.

3.1.4 De mirilla.

3.2 Costos.

3.2.1 Costo de ejecución. (memoria, registros, pilas)

3.2.2 Criterios para mejorar el código.

3.2.3 Herramientas para el análisis del flujo de datos.

UNIDAD 4 Generación de código objeto

4.1 Registros.

4.2 Lenguaje ensamblador.

4.3 Lenguaje máquina.

4.4 Administración de memoria.

COMPETENCIAS A ALCANZAR EN EL CURSO

PROGRAMACION LENGUAJE Y AUTOMATAS II 6


ANTOLOGIA
Instituto Tecnológico Superior de Alvarado

Al término del curso el participante logrará las siguientes competencias:

Unidad 1: Análisis Semántico.

Diseñar mediante el uso de árboles de expresiones dirigida por la sintaxis un


analizador semántico para un meta-compilador.

Unidad 2: Generación de código intermedio.

Aplicar las herramientas para desarrollar una máquina virtual que ejecute
código intermedio a partir del código fuente de un lenguaje prototipo.

Unidad 3: Optimización.

Conocer e Identificar los diferentes tipos de optimización que permita eficientar


el código intermedio.

Unidad 4: Generación del código objeto.

Utilizar un lenguaje de bajo nivel para traducir el código construido a lenguaje


máquina para su ejecución.

UNIDAD 1 ANÁLISIS SEMÁNTICO.

Competencia específica de la unidad:

Diseñar mediante el uso de árboles de expresiones dirigida por la sintaxis un


analizador semántico para un meta-compilador.

PROGRAMACION LENGUAJE Y AUTOMATAS II 7


ANTOLOGIA
Instituto Tecnológico Superior de Alvarado

CONTENIDO TEMATICO

En este capítulo analizamos la fase del compilador que calcula la información


adicional necesaria para la compilación una vez que se conoce la estructura
sintáctica de un programa. Esta fase se conoce como análisis semántico
debido a que involucra el cálculo de información que rebasa las capacidades
de las gramáticas

libres de contexto y los algoritmos de análisis sintáctico estándar, por lo que no


se considera como sintaxis. La información calculada también está
estrechamente relacionada con el significado

final, o semántica, del programa que se traduce. Como el análisis que realiza
un compilador es estático por definición (tiene lugar antes de la ejecución),
dicho análisis semántico también se conoce como análisis semántico estático.
En un lenguaje típico estáticamente tipificado como C. el análisis semántico
involucra la construcción de una tabla de símbolos para mantenerse al tanto de
los significados de nombres establecidos en declaraciones e inferir tipos y
verificarlos en expresiones y sentencias con el fin de determinar su exactitud
dentro de las reglas de tipos del lenguaje.

El análisis semántico se puede dividir en dos categorías. La primera es el


análisis de un programa que requiere las reglas del lenguaje de programación
para establecer su exactitud y garantizar una ejecución adecuada. La
complejidad de un análisis de esta clase requerido por una definición del
lenguaje varía enormemente de lenguaje a lenguaje. En lenguajes orientados
en forma dinámica, tales como LISP y Smalltalk, puede no haber análisis
semántico estático en absoluto, mientras que en un lenguaje como Ada existen
fuertes requerimientos que debe cumplir un programa para ser ejecutable.

PROGRAMACION LENGUAJE Y AUTOMATAS II 8


ANTOLOGIA
Instituto Tecnológico Superior de Alvarado

Otros lenguajes se encuentran entre estos extremos (Pascal, por ejemplo. no


es tan estricto en sus requerimientos estáticos como Ada y C, pero no es tan
condescendiente como LISP).

La segunda categoría de análisis semántico es el análisis realizado por un


compilador para mejorar la eficiencia de ejecución del programa traducido. Esta
clase de análisis por lo regular se incluye en análisis de "optimización", o
técnicas de mejoramiento de código. Investigaremos algunos de estos métodos
en el capítulo sobre generación de código, mientras que en este capítulo nos
enfocaremos en los análisis comunes que por exactitud son requeridos para
una definición del lenguaje. Conviene advertir que las técnicas estudiadas aquí
se aplican a ambas situaciones. También que las dos categorías no son
mutuamente excluyentes, ya que los requerimientos de exactitud, tales como la
verificación de tipos estáticos, también permiten que un compilador genere
código más eficiente que para un lenguaje sin estos requerimientos. Además,
vale la pena hacer notar que los requerimientos de exactitud que aquí se
comentan nunca pueden establecer Ia exactitud completa de un programa, sino
sólo una clase de exactitud parcial. Tales requerimientos todavía son
útiles debido a que proporcionan al programador información para mejorar
la seguridad y fortaleza del programa.

El análisis semántico estático involucra tanto la descripción de los análisis a


realizar como la implementación de los análisis utilizando algoritmos
apropiados. En este sentido, es semejante al análisis léxico y sintáctico. En el
análisis sintáctico, por ejemplo, utilizamos gramáticas libres de contexto en la
Forma Backus-Naus (BNF, por sus siglas en inglés) para describir la sintaxis y
diversos algoritmos de análisis sintáctico descendente ascendente para
implementar la sintaxis. En el análisis semántico la situación no es tan clara, en
parte porque no hay un método estándar (como el BNF) que permita
especificar la semántica estática de un lenguaje, y en parte porque la cantidad
y categoría del análisis semántico estático varía demasiado de un lenguaje a
otro. Un método para describir el análisis semántico que los escritores de

PROGRAMACION LENGUAJE Y AUTOMATAS II 9


ANTOLOGIA
Instituto Tecnológico Superior de Alvarado

compiladores usan muy a menudo con buen efecto es la identificación de


atributos, o propiedades, de entidades del lenguaje que deben calcularse y
escribir ecuaciones de atributos o reglas semánticas, que expresan cómo el
cálculo de tales atributos está relacionado con las reglas gramaticales del
lenguaje. Un conjunto así de atributos y ecuaciones se denomina gramática
con atributos.

Las gramáticas con atributos son más útiles para los lenguajes que obedecen
el principio de la semántica dirigida por sintaxis, la cual asegura que el
contenido semántico de un programa se encuentra estrechamente relacionado
con su sintaxis. Todos los lenguajes modernos tienen esta propiedad. Por
desgracia, el escritor de compiladores casi siempre debe construir una
gramática con atributos a mano a partir del manual del lenguaje, ya que rara
vez la da el diseñador del lenguaje. Aún peor, la construcción de una gramática
con atributos puede complicarse innecesariamente debido a su estrecha
adhesión con la estructura sintáctica explícita del lenguaje. Un fundamento
mucho mejor para la expresión de los cálculos semánticos es la sintaxis
abstracta, como se representa mediante un árbol sintáctico abstracto. Incluso.
el diseñador del lenguaje, también suele dejar al escritor del compilador la
especificación de la sintaxis abstracta.

Los algoritmos para la implementación del análisis semántico tampoco son tan
claramente expresables como los algoritmos de análisis sintáctico. De nuevo,
esto se debe en parte a los mismos problemas que se acaban de mencionar
respecto a la especificación del análisis semántico. No obstante, existe un
problema adicional causado por la temporización del análisis durante el
proceso de compilación. Si el análisis semántico se puede suspender hasta
que todo el análisis sintáctico (y la construcción de un árbol sintáctico
abstracto) esté completo, entonces la tarea de implementar el análisis
semántico se vuelve considerablemente más fácil y consiste en esencia en la
especificación de orden para un recorrido del árbol sintáctico. junto con los
cálculos a realizar cada vez que se encuentra un nodo en el recorrido. Sin

PROGRAMACION LENGUAJE Y AUTOMATAS II 10


ANTOLOGIA
Instituto Tecnológico Superior de Alvarado

embargo, esto implica que el compilador debe ser de paso múltiple. Si. por otra
parte, el compilador necesita realizar todas sus operaciones en un solo paso
(incluyendo la generación del código), entonces la implementación del análisis
semántico se convierte en mucho más que un proceso a propósito para
encontrar un orden correcto y un método para calcular la información
semántica (suponiendo que un orden correcto así exista en realidad).
Afortunadamente, la práctica moderna cada vez más permite al escritor de
compiladores utilizar pasos múltiples para simplificar los procesos de análisis
semántico y generación de código.

A pesar de este estado algo desordenado del análisis semántico, es muy útil
para estudiar gramáticas con atributos y cuestiones de especificación. ya que
esto redundará en la capacidad de escribir un código más claro, más conciso y
menos proclive a errores para análisis semántico, además de permitir una
comprensión más fácil de ese código. Por lo tanto, el capítulo comienza con un
estudio de atributos y gramáticas con atributos. Continúa con técnicas para
implementar los cálculos especificados mediante una gramática con atributos,
incluyendo la inferencia de un orden para los cálculos y los recorridos del árbol
que los acompañan. Dos secciones posteriores se concentran en las áreas
principales del análisis semántico: tablas de símbolos y verificación de tipos. La
última sección describe un analizador semántico para el lenguaje de
programación TlNY.

ATRIBUTOS Y GRAMATICAS CON ATRIBUTOS

Un atributo es cualquier propiedad de una construcción del lenguaje de


programación. Los atributos pueden variar ampliamente en cuanto a la
información que contienen, su complejidad y en particular el tiempo que les
torna realizar el proceso de traducción/ejecución cuando pueden ser
determinados. Ejemplos típicos de atributos son El tipo de <latos de una
variable El valor de una expresión La ubicación de una variable

PROGRAMACION LENGUAJE Y AUTOMATAS II 11


ANTOLOGIA
Instituto Tecnológico Superior de Alvarado

en la memoria El código objeto de un procedimiento El número de dígitos


significativos en un número Los atributos se pueden establecer antes del
proceso de compilación (o incluso la construcción de un compilador). Por
ejemplo, el número de dígitos significativos de un número se puede establecer
(o por lo menos dar un valor mínimo) mediante la definición de un Lenguaje.

El proceso (te calcular un atributo y asociar su valor calculado con la


construcción del lenguaje en cuestión se define corno fijación del atributo. El
tiempo que toma el proceso de compilación cuando se presenta la fijación de
un atributo se denomina tiempo de fijación. Los tiempos de fijación de atributos
diferentes varían, e incluso el mismo atributo puede tener tiempos de fijación
bastante diferentes de un lenguaje a otro.

Definición de otro autor:

Análisis sintáctico, semántico y generación de código

Como lo hemos mencionado antes, la sintaxis trata con la forma de los


programas válidos, mientras que la semántica trata con su significado. Se dice
que la sintaxis de un lenguaje es aquella porción del mismo que puede ser
descrita de manera adecuada por medio de una Gramática Libre de Contexto
(GLC), mientras que la semántica es aquella porción del lenguaje que no puede
ser descrita de esta manera. El análisis semántico y la generación de código
intermedio pueden ser descritos en términos de la anotación o decoración de
un árbol sintáctico o árbol de análisis sintáctico (parse tree). Las anotaciones
de un árbol sintáctico son llamados “atributos”.

El analizador sintáctico recibe una serie de tokens del analizador léxico y los
organiza en un árbol sintáctico. La manera en que estos tokens son

PROGRAMACION LENGUAJE Y AUTOMATAS II 12


ANTOLOGIA
Instituto Tecnológico Superior de Alvarado

organizados está definida por un conjunto de reglas, potencialmente recursivas.


Este conjunto de reglas es la gramática libre de contexto.

En general, una GLC consiste de:

 Un conjunto finito N de símbolos no terminales.

 Un conjunto finito T de símbolos terminales.

 Un subconjunto finito P de [(N U T)* - T*] x (N U T)* llamado conjunto de


producciones, y Un símbolo inicial σ Є N.

La tarea del analizador sintáctico (parser) es determinar si una cadena de


entrada puede derivarse a partir del símbolo inicial, y si es así, cómo puede
hacerse. Hay dos maneras básicas de hacer esto:

 Análisis sintáctico descendente (top-down): puede verse como el intento


de encontrar la derivación más a la izquierda de un flujo de entrada
recorriendo el árbol sintáctico de arriba hacia abajo. Los tokens son
analizados de izquierda a derecha. Para manejar la ambigüedad se
suele utilizar lo que se conoce como opción inclusiva, expandiendo los
lados derechos de las reglas gramaticales. Los analizadores LL (Left-to-
right, Leftmost derivation) y Recursivos-descendentes son ejemplos de
este tipo de análisis.

 Análisis sintáctico ascendente (bottom-up): el analizador inicia con la


secuencia de entrada e intenta re-escribirla hasta llegar al símbolo
inicial. Intuitivamente el analizador intenta localizar el elemento más
básico, después los elementos que contienen éstos, y así
sucesivamente. Los analizadores LR (Left-to-right, Rightmost derivation)
son ejemplos de este tipo de analizadores, que también son conocidos
como Analizadores Desplaza-Reduce (Shift-Reduce).

PROGRAMACION LENGUAJE Y AUTOMATAS II 13


ANTOLOGIA
Instituto Tecnológico Superior de Alvarado

Analizadores LL vs. LR

El acrónimo LL viene de Left-to-Right, Leftmost Derivation, lo que significa que


cuando se encuentra una regla de producción no terminal que contiene
componentes no terminales en su parte derecha, el componente no terminal de
más a la izquierda es sustituido por el lado derecho de su respectiva regla. Si
esa regla también contiene componentes no terminales, el proceso se repite.

Debido a este comportamiento, los árboles sintácticos generados por los


analizadores LL tienden a crecer más hacia la izquierda que hacia la derecha.
Para tratar de evitar ambigüedades, el analizador siempre lee el siguiente
símbolo en la cadena de entrada, y lo llama “look-ahead”. Por otro lado, el
analizador LL decide la regla de producción a utilizar, dependiendo de lo que
resta por analizar en la cadena de entrada. No mantiene información sobre el
progreso del análisis. Si surge alguna ambigüedad que no pueda ser resuelta
con el auxilio del símbolo “look- ahead”, el analizador produce un error. Aunque
es posible aumentar el número de símbolos “look-ahead”, el problema esencial
persiste; además de los símbolos “look-ahead”, no hay otro método para
recuperarse de ambigüedades en este tipo de analizadores.

Los analizadores LR (Left-to-right, Rightmost derivation) también utilizan


símbolos “look-ahead”, pero además mantienen información sobre el estado del
proceso de análisis. Para este registro utilizan una pila (stack) donde guardan
información del estado inmediato anterior. Cada regla de producción es
considerada un estado y cada regla que resulta en un símbolo no terminal es
considerada una transición a otro estado. Esto hace que los analizadores LR
sean más resistentes a las ambigüedades que los LL, pero pueden resultar en
analizadores que requieren más recursos (en términos de tiempo de
procesamiento y espacio de memoria).

PROGRAMACION LENGUAJE Y AUTOMATAS II 14


ANTOLOGIA
Instituto Tecnológico Superior de Alvarado

Los analizadores LALR (Look Ahead LR) son una especialización de los
analizadores LR. Pueden manejar más GLC que el LR básico. Es un tipo de
analizador muy popular porque logra un buen balance entre el número de
gramáticas que puede manejar y su consumo computacional (en términos de
procesador y memoria). Las herramientas Yacc y Bison generan analizadores
sintácticos de este tipo.

Ejemplo de análisis sintáctico

Para ilustrar estos conceptos, supongamos que tenemos un lenguaje de


expresiones algebraicas escritas con los símbolos T = {x, y, z, +, *, (, )}, que
constituyen los símbolos terminales de la gramática. Los símbolos no-
terminales que utilizaremos son E para una expresión y T para términos que
constan de factores (símbolo no-terminal F), es decir, N={E,T,F}.

Una expresión algebraica puede consistir de un solo término: E → T O la suma


de una expresión y un término: E → E + T

Un término puede consistir de un factor, o del producto de un factor y un


término:

T→F

T→F*T

Un factor puede consistir de una expresión algebraica entre paréntesis o de


uno de los símbolos terminales:

F→(E)

F→xF→yF→z

PROGRAMACION LENGUAJE Y AUTOMATAS II 15


ANTOLOGIA
Instituto Tecnológico Superior de Alvarado

Estas reglas, también llamadas producciones, definen el conjunto de todas las


cadenas válidas de este lenguaje.

Una expresión válida para este lenguaje es x * y + z * (x + y). El árbol


sintáctico correspondiente a esta expresión es el siguiente:

Figura 1. Árbol sintáctico de la expresión algebraica x * y + z * (x + y)


Gramática de atributos

Cuando describimos un lenguaje por medio de una GLC, no se puede decir


nada acerca de los significados de las expresiones. Para el manejo de los
significados es común usar una gramática de atributos, la cual asigna un valor
a cada símbolo (terminal y no-terminal) del lenguaje.

La gramática se incrementa con un conjunto de reglas de producción que


especifican como se relaciona cada símbolo con su valor. Para ilustrar el uso
de las gramáticas de atributos, supongamos la siguiente gramática de
expresiones algebraicas compuesta por constantes y que maneja precedencia
y asociatividad:

PROGRAMACION LENGUAJE Y AUTOMATAS II 16


ANTOLOGIA
Instituto Tecnológico Superior de Alvarado

E→E+T

E→E-T

E →T

T→T*FT→T/FT→F

F → -F

F→(E)

F → constante

Ahora presentamos la gramática de atributos para este lenguaje de


expresiones algebraicas:

1. E1→ E2 + T

» E1.val := suma(E2.val, T.val) 2. E1→ E2 - T

» E1.val := diferencia(E2.val, T.val)

3. E → T

» E.val := T.val 4. T1→ T2 * F

» T1.val := producto(T2.val, F.val) 5. T1→ T2 / F

» T1.val := cociente(T2.val, F.val)

6. T → F

» T.val := F.val 7. F1→ -F2

» F1.val := inverso(F2.val) 8. F → ( E )

» F.val := E.val

9. F → constante

» F.val := constante. Val

Como vemos, en estas reglas de la gramática de atributos, asignar significados


a las sentencias del lenguaje equivale a asignar una función que recibe como

PROGRAMACION LENGUAJE Y AUTOMATAS II 17


ANTOLOGIA
Instituto Tecnológico Superior de Alvarado

parámetros los componentes de la sentencia y regresa un valor que se asigna


a esa expresión.

Resumen

Hemos visto hasta ahora que el análisis sintáctico y el análisis semántico


utilizan algunas herramientas matemáticas para realizar sus funciones: árboles
sintácticos y gramáticas de atributos. Estas herramientas nos permiten definir la
sintaxis y semántica de un lenguaje y nos preparan para la generación de
código intermedio.

UNIDAD 2 GENERACIÓN DE CÓDIGO INTERMEDIO.

Competencia específica de la unidad:

Aplicar las herramientas para desarrollar una máquina virtual que ejecute
código intermedio a partir del código fuente de un lenguaje prototipo.

CONTENIDO TEMATICO

Código máquina

La aparición de los lenguajes de alto nivel mejoró la productividad de los


programadores al liberarlos de los detalles de bajo nivel de la computadora que
estaban programando. El compilador es quien se encarga de realizar la
conversión del código fuente en lenguaje de alto nivel al código máquina de la
computadora que ejecutará el programa. Es, por tanto, el compilador quien
ahora maneja los detalles de bajo nivel de la arquitectura a programar. Esto
implica que el desarrollo del compilador requiere del conocimiento de los
detalles de la arquitectura de la máquina destino. En este apartado
conoceremos la computadora abstracta para la que Tiny traduce sus
programas.

PROGRAMACION LENGUAJE Y AUTOMATAS II 18


ANTOLOGIA
Instituto Tecnológico Superior de Alvarado

La máquina abstracta

En este apartado describimos la computadora para la cual el compilador de


Tiny genera código objeto. Está diseñada para facilitar la traducción de un
lenguaje similar al Pascal a una arquitectura abstracta. Esta computadora es un
subconjunto de la computadora Dream (Dream machine) diseñada por Frank
DeRemer. Se trata de una computadora orientada a pila (stack) sin registros
direccionales, por lo que carece de instrucciones como “carga registro X con
…”; es decir, todas las instrucciones suponen que los datos están en la pila y
guardan los resultados en la pila.

Además, se han eliminado algunas restricciones normales en las máquinas


reales:

 Las instrucciones de ramificación no están limitadas a un determinado


rango.

 Todas las instrucciones tienen el mismo formato, y no dos o tres


formatos diferentes, como en la arquitectura IA32, por ejemplo.

La computadora tiene tres memorias diferentes y separadas entre sí:

 De código

 De datos

 De direcciones de retorno

PROGRAMACION LENGUAJE Y AUTOMATAS II 19


ANTOLOGIA
Instituto Tecnológico Superior de Alvarado

Los límites de cada tipo de memoria están indicados por una serie de registros.
La siguiente figura ilustra estas memorias y sus registros límite:

La memoria de código se considera de sólo-lectura. Ningún programa puede


saltar (branch) fuera de su área de código, ya que todas sus instrucciones
están confinadas al rango de direcciones limitado por los registros CBR y CLR.
Ni el código ni las direcciones de retorno pueden modificarse ni copiarse,
debido a que todas las lecturas y escrituras a memoria están restringidas a
realizarse entre los registros GBR (Global Base Register) y STR (Stack Top
Register).

En otras palabras, en esta máquina es imposible ejecutar código que se auto


modifiqué.

PROGRAMACION LENGUAJE Y AUTOMATAS II 20


ANTOLOGIA
Instituto Tecnológico Superior de Alvarado

El límite de la memoria disponible está determinado por el registro SLR (Stack


Limit Register). Este registro simula el tamaño limitado de la memoria física de
las computadoras reales; sin embargo, para fines del compilador de Tiny,
supondremos que la máquina no tiene limitación de memoria, es decir, el
contenido de SLR es “infinito”.

Todo el direccionamiento de las variables globales y locales es relativo a GBR


(Global Base Register) o a LBR (Local Base Register), según corresponda. Por
tanto, la instrucción “LLV i” (Load Local Value i) significa “coloca en el tope de la
pila (push) el valor de la i- ésima palabra del marco local (local frame). Por
tanto, i puede verse como un desplazamiento (offset) a partir de LBR. La
ejecución de esta instrucción ajustará el registro del tope de la pila (STR). Otro
ejemplo es “SGVi” (Storage Global Value i), que significa “saca de la pila el
valor en el tope (pop) y almacénalo en la i-ésima palabra global”. De nuevo, la
ejecución de esta instrucción ajustará el STR, e i es el desplazamiento respecto
de GBR. Otro ejemplo más es “LIT i” (Literal i), que significa “coloca i en la pila”.

Con el fin de simplificar, asumimos que la ejecución del programa, una vez
cargado en memoria, inicia en la instrucción indicada en CBR; es decir,
asumimos que el cargador carga el programa a partir de esa localización. La
herramienta TWS incluye un intérprete que hace las veces de la máquina
abstracta.

Formato de instrucciones

El formato de las instrucciones de la máquina abstracta es el siguiente:

{etiqueta} [mnemónico de la instrucción] [0, 1 o 2 operando]

Donde la etiqueta es opcional, además vemos que hay instrucciones que no


manejan datos, otras que manejan un sólo dato y otras que manejan dos datos.

PROGRAMACION LENGUAJE Y AUTOMATAS II 21


ANTOLOGIA
Instituto Tecnológico Superior de Alvarado

Es decir, en el conjunto de instrucciones encontramos instrucciones con 0, 1 o


2 campos de direccionamiento.

Conjunto de instrucciones

Las instrucciones que la máquina abstracta puede ejecutar son las siguientes:

De control del programa

NOP: Hacer nada

HALT: Detenerse (halt)

De transferencia de datos

LIT v: Cargar (Push) la Literal v en Local frame (Lf) de la memoria de datos.


LLV i: Cargar valor local (Load Local Value) i en Lf.

LGV i: Cargar valor global (Load Local Value) i en Lf. SLV i: Almacena valor
local i (Store Local Value i) en Lf. SGV i: Almacena valor global i.

LLA i: Cargar Dirección Local i (Load Local Address i) en Lf. LGA i: Cargar
Dirección Global i (Load Global Address i) en Lf. POP n: Extrae n valores (Pop
n values).

DUP : Duplica el tope de la pila.

SWAP : Intercambia los dos valores superiores de la pila.

Aritméticas y lógicas

UOP i: Operación unitaria i (Unary Operation i): const X = Pop Lf. Push (Unop
(i, X)) en Lf

PROGRAMACION LENGUAJE Y AUTOMATAS II 22


ANTOLOGIA
Instituto Tecnológico Superior de Alvarado

BOP i: Operación binaria i: const Xr, Xl = Pop Lf, Pop Lf Push (Binop(i,Xl,Xr)) en
Lf

De control de flujo

CALL n: Llamada a una subrutina. RTN n: Retorno de rutina.

GOTO L: I <- L Salto incondicional a L

COND L M: I <- if Pop Lf = True # Pop Stack. Si el valor es: then L # Verdadero,
salta a L

else M # Falso, salta a M. fi

CODE F: Push F on Lf # Carga el punto de entrada. SOS i: Llamada a la


función i del sistema operativo Donde

Unop (i, X) significa: case i of

UNOT: not(X) UNEG: -(X) USUCC: X+1 UPRED: X-1

Binop (i, Xl, Xr) significa: case i of

BAND: Xl and Xr BOR: Xl or Xr BPLUS: Xl + Xr BMINUS: Xl - Xr BMULT : Xl *


Xr

BDIV: Xl div Xr BMOD: Xl mod Xr BEQ: Xl = Xr BNE: Xl <> Xr BLE: Xl <= Xr


BGE: Xl >= Xr BLT: Xl < Xr

BGT: Xl > Xr

Llamadas al sistema

Para simplificar el manejo de los dispositivos de Entrada/Salida suponemos


que tenemos disponibles algunas llamadas al sistema operativo, a las cuales
se accede por medio de la instrucción SOS:

PROGRAMACION LENGUAJE Y AUTOMATAS II 23


ANTOLOGIA
Instituto Tecnológico Superior de Alvarado

SOS (Sistema operativo) i significa:

case i of
TRACEX: Trace Execution <- not Trace Execution DUMPMEM: Dump Memory
INPUT: readln(i) Push i on Lf INPUTC: readln(ch) Push Ord(ch) on Lf
OUTPUT: write (Pop Lf) OUTPUTC: write (Chr(Pop(Lf))) OUTPUTL: writeln
EOF: if eof(input) then Push True on Lf else Push False on Lf

Vemos que la máquina abstracta puede ejecutar las cuatro operaciones


aritméticas básicas: suma, resta, multiplicación y división, además la operación
módulo; también puede ejecutar las tres operaciones lógicas básicas: NOT,
AND y OR; puede ejecutar saltos condicionales e incondicionales. Cuenta con
llamadas al sistema operativo para las operaciones de Entrada/Salida y para
funciones de depuración.

Como sabemos, el compilador debe traducir del lenguaje de alto nivel a este
conjunto de instrucciones. Es, por tanto, necesario entender cómo utilizar este
lenguaje ensamblador para escribir programas en bajo nivel para la máquina
abstracta. A continuación, presentamos un par de ejemplos de traducción de
Tiny al ensamblador de la máquina abstracta.

Ejemplo 1

Vamos a mostrar la traducción del siguiente:

programa en Tiny: Program copy:


{Despliega en pantalla (eco) los primeros 10 números leídos con input}
Var count: integer; Begin
Count: =1;
While (count <= 10) do Begin
Output(read); Count: = count + 1; End

PROGRAMACION LENGUAJE Y AUTOMATAS II 24


ANTOLOGIA
Instituto Tecnológico Superior de Alvarado

End copy.

El código máquina para este programa se muestra abajo. Obviamente, los


comentarios no son generados por el generador de código.

LIT 0 # Espacio para Count LIT 1 # Asigna 1


SGV 0 # Count: = 1
L2 LGV 0 # Carga Count LIT 10 # Carga 10
BOP BLE # POP, Compara. Push resultado T/F COND L3 L4 # Pop stack, si V,
ve a L3, sino, ve a L4.
L3 SOS INPUT # Llamada al SO. Lee y coloca en la pila SOS OUTPUT #
Llamada al SO. Saca de la pila y despliega SOS OUTPUTL # Despliega un
avance de línea
LGV 0 # Carga Count LIT 1 # Carga 1
BOP BPLUS # Súmalos y guarda resultado en la pila SGV 0 # Saca de la pila y
almacena en Count
GOTO L2 # Regresa a L2
L4 HALT # Salta aquí cuando termines. Détente.

Ejemplo 2

Con el fin de ayudar al entendimiento de esta máquina estudiaremos otro


programa ejemplo, escrito en el lenguaje “Medium”, que es el lenguaje
intermedio para Tiny:

program fact: var m: integer;


function fact (n: integer): integer begin
If n > 0 then
fact: = n * fact (n – 1); else
fact: = 1;
m: = m + 1;
end; begin m: = 0;
output (fact (read), m); end fact.

PROGRAMACION LENGUAJE Y AUTOMATAS II 25


ANTOLOGIA
Instituto Tecnológico Superior de Alvarado

El código para la máquina:

LIT 0
GOTO L1 L2 LLV 1
LIT 0 BOP BGT
COND L3 L4 L3 LLV 1
LIT 0
LLV 1
LIT 1
BOP BMINUS CODE L2 CALL 3
BOP BMULT SLV 0
GOTO L5 L4 LIT 1
SLV 0 NOP
L5 LGV 0
LIT 1
BOP BPLUS
SGV 0
LLV 0
RTN 1
L1 LIT 0
SGV 0
LIT 0
SOS INPUT CODE L2 CALL 1
SOS OUTPUT LGV 0
SOS OUTPUT SOS OUTPUTL HALT

Es muy recomendable que traces con papel y lápiz la ejecución de estos dos
programas de ejemplo para que entiendas mejor la programación en bajo nivel
de la máquina abstracta.

PROGRAMACION LENGUAJE Y AUTOMATAS II 26


ANTOLOGIA
Instituto Tecnológico Superior de Alvarado

UNIDAD 3 Optimización.

Competencia específica de la unidad:

Conocer e Identificar los diferentes tipos de optimización que permita eficientar


el código intermedio.

CONTENIDO TEMATICO

Estrategias de optimización

La optimización del código es el proceso de "puesta a punto" (tuning) de la


salida de un compilador con el fin de minimizar o maximizar algún atributo del
programa objeto. Es común que la meta de la optimización sea minimizar el
espacio en memoria que ocupa del código o maximizar la velocidad de
ejecución.

Una forma de clasificar las estrategias de optimización del código se basa en el


alcance de la optimización: desde una sola instrucción (optimización local),
hasta un programa entero (optimización global). Técnicamente hablando es
más sencillo implementar estrategias locales que globales, aunque
normalmente el beneficio es menor que el obtenido con estrategias globales.

Algunos ejemplos de estrategias de optimización clasificadas por su alcance


son:

 Optimizaciones locales: Sólo toman en cuenta la información local de


una función.

PROGRAMACION LENGUAJE Y AUTOMATAS II 27


ANTOLOGIA
Instituto Tecnológico Superior de Alvarado

 Requieren menos tiempo de análisis.

 Optimización interprocedural o de programa completo: Requieren


analizar el programa completo. Un ejemplo de estrategia de este tipo es
el reemplazo de una llamada a función con el código de la función, lo
que reduce la sobrecarga normal de una llamada a función.

 Optimización de lazos: Se aplica sólo a las estructuras repetitivas, como


for () y while (). Este tipo de optimización puede tener un fuerte impacto
porque muchos programas dedican un buen porcentaje de su tiempo de
ejecución a este tipo de instrucciones.

Además de la clasificación por su alcance, las estrategias de optimización se


dividen en dos categorías generales:

 Independiente vs. dependiente del lenguaje de programación. La


optimización independiente del lenguaje de programación se basa en el
hecho de que muchos lenguajes de programación comparten estructuras
comunes. La optimización dependiente del lenguaje trata de aprovechar
las particularidades de cada lenguaje de alto nivel.

 Independiente vs. dependiente de la máquina destino. Muchas de las


mejores optimizaciones se logran explotando las características
particulares de la plataforma destino.

PROGRAMACION LENGUAJE Y AUTOMATAS II 28


ANTOLOGIA
Instituto Tecnológico Superior de Alvarado

Obviamente las características de la plataforma destino también afectan las


estrategias de optimización. Entre estas características tenemos las siguientes:

 Número de registros internos en el CPU.

 Tipo de procesador: RISC vs. CISC.

 Número de unidades funcionales: coprocesador numérico o no,


pipelining, etcétera.

Para evitar complicar mucho al compilador de Tiny, la herramienta TWS no


maneja ningún tipo de optimización; sin embargo, muchos compiladores sí lo
hacen, ya que la posibilidad de obtener código optimizado siempre es un
atractivo más para cualquier compilador, independientemente del lenguaje a
traducir.

Detección de errores

Los errores de programación pueden clasificarse en tres categorías:

 Errores de compilación

 Errores de ejecución

 Errores lógicos

PROGRAMACION LENGUAJE Y AUTOMATAS II 29


ANTOLOGIA
Instituto Tecnológico Superior de Alvarado

Errores de compilación

Son errores que aparecen cuando se está compilando el programa y


eventualmente ocasionan que se detenga el proceso de traducción.

Muchos de estos errores son causados por equivocaciones de captura, por


ejemplo, un mal deletreo de alguna palabra clave, mezcla incorrecta de
mayúsculas y minúsculas en los nombres de variables, o la falta de un signo de
puntuación, entre otros.

Son los errores más fáciles de corregir porque contamos con la ayuda del
compilador. Cuando el compilador detecta un error durante el analizador
sintáctico o no puede asignar significado a una expresión durante el análisis
semántico, genera un mensaje de error mostrando una breve descripción del
error y la línea del código fuente donde lo encontró. Normalmente el compilador
intenta seguir el proceso de traducción con el fin de detectar la mayor cantidad
posible de errores e informarlos al programador para que proceda a corregirlos.
Cuando aparecen varios errores de compilación, por lo general sólo el primer
error es real, los demás pueden no serlo.

Errores de ejecución

Son errores que aparecen mientras el programa está en ejecución.

Un ejemplo típico es la división entre cero; por ejemplo, supón que tienes la
siguiente expresión:

PROGRAMACION LENGUAJE Y AUTOMATAS II 30


ANTOLOGIA
Instituto Tecnológico Superior de Alvarado

velocidad = kilómetros / horas

Si la variable horas = 0 cuando se intente ejecutar la instrucción, la división no


puede calcularse y se genera un error de ejecución.

Errores lógicos

Causan que el programa no funcione correctamente y que arroje resultados


equivocados.

En este tipo de errores la compilación termina sin errores y el programa se


ejecuta sin errores de ejecución, pero arroja resultados no esperados, éstos
son los más difíciles de encontrar y corregir.

PRACTICA: Agregando la multiplicación al traductor inicial

En este apartado vamos a ilustrar la manera de expandir el lenguaje inicial


agregando la multiplicación de dos enteros. Esto requiere modificar los
siguientes componentes del traductor:

 El analizador léxico (lex.tiny)

 El analizador sintáctico (parse. tiny)

 El analizador semántico (constrainer)

 El generador de código (gencode)

PROGRAMACION LENGUAJE Y AUTOMATAS II 31


ANTOLOGIA
Instituto Tecnológico Superior de Alvarado

Vamos a ver como se realiza este proceso:

Cambios al analizador léxico lex.tiny

Las reglas para las operaciones dentro del analizador léxico son las siguientes:

"+"{returnrule(yytext[0]);}
"-" { returnrule(yytext[0]);}

Para agregar el operador "*" se debe agregar la siguiente línea: "*"


{ returnrule(yytext[0]);}

Cambios al analizador sintáctico parser.tiny

A continuación, reproducimos la parte del analizador sintáctico que define las


operaciones aritméticas:

Expression ->Term
->Term LTE Term => "<="; Term ->Primary
->Primary '+' Term => "+"; Primary -> '-' Primary => "-"
-> READ => "read"
->Name
->INTEGER_NUM => "<integer>"
-> '(' Expression ')';
Name -> IDENTIFIER => "<identifier>";
Para introducir el operador de la multiplicación, se debe agregar un nuevo
productor de factor:

Expression ->Term
->Term LTE Term => "<="; Term ->Primary
->Primary '+' Term => "+"; Factor ->Primary
->Primary '*' Factor => "*"; Primary -> '-' Primary => "-"
-> READ => "read"
->Name

PROGRAMACION LENGUAJE Y AUTOMATAS II 32


ANTOLOGIA
Instituto Tecnológico Superior de Alvarado

->INTEGER_NUM => "<integer>"


-> '(' Expression ')';
Name -> IDENTIFIER => "<identifier>";

Modificaciones al constrainer

El constrainer consiste de la clase tws: constrainer y del archivo de cabecera


nodes.h, que definen los nodos del árbol que el constrainer y el generador de
código deben recorrer para realizar su trabajo. El archivo nodes.h contiene las
siguientes líneas:

addnode(ProgramNode,"program");
addnode(TypesNode, "types");
addnode(TypeNode, "type");
addnode(DclnsNode ,"dclns");
addnode(DclnNode, "dcln");
addnode(IntegerTNode,"integer");
addnode(BooleanTNode, "boolean");
addnode(BlockNode,"block");
addnode(AssignNode, "assign");
addnode(OutputNode, "output");
addnode(IfNode ,"if");
addnode(WhileNode ,"while");
addnode(NullNode , "null");
addnode(LENode ,"<=");
addnode(PlusNode ,"+");
addnode(MinusNode,"-");
addnode(ReadNode,"read");
addnode(IntegerNode,"<integer>");
addnode(IdentifierNode,"<identifier>");

Se debe agregar la siguiente línea para el nodo de la multiplicación: addnode


(MultNode,"*");

PROGRAMACION LENGUAJE Y AUTOMATAS II 33


ANTOLOGIA
Instituto Tecnológico Superior de Alvarado

El archivo de la clase tws: constrainer tiene el método expression(), que analiza


las expresiones en el AST y determina si están bien formadas. A continuación,
mostramos el fragmento de este método que verifica los nodos "+" y "-":

if ((nodename == PlusNode) or (nodename == MinusNode)){ Type1 =


expression(T->get_child(0));
if(T->get_degree()==2){
Type2 = expression(T->get_child(1));
}else{
Type2 = TypeInteger;
}
if( (Type1 != TypeInteger) or (Type2 != TypeInteger)){ error(T);
cout<< "ARGUMENTS OF '+', '-' etc. MUST BE OF TYPE\ INTEGER" <<endl;
}
returnTypeInteger;
}

Para manejar el nuevo nodo de multiplicación "*", se debe agregar el siguiente


código al método:

if (nodename == MultNode){
Type1 = expression(T->get_child(0));
Type2 = expression(T->get_child(1));

if( (Type1 != TypeInteger) or (Type2 != TypeInteger)){ error(T);


cout<< "ARGUMENTS OF '*', MUST BE OF TYPE\ INTEGER" <<endl;
}
returnTypeInteger;
}

Modificaciones al generador de código

PROGRAMACION LENGUAJE Y AUTOMATAS II 34


ANTOLOGIA
Instituto Tecnológico Superior de Alvarado

El código para las expresiones aritméticas es generado en la clase


tws::codegenerator, en el método expression(). A continuación, mostramos el
fragmento de este método que genera el código para el nodo "-":

if (name == MinusNode){
expression(T->get_child(0),CurrLabel);
if (T->get_degree() == 2){
expression(T->get_child(1),NoLabel);
codegen(NoLabel,BOPOP,BMINUS);
dec_framesize();
}else{
codegen(NoLabel,UOPOP,UNEG);
}
}

El código para el nodo "*" debe generarse de la siguiente manera:

if (name == MultNode){
expression(T->get_child(0),CurrLabel);
expression(T->get_child(1),NoLabel);
codegen(NoLabel,BOPOP,BMULT);
dec_framesize();
}

Donde BMULT es la instrucción en ensamblador para la multiplicación.

Recompilando el traductor

Una vez realizados todos los cambios anteriores en sus respectivos archivos
fuente, es necesario recompilar nuestro traductor. Esto lo hacemos por medio
de la utilería make, es decir, estando ubicados en el directorio tws simplemente
tecleamos:

PROGRAMACION LENGUAJE Y AUTOMATAS II 35


ANTOLOGIA
Instituto Tecnológico Superior de Alvarado

make

Probando la multiplicación

El último paso es capturar y compilar un programa que utilice la nueva


instrucción, por ejemplo:

{Archivo: mult.tiny } programmulti:


var factor1,factor2, producto : integer; begin
factor1 := read; factor2 := read;
producto = factor1 * factor2;
output(producto) endmulti.

Recuerda que compilas el programa con la instrucción: tc mult.tiny

UNIDAD 4 Generación del código objeto.

Competencia específica de la unidad:

Utilizar un lenguaje de bajo nivel para traducir el código construido a lenguaje


máquina para su ejecución.

Análisis del código objeto a crear

La función del compilador es traducir un programa escrito en un lenguaje fuente


(un lenguaje de alto nivel) a un programa equivalente en lenguaje máquina del
procesador destino. Este programa está escrito en código objeto. En esta
sección del Módulo 4 veremos cómo obtener el código objeto que se genera a
partir de algunas instrucciones en lenguaje fuente. A manera de recordatorio, a
continuación, están reproducidas las instrucciones en lenguaje ensamblador de
la máquina abstracta. Toda instrucción del lenguaje fuente debe traducirse a
una o varias de estas instrucciones.

Las instrucciones que la máquina abstracta puede ejecutar son las siguientes:

PROGRAMACION LENGUAJE Y AUTOMATAS II 36


ANTOLOGIA
Instituto Tecnológico Superior de Alvarado

De control del programa

NOP: Hacer nada


HALT: Detenerse (halt)

De transferencia de datos

LIT v: Cargar (Push) la Literal v en Local frame (Lf) de la memoria de


datos.
LLV i: Cargar valor local (Load Local Value) i en Lf.
LGV i: Cargar valor global (Load Local Value) i en Lf.
SLV i: Almacena valor local i (Store Local Value i) en Lf.
SGV i: Almacena valor global i.
LLA i: Cargar Dirección Local i (Load Local Address i) en Lf.
LGA i: Cargar Dirección Global i (Load Global Address i) en Lf.
POP n: Extrae n valores (Pop n values)
DUP: DUPlica el tope de la pila.
SWAP: Intercambia los 2 valores superiores de la pila.

Aritméticas y lógicas

UOP i: Operación unitaria i (Unary Operation i): const X = Pop Lf.


Push (Unop (i, X)) en Lf
BOP i: Operación binaria i: const Xr, Xl = Pop Lf, Pop Lf
Push (Binop(i,Xl,Xr)) en L

De control de flujo

CALL n: Llamad a una subrutina.


RTN n: Retorno de rutina.
GOTO L: I <- L Salto incondicional a L
COND L M: I <- if Pop Lf = True # Pop Stack. Si el valor es:
then L # Verdadero, salta a L else M # Falso, salta a M. fi
CODE F: Push F on Lf # Carga el punto de entrada.
SOS i: Llamada a la función i del sistema operativo

Para las operaciones aritméticas y lógicas tenemos que:

Unop (i, X) significa:

PROGRAMACION LENGUAJE Y AUTOMATAS II 37


ANTOLOGIA
Instituto Tecnológico Superior de Alvarado

case i of
UNOT: not (X)
UNEG: -(X)
USUCC: X+1
UPRED: X-1

Binop(i,Xl,Xr)
case i of
BAND : Xl and Xr
BOR : Xl or Xr
Código generado por
BPLUS : Xl + Xr el compilador de
BMINUS : Xl - Xr
Tiny
BMULT : Xl * Xr
BDIV : Xl div Xr
Cuando compilamos BMOD : Xl mod Xr un programa de Tiny
BEQ : Xl = Xr
se generan varios BNE : Xl <> Xr archivos a partir del
BLE : Xl <= Xr
programa fuente; estos archivos son:
BGE : Xl >= Xr
BLT : Xl < Xr
BGT : Xl > Xr
 _CONS: Creado por el
analizador semántico, contiene comentarios sobre el cumplimiento de
las restricciones de Tiny.

 _CGEN: Creado por el generador de código, con comentarios sobre la


traducción.

 Contiene además la traducción de las instrucciones del programa fuente,


enriquecido con algunos comentarios útiles para entender mejor el
proceso de traducción.

PROGRAMACION LENGUAJE Y AUTOMATAS II 38


ANTOLOGIA
Instituto Tecnológico Superior de Alvarado

 _TREE: Creado por el analizador sintáctico, contiene el árbol sintáctico


obtenido a partir del programa fuente.

 _CODE: Creado por el generador de código, contiene el programa


objeto, sin comentarios.

Es importante anotar que estos archivos se sobrescriben cada vez que compilo
un programa en Tiny. Esto es útil porque me permite analizar el código
generado por el compilador de Tiny para el último programa traducido, y se
evita el uso inmoderado del espacio en disco. A manera de ejemplo, vamos a
ver el código generado por el compilador de Tiny para el programa de prueba
p7, que se muestra a continuación:

Program x:
begin
output (3)
end x.

Recuerda que para compilar este programa debes ejecutar el programa


Cygwin. El comando para compilar este archivo, está ubicado en el directorio
tiny, es:

./tc tests/p7
En la siguiente figura se muestra el resultado de la compilación y ejecución de
p7. También se pueden observar los comandos "cd" para posicionarme en el
directorio de tiny:

A continuación, te muestro el contenido del archivo _CGEN creador por el


compilador de Tiny:

<<< CODE GENERATOR>>> Processing Node program, Label is


<<< CODE GENERATOR>>> Processing Node dclns, Label is
<<< CODE GENERATOR>>> Processing Node block, Label is
<<< CODE GENERATOR>>> Processing Node output, Label is
<<< CODE GENERATOR>>> Processing Node<integer>, Label is

PROGRAMACION LENGUAJE Y AUTOMATAS II 39


ANTOLOGIA
Instituto Tecnológico Superior de Alvarado

LIT 3
Incrementing Frame size to 1
SOS OUTPUT
Decrementing Frame size to 0
SOS OUTPUTL
HALT

Recuerda que el generador de código recorre el árbol sintáctico generado por


el analizador sintáctico y revisado por el analizador semántico, para ir
generando el código objeto.

Observa que el primer nodo analizado es el nodo program, después el nombre


del programa, que es un nodo de cln, posteriormente un nodo block (bloque),
cuyo inicio es indicado por el begin, después una instrucción de salida (output),
que recibe como parámetro un nodo entero (3, en este caso). Esta instrucción
output (3) se traduce por las instrucciones lit 3, sos output y sos outputl.
Posteriormente, la instrucción end x. que se traduce por halt.
El contenido del archivo _CODE es el siguiente:

LIT 3
SOS OUTPUT
SOS OUTPUTL
HALT

Fácilmente puede verse que es el mismo código objeto que aparece en el


archivo _CGEN, pero sin comentario alguno. Este archivo nos muestra el
contenido del archivo ejecutable que se "cargará" al intérprete de la máquina
abstracta para su ejecución.

 Ejemplo del código generado por el compilador de Tiny para una


estructura selectiva

PROGRAMACION LENGUAJE Y AUTOMATAS II 40


ANTOLOGIA
Instituto Tecnológico Superior de Alvarado

Para ilustrar el código generado por el compilador de Tiny para una estructura
selectiva, vamos a analizar brevemente el código generado por el siguiente
programa:

program
ejemplo2: begin
if (1<=2) then output (1) else output (2)
end ejemplo2.

El contenido del archivo _CGEN es el siguiente:

<<<CODE GENERATOR>>> Processing Node program, Label is


<<<CODE GENERATOR>>> Processing Node dclns, Label is
<<< CODE GENERATOR>>> Processing Node block, Label is
<<< CODE GENERATOR >>> Processing Node if, Label is
<<< CODE GENERATOR >>> Processing Node <=, Label is
<<< CODE GENERATOR>>> Processing Node <integer>, Label is LIT 1
Incrementing Frame size to 1

Observa el orden del recorrido del árbol sintáctico en el archivo _CGEN:

 Node program: Siempre debe iniciar con este nodo.

 Node dclns: Y continuar con éste, por el nombre del programa.

 Node block: Inicio del begin.

 Node if: En este ejemplo, primera instrucción del programa.

 Node <=: Operador relacional de la condición del if.

 Node <integer>: Entero 1, etcétera.

PROGRAMACION LENGUAJE Y AUTOMATAS II 41


ANTOLOGIA
Instituto Tecnológico Superior de Alvarado

El código ejecutable de este programa, que es el contenido del archivo _CODE,


es el siguiente:

LIT 1
LIT 2
BOP BLE
COND L1 L2
L1 LIT 1
SOS OUTPUT
SOS OUTPUTL
GOTO L3
L2 LIT 2
SOS OUTPUT
SOS OUTPUTL
NOP
L3 HALT

Que realiza las siguientes operaciones:

1. Coloca el número 1 en el tope de la pila (LIT 1).

2. Coloca el número 2 en el tope de la pila (LIT 2).

3. Realiza la operación binaria (con dos operando) BOP BLE (Branch if


less or equal, salta si es menor o igual), que compara los dos datos en el
tope de la pila.

4. Si la condición es verdadera (COND L1 L2), salta a L1, si no salta a L2.

5. L1: Coloca el 1 en el tope de la pila (LIT 1) y llama a la función output


(SOS OUTPUT) del SO, para desplegar el tope de la pila, llama a outputl
(SOS OUTPUTL), para desplegar un avance de línea y salta a L3
(GOTO L3), con lo que termina la ejecución del programa.

PROGRAMACION LENGUAJE Y AUTOMATAS II 42


ANTOLOGIA
Instituto Tecnológico Superior de Alvarado

6. L2: Coloca el 2 en el tope de la pila (LIT 2) y llama a la función output del


SO (SOS OUTPUT), para desplegar el tope de la pila, llama a outputl
(SOS OUTPUTL), para desplegar un avance de línea, ejecuta nada
(NOP) y termina (HALT) la ejecución del programa.

Ejemplo del código generado por el compilador de Tiny para una estructura
repetitiva. Para analizar brevemente el código generado por el compilador de
Tiny para una estructura repetitiva, vamos a revisar brevemente el código
generado para el siguiente programa:

Program mientras:
Var i: integer;
begin
while i <=5 do
begin
output(i);
i: = i+1;
end;
end mientras.

El código generado para este programa es el siguiente:

LIT 0
L1 LGV 0
LIT 5
BOP BLE
COND L2 L3
L2 LGV 0
SOS OUTPUT
SOS OUTPUTL
LGV 0
LIT 1
BOP BPLUS
SGV 0
GOTO L1
L3 HALT

Donde:

PROGRAMACION LENGUAJE Y AUTOMATAS II 43


ANTOLOGIA
Instituto Tecnológico Superior de Alvarado

1 LIT 0 es la instrucción que utiliza el compilador de Tiny para definir


una variable (en este caso la variable entera i).

2 LGV 0 (que en este ejemplo está etiquetada con L1), es la instrucción


para referirse a esa primera variable y cargarla en el tope de la pila.

3 Posteriormente carga el número 5 en el tope de la pila (LIT 5) y


compara con la operación BOP BLE (Salta si menor o igual). Si el
contenido de la variable i es menor o igual a 5, entonces salta a L2, si
no salta a L3 (COND L2 L3).

4 L2: Carga i en el tope de la pila (LGV 0), llama a output para que se
despliegue en pantalla (SOS OUTPUT) y despliega un "avance de
línea" llamando a outputl (SOS OUTPUTL).

5 Carga i en el tope de la pila (LGV 0) y el 1 en el tope de la pila (LIT


1).

6 Realiza la operación suma con los dos datos en el tope de la pila y


guarda el resultado en la variable i (BOP BPLUS).

7 Salta incondicionalmente a L1 (GOTO L1), que es la verificación de


la condición del while ().

8 L3: Termina (halt).

BIBLIOGRAFIA

1. Aho, Sethi, Ullman. Compiladores Principios, técnicas y


herramientasEd. Addison Wesley.
2. Lemone Karen A., Fundamentos de compiladores Cómo traducir al
lenguaje de computadora, Ed. Compañía Editorial Continental.

PROGRAMACION LENGUAJE Y AUTOMATAS II 44


ANTOLOGIA
Instituto Tecnológico Superior de Alvarado

3. Kenneth C. Louden. Construcción de compiladores Principios y


práctica.Ed.
4. Thomson.
5. Martin John, Lenguajes formales y teoría de la computación, ED. Mc
Graw Hill
6. Hopcroft John E., Introducción a la Teoría de Autómatas, Lenguajes y
Computación, ED. Addison Wesley
7. Guerra Crespo. Hector. Compiladores. Ed. Tecnologica didáctica.
8. Ronald Mak. Writing compilers and interpreters. Ed. Wiley Computer
Publishing.
9. Fischer, LeBlanc. Crafting a compiler with C. Ed. Cummings
Publishing Company, Inc.
10. Salas Parrilla, Jesús. Sistemas Operativos y Compiladores. McGraw
Hill.
11. Beck. Software de Sistemas, Introducción a la programación de
Sistemas. Addison-Wesley Iberoamericana.
12. Teufel, Schmidt, Teufel. Compiladores Conceptos Fundamentales.
Addison-Wesley Iberoamericana.
13. C. Louden, Kenneth. Lenguajes de programación Principios y
práctica. Thomson.
9 Levine Gutiérrez, Guillermo. Computación y programación moderna
Perspectiva integral de la informática. Pearson Educación.
10 Abel, Peter. Lenguaje ensamblador y programación para PC IBM y
11 compatibles. Pearson Educación.

PROGRAMACION LENGUAJE Y AUTOMATAS II 45

Vous aimerez peut-être aussi