Beruflich Dokumente
Kultur Dokumente
DE ALVARADO
MATERIA:
LENGUAJES DE AUTÓMATAS II
SEMESTRE-GRUPO:
NOVENO SEMESTRE
PRODUCTO ACADÉMICO:
PRESENTA:
DOCENTE:
INDICE
INTRODUCCION.......................................................................................................................1
PROPOSITO Y CONTENIDO..................................................................................................2
OBJETIVO.................................................................................................................................4
POSTERIORES.........................................................................................................................4
CONTENIDO..............................................................................................................................4
UNIDAD 3 OPTIMIZACIÓN...................................................................................................26
BIBLIOGRAFIA.......................................................................................................................44
ANTOLOGIA
Instituto Tecnológico Superior de Alvarado
INTRODUCCION
PROPOSITO Y CONTENIDO
OBJETIVO
CONTENIDO
2.1 Notaciones
2.1.1 Prefija
2.1.2 Infija
2.1.3 Postfija
2.2.2 Código P
2.2.3 Triplos
2.2.4 Cuádruplos.
2.3.2 Expresiones.
2.3.5 Funciones
2.3.6 Estructuras
UNIDAD 3 Optimización
3.1.1 Locales.
3.1.2 Ciclos.
3.1.3 Globales.
3.1.4 De mirilla.
3.2 Costos.
4.1 Registros.
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.
CONTENIDO TEMATICO
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.
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
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.
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
Analizadores LL vs. LR
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.
T→F
T→F*T
F→(E)
F→xF→yF→z
E→E+T
E→E-T
E →T
T→T*FT→T/FT→F
F → -F
F→(E)
F → constante
1. E1→ E2 + T
3. E → T
6. T → F
» F1.val := inverso(F2.val) 8. F → ( E )
» F.val := E.val
9. F → constante
Resumen
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 máquina abstracta
De código
De datos
De direcciones de retorno
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:
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
Conjunto de instrucciones
Las instrucciones que la máquina abstracta puede ejecutar son las siguientes:
De transferencia de datos
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).
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
Lf
De control de flujo
COND L M: I <- if Pop Lf = True # Pop Stack. Si el valor es: then L # Verdadero,
salta a L
BGT: Xl > Xr
Llamadas al sistema
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
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
End copy.
Ejemplo 2
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.
UNIDAD 3 Optimización.
CONTENIDO TEMATICO
Estrategias de optimización
Detección de errores
Errores de compilación
Errores de ejecución
Errores lógicos
Errores de compilación
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
Un ejemplo típico es la división entre cero; por ejemplo, supón que tienes la
siguiente expresión:
Errores lógicos
Las reglas para las operaciones dentro del analizador léxico son las siguientes:
"+"{returnrule(yytext[0]);}
"-" { returnrule(yytext[0]);}
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
Modificaciones al constrainer
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>");
if (nodename == MultNode){
Type1 = expression(T->get_child(0));
Type2 = expression(T->get_child(1));
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);
}
}
if (name == MultNode){
expression(T->get_child(0),CurrLabel);
expression(T->get_child(1),NoLabel);
codegen(NoLabel,BOPOP,BMULT);
dec_framesize();
}
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:
make
Probando la multiplicación
Las instrucciones que la máquina abstracta puede ejecutar son las siguientes:
De transferencia de datos
Aritméticas y lógicas
De control de flujo
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.
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.
./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:
LIT 3
Incrementing Frame size to 1
SOS OUTPUT
Decrementing Frame size to 0
SOS OUTPUTL
HALT
LIT 3
SOS OUTPUT
SOS OUTPUTL
HALT
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.
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
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.
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:
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).
BIBLIOGRAFIA