Dos
tipos de representaciones intermedias:En
el modelo de compilación análisis-sintesis, el front-end traduce el programa fuente en una representación de código intermedio, y el
back-end traduce esta representación en
código final.
- Permite crear facilmente un compilador para diferentes maquinas
- La representación intermedia puede ser optimizada por un optimizador independiente del código final
- Lenguajes Intermedios:
- Arboles de Sintaxis
- Código de tres direcciones
Construcción
de árboles sintácticos: El uso de árboles de análisis sintáctico como representación intermedia
permite que la traducción se separe del análisis sintáctico. Las rutinas de
traducción invocadas durante el análisis sintáctico deben activarse con dos
clases de limitaciones. La primera, una gramática que resulte adecuada para le
análisis sintáctico puede no reflejar la estructura jerárquica natural de las
construcciones del lenguaje. La segunda, el método de análisis sintáctico
restringe el orden en que se consideran los nodos de un árbol de análisis sintáctico.
Pues este orden puede no coincidir con el orden en que se va disponiendo de la información
sobre una construcción.
Arboles Sintácticos
Un árbol sintáctico es una forma condensada de un árbol de análisis
sintáctico, útil para representar construcciones de lenguajes. Los operadores y
las palabras no aparecen como hojas sino más bien están asociadas con el nodo
interior que sería el padre de dichas hojas en el árbol análisis sintáctico.
La construcción de un árbol sintáctico para una expresión es similar a
la traducción de la expresión a una forma posfija. Se construyen subárboles
para las subexpresiones creando un nodo para cada operador y para cada
operando. Los hijos de un nodo de un operador son las raíces de los nodos que representan
las subexpresiones que constituyen los operandos de dicho operador. Se puede
implantar cada nodo en un árbol sintáctico como un registro con varios campos.
En el nodo para un operador, un campo identifica el operador y el resto de los
campos contienen apuntadores a los nodos de los operandos. El operador a menudo
se denomina etiqueta del nodo.
Comprobación
estática:
- Comprobación de tipos (compatibilidad)
- Comprobación del flujo de control (en C break en un switch)
- Comprobaciones de unicidad (en Pascal etiquetas en un case)
- Comprobaciones relacionadas con nombres (en Ada, un bloque empieza y termina con el mismo nombre)
- La comprobación estática se realiza en distintas fases de compilación.
- Criterio del diseñador ajustar las comprobaciones a las fases
- El léxico puede detectar unicidad de declaración de variables al insertar en la TS
- El sintáctico puede detectar unicidad de declaración cuando el léxico devuelve token VAR
Algunos compiladores como Pascal realizan comprobación estática y generación de código intermedio durante el
análisis sintáctico (cosas del semántico
se mezclan con el sintáctico)
- Otros como Ada realizan una comprobación de tipos después de terminar el análisis sintáctico y antes de la generación de código intermedio Îcomprobador de tipos (algunas cosas del semántico se separa en una fase más aislada)
- Algunas de las comprobaciones semánticas se pueden resolver mediante la Tabla de Símbolos.
- Independientemente de la fase de compilación que lo gestiones
- Principalmente cuestiones de ámbito de variables en registros, funciones ,etc.
Código
de tres direcciones: Es una manera linear
de representar un grafo de subexpresiones en donde los nombres explícitos corresponden
a los nodos interiores de un grafo), muy útil principalmente a la hora de optimizar. Las instrucciones se representan
utilizando cuando mucho un solo operador en la parte derecha de la expresión, es
decir, una expresión del tipo "x+y*z" se representaría de la manera
"t1=y*z y t2=x+t1".
El código de tres
direcciones se basa en 2 conceptos principalmente: direcciones e
instrucciones.
Las direcciones se pueden definir como: deben tener los suficientes
operadores para satisfacer las instrucciones del lenguaje, y que a su vez sean
cercanos al lenguaje de maquina. Sin embargo, si se crean demasiadas
instrucciones, el optimizados y el generador de código tendrán que trabajar mas
para generar el código final.
Hay dos formas principales de implementar el código de tres direcciones:
Cuádruplas
Una cuádrupla es una estructura tipo registro con cuatro campos que se
llaman (op, result, arg1, arg2). El
campo op contiene un código interno para el operador.
Por ejemplo, la proposición de tres direcciones x = y + z se podría
representar mediante la
cuádrupla (ADD, x,y, z). Las proposiciones con operadores unarios no
usan el arg2. Los campos que no se usan se dejan vacíos o un valor NULL. Como
se necesitan cuatro campos se le llama representación mediante cuádruplas.
Tripletas
Para evitar tener que introducir nombres temporales en la tabla de
símbolos, se hace referencia a un valor temporal según la posición de la
proposición que lo calcula. Las propias instrucciones representan el valor del
nombre temporal. La implementación se hace mediante registros de solo tres
campos (op, arg1, arg2).
En la notación de tripletes se necesita menor espacio y el compilador no
necesita generar los nombres temporales. Sin embargo, en esta notación,
trasladar una proposición que defina un valor temporal exige que se modifiquen
todas las referencias a esa proposición. Lo cual supone un inconveniente a la
hora de optimizar el código, pues a menudo es necesario cambiar proposiciones
de lugar.
0 comentarios:
Publicar un comentario