2.1 Computadores virtuales y binding-time.
Entendemos que un computador es un conjunto integrado de algoritmos y estructuras de datos, capaz de almacenar y ejecutar programas. La construcción efectiva de un computador puede estar dada:
![]() | A través de una producción en hardware, representando las estructuras de datos y algoritmos en los dispositivos físicos. |
![]() | A través de una producción en firmware, representando las estructuras de datos y algoritmos por microprogramación de una comp. hw. |
![]() | A través de simulación de software, representando las estructuras de datos y algoritmos por medio de programas y estructuras de datos en algún otro LP.(cierre algorítmico) |
![]() | A través de alguna combinación de las anteriores. |
Cuando se implementa un LP, se definen las estructuras de datos y algoritmos que se usarán en tiempo de ejecución, para ejecutar los programas en dicho LP. estas estructuras de datos y algoritmos, definen un computador. Por ello se habla del computador virtual definido por el lenguaje. El lenguaje máquina de este computador virtual es el programa ejecutable que produce el traductor del lenguaje, que puede ser código de máquina auténtico si el lenguaje se compila, o puede tener alguna estructura arbitraria si el lenguaje se interpreta. Las estructuras de datos de este computador virtual son las estructuras que se utilizan durante la ejecución del programa. Las operaciones primitivas son aquellas efectivamente ejecutables en tiempo de ejecución, igual ocurre con las estructuras de control de secuencia, de datos y administración de memoria; son las que se emplean en tiempo de ejecución.
2.1.1 Sintaxis y semántica.
La sintaxis de un LP proporciona la forma de escribir el programa en ese LP, esto es, como se escriben las declaraciones, enunciados y otras construcciones del lenguaje(dados por por la gramática, por ejemplo BNF), mientras que la semántica de un LP es el significado que se da a las distintas construcciones sintácticas.
Por ejemplo:
v: array [0..9] of integer;
En Pascal y
int v[10];
en C; tienen distinta sintaxis, puesto que se escriben diferente, pero ambos tienen la misma semántica; es una declaración de un arreglo de 10 enteros.
Para conocer la semántica o significado, es necesario conocer la semántica de Pascal y C; saber por ejemplo que una declaración de este tipo colocada al principio de un subprograma significa crear el vector cada vez que se entra al subprograma y destruirlo cada vez que se sale.
En un manual de un lenguaje, típicamente se describe la sintaxis y luego una explicación de la semántica. En este curso, se describe la semántica del lenguaje en términos de las estructuras de datos y operaciones que se usan en el computador virtual para ese LP. En ocasiones, estas estructuras de datos y operaciones están ligadas directamente con construcciones sintácticas, pero en otros casos la relación puede ser menos directa. Por ejemplo, el computador virtual de Pascal, puede usar el vector v durante la ejecución del programa, donde se ve claramente que v tiene una estructura dada por la declaración anterior; pero además el contador virtual puede tener una pila donde maneja los registros de activación de llamadas recursivas a un subprograma y esta pila no se "ve" en la sintaxis del lenguaje. Estas estructuras "ocultas" son tan importantes como las "visibles" para entender el lenguaje.
2.1.2 Computadoras virtuales e implementación de lenguajes.
Cada vez que un LP se implementa en un computador distinto, el implementador tiende a ver un computador virtual diferente, por lo tanto distintas implementaciones tenderán a usar diferentes conjuntos de estructuras de datos y operaciones, en particular aquellas ocultas.
El implementador tiene libertad para determinar las estructuras de computador virtual que constituyen la base de implementación de su LP.
cuando un LP se está implementando en un computador particular, el implementador determina primero el computador virtual que representa una interpretación de la semántica del LP y luego construye ese computador virtual a partir de los elementos hardware y software que le proporciona el computador real.
Por ejemplo, una operación de suma de enteros es implementada directamente sobre el hardware, mientras que el cálculo de raíces cuadradas, decide implementarlo por software. Si el computador virtual tiene una variable entera X, el implementador puede decidir representar X directamente por una localidad de memoria, o representarla por una localidad que contenga "marca del tipo" que diga "entero", junto con un puntero hacia otra localidad donde se guarda el valor.
El implementador también debe determinar que se va a hacer durante la traducción de un programa y que durante la ejecución.
Así pues, tres factores conducen a diferencias entre implementaciones del mismo lenguaje:
![]() | Diferencias en la concepción del computador virtual implícito en el LP. |
![]() | Diferencias en los recursos que proporciona el computador anfitrión. |
![]() | Diferencias en las decisiones que toma cada implementador en cuanto a cómo simular los elementos del computador virtual usando los recursos que proporciona el computador real. |
2.1.2 Binding y binding-time.
Binding = enlace, ligazón.
Cuando hablamos de binding, hablamos del enlace o ligazón de un elemento de programa a una característica o propiedad particular; igualmente de la elección de la propiedad de un conjunto de propiedades posibles.
El momento durante la formulación o procesamiento del programa en el que se hace esta elección se conoce como tiempo de enlace o binding-time.
Si bien no existe una clasificación del tipo de binding, es posible distinguir unos cuantos binding-time:
Tomemos por ejemplo, el comando de asignación:
x := x+10
Los tipos posibles para una variable (real, entero, booleano, etc.) suelen fijarse en tiempo de definición del lenguaje. Además el lenguaje puede permitir que cada programa defina tipos nuevos, como ADA, Pascal, C, de modo que el conjunto de posibles tipos para x, se fija durante la traducción.
En otros lenguajes como Smalltalk y Prolog el tipo de dato de X sólo puede enlazarse durante la ejecución a través de la asignación de un tipo de valor. En estos lenguajes X puede contener un string en un punto del programa, un número real o entero en otro, y un valor booleano en otro punto del mismo programa.
La ligazón de un valor con X se hace durante la ejecución, pero supongamos que X es real, el conjunto de valores reales que pueden asignarse a X, depende de la representación de los reales en el hardware del computador real que soporta el lenguaje, por lo tanto este binding se realiza en tiempo de implementación.
En la representación de la constante 10, el string 10 para representar el valor diez se liga durante la definición del lenguaje, pero el conjunto de bits que representará este número, por lo general se hace en tiempo de implementación.
En un lenguaje como Pascal, el símbolo "+" se enlaza a un conjunto de operaciones de adición (real, entera, etc.) durante la definición del lenguaje, cada operación se define durante la implementación; cada uso particular del símbolo "+" se enlaza a una operación particular durante la traducción, y el valor particular de cada operando se determina en tiempo de ejecución.
El binding y binding-time es importante puesto que muchas de las diferencias significativas entre los lenguajes implican diferencias en cuanto a tiempos de enlace.
Se dice que un lenguaje como FORTRAN, donde todos los enlaces se efectúan durante la traducción, tiene binding temprano; un lenguaje con binding tardío, como ML, demora casi todos los enlaces hasta el tiempo de ejecución.
Las ventajas y desventajas del binding temprano versus binding tardío giran en torno al conflicto entre eficiencia y flexibilidad. El binding temprano es mucho más eficiente en tiempo de ejecución ya que todo está fijado en la compilación, en cambio el binding tardío toma gran parte del tiempo de ejecución enlazando y rompiendo dichos enlaces; pero a cambio, y por el mismo motivo, tiene una mayor flexibilidad.
En lenguajes como FORTRAN, C, Pascal, donde la eficiencia de ejecución es importante, tienden a realizar la mayor cantidad de bindings en tiempo de traducción, en cambio, lenguajes como ML y Lisp, donde la flexibilidad es importante tienden a retrasar los binding a la ejecución para que puedan hacerse independientes de los datos.