GENERACIÓN DE CODIGO INTERMEDIO

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.
EJEMPLO PROPUESTO


Scanner reader = new Scanner(System.in);               
int Numero1, Numero2;





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