Intérprete (informática)

Programa que ejecuta código fuente sin un paso de compilación separado
Patrón de diseño de intérprete UML de W3sDesign

En informática , un intérprete es un programa informático que ejecuta directamente instrucciones escritas en un lenguaje de programación o scripting , sin necesidad de que hayan sido compiladas previamente en un programa en lenguaje máquina . Un intérprete generalmente utiliza una de las siguientes estrategias para la ejecución del programa:

  1. Analizar el código fuente y ejecutar su comportamiento directamente;
  2. Traducir el código fuente a alguna representación intermedia eficiente o código objeto y ejecutarlo inmediatamente;
  3. Ejecutar explícitamente el bytecode precompilado almacenado [1] creado por un compilador y coincidente con la máquina virtual del intérprete .

Las primeras versiones del lenguaje de programación Lisp y los dialectos BASIC para minicomputadoras y microcomputadoras serían ejemplos del primer tipo. Perl , Raku , Python , MATLAB y Ruby son ejemplos del segundo, mientras que UCSD Pascal es un ejemplo del tercer tipo. Los programas fuente se compilan con anticipación y se almacenan como código independiente de la máquina, que luego se vincula en tiempo de ejecución y se ejecuta por un intérprete y/o compilador (para sistemas JIT ). Algunos sistemas, como Smalltalk y versiones contemporáneas de BASIC y Java , también pueden combinar dos y tres tipos. [2] También se han construido intérpretes de varios tipos para muchos lenguajes tradicionalmente asociados con la compilación, como Algol , Fortran , Cobol , C y C++ .

Si bien la interpretación y la compilación son los dos medios principales por los que se implementan los lenguajes de programación, no son mutuamente excluyentes, ya que la mayoría de los sistemas de interpretación también realizan algún trabajo de traducción, al igual que los compiladores. Los términos " lenguaje interpretado " o " lenguaje compilado " significan que la implementación canónica de ese lenguaje es un intérprete o un compilador, respectivamente. Un lenguaje de alto nivel es idealmente una abstracción independiente de implementaciones particulares.

Historia

Los intérpretes se empezaron a utilizar en 1952 para facilitar la programación dentro de las limitaciones de los ordenadores de la época (por ejemplo, la escasez de espacio de almacenamiento de programas o la falta de soporte nativo para números de coma flotante). Los intérpretes también se utilizaban para traducir entre lenguajes de máquina de bajo nivel, lo que permitía escribir código para máquinas que todavía estaban en construcción y probarlo en ordenadores que ya existían. [3] El primer lenguaje de alto nivel interpretado fue Lisp . Lisp fue implementado por primera vez por Steve Russell en un ordenador IBM 704. Russell había leído el artículo de John McCarthy , "Funciones recursivas de expresiones simbólicas y su cálculo por máquina, parte I", y se dio cuenta (para sorpresa de McCarthy) de que la función eval de Lisp podía implementarse en código máquina. [4] El resultado fue un intérprete de Lisp funcional que podía utilizarse para ejecutar programas de Lisp o, más propiamente, "evaluar expresiones de Lisp".

El desarrollo de los intérpretes de edición estuvo influenciado por la necesidad de computación interactiva. En la década de 1960, la introducción de sistemas de tiempo compartido permitió que varios usuarios accedieran a una computadora simultáneamente, y los intérpretes de edición se volvieron esenciales para administrar y modificar código en tiempo real. Los primeros intérpretes de edición probablemente se desarrollaron para computadoras mainframe, donde se usaban para crear y modificar programas sobre la marcha. Uno de los primeros ejemplos de un intérprete de edición es el sistema EDT (Editor and Debugger for the TECO), que se desarrolló a fines de la década de 1960 para la computadora PDP-1. EDT permitía a los usuarios editar y depurar programas utilizando una combinación de comandos y macros, allanando el camino para los editores de texto modernos y los entornos de desarrollo interactivos. [ cita requerida ]

Operación general

Un intérprete suele estar formado por un conjunto de comandos conocidos que puede ejecutar y una lista de estos comandos en el orden en que el programador desea ejecutarlos. Cada comando (también conocido como Instrucción ) contiene los datos que el programador desea mutar e información sobre cómo hacerlo. Por ejemplo, un intérprete podría leerlo ADD Books, 5e interpretarlo como una solicitud para agregar cinco a la Books variable .

Los intérpretes tienen una amplia variedad de instrucciones que están especializadas para realizar diferentes tareas, pero normalmente encontrará instrucciones de intérprete para operaciones matemáticas básicas , ramificaciones y administración de memoria , lo que hace que la mayoría de los intérpretes sean completos de Turing . Muchos intérpretes también están estrechamente integrados con un recolector de basura y un depurador .

Compiladores versus intérpretes

Ilustración del proceso de vinculación. Los archivos de objetos y las bibliotecas estáticas se ensamblan en una nueva biblioteca o ejecutable.

Los programas escritos en un lenguaje de alto nivel son ejecutados directamente por algún tipo de intérprete o convertidos en código máquina por un compilador (y ensamblador y enlazador ) para que la CPU los ejecute.

Aunque los compiladores (y ensambladores) generalmente producen código de máquina directamente ejecutable por hardware de computadora, a menudo pueden (opcionalmente) producir una forma intermedia llamada código objeto . Este es básicamente el mismo código específico de la máquina pero aumentado con una tabla de símbolos con nombres y etiquetas para hacer que los bloques ejecutables (o módulos) sean identificables y reubicables. Los programas compilados generalmente usarán bloques de construcción (funciones) guardados en una biblioteca de dichos módulos de código objeto. Se utiliza un enlazador para combinar archivos de biblioteca (prefabricados) con el archivo o archivos objeto de la aplicación para formar un solo archivo ejecutable. Por lo tanto, los archivos objeto que se usan para generar un archivo ejecutable a menudo se producen en diferentes momentos y, a veces, incluso por diferentes lenguajes (capaces de generar el mismo formato de objeto).

Un intérprete simple escrito en un lenguaje de bajo nivel (por ejemplo, lenguaje ensamblador ) puede tener bloques de código de máquina similares que implementan funciones del lenguaje de alto nivel almacenadas y ejecutadas cuando la entrada de una función en una tabla de búsqueda apunta a ese código. Sin embargo, un intérprete escrito en un lenguaje de alto nivel normalmente utiliza otro enfoque, como generar y luego recorrer un árbol de análisis , o generar y ejecutar instrucciones intermedias definidas por software, o ambas cosas.

Por lo tanto, tanto los compiladores como los intérpretes generalmente convierten el código fuente (archivos de texto) en tokens, ambos pueden (o no) generar un árbol de análisis y ambos pueden generar instrucciones inmediatas (para una máquina de pila , código cuádruple o por otros medios). La diferencia básica es que un sistema compilador, que incluye un enlazador (integrado o separado), genera un programa de código de máquina independiente , mientras que un sistema intérprete, en cambio, realiza las acciones descritas por el programa de alto nivel.

De este modo, un compilador puede realizar casi todas las conversiones desde la semántica del código fuente al nivel de máquina de una vez por todas (es decir, hasta que se deba cambiar el programa), mientras que un intérprete tiene que realizar parte de este trabajo de conversión cada vez que se ejecuta una sentencia o función. Sin embargo, en un intérprete eficiente, gran parte del trabajo de traducción (incluido el análisis de tipos y similares) se descarta y se realiza solo la primera vez que se ejecuta un programa, módulo, función o incluso sentencia, por lo que es bastante similar a cómo funciona un compilador. Sin embargo, un programa compilado aún se ejecuta mucho más rápido, en la mayoría de las circunstancias, en parte porque los compiladores están diseñados para optimizar el código y se les puede dar suficiente tiempo para esto. Esto es especialmente cierto para lenguajes de alto nivel más simples sin (muchas) estructuras de datos dinámicas, verificaciones o comprobación de tipos .

En la compilación tradicional, la salida ejecutable de los enlazadores (archivos .exe o .dll o una biblioteca, ver imagen) es típicamente reubicable cuando se ejecuta bajo un sistema operativo general, de manera muy similar a los módulos de código objeto, pero con la diferencia de que esta reubicación se realiza dinámicamente en tiempo de ejecución, es decir, cuando el programa se carga para su ejecución. Por otro lado, los programas compilados y enlazados para pequeños sistemas embebidos suelen estar asignados estáticamente, a menudo codificados en una memoria flash NOR , ya que a menudo no hay almacenamiento secundario ni sistema operativo en este sentido.

Históricamente, la mayoría de los sistemas de interpretación tenían un editor autónomo integrado. Esto se está volviendo más común también para los compiladores (en aquel entonces llamados IDE ), aunque algunos programadores prefieren usar un editor de su elección y ejecutar el compilador, el enlazador y otras herramientas manualmente. Históricamente, los compiladores son anteriores a los intérpretes porque el hardware en ese momento no podía soportar tanto el intérprete como el código interpretado y el entorno de procesamiento por lotes típico de la época limitaba las ventajas de la interpretación. [5]

Ciclo de desarrollo

Durante el ciclo de desarrollo de software , los programadores realizan cambios frecuentes en el código fuente. Cuando se utiliza un compilador, cada vez que se realiza un cambio en el código fuente, deben esperar a que el compilador traduzca los archivos fuente modificados y vincule todos los archivos de código binario antes de que se pueda ejecutar el programa. Cuanto más grande sea el programa, más larga será la espera. Por el contrario, un programador que utiliza un intérprete espera mucho menos, ya que el intérprete generalmente solo necesita traducir el código en el que se está trabajando a una representación intermedia (o no traducirlo en absoluto), por lo que requiere mucho menos tiempo antes de que se puedan probar los cambios. Los efectos son evidentes al guardar el código fuente y volver a cargar el programa. El código compilado generalmente se depura con menos facilidad, ya que la edición, la compilación y la vinculación son procesos secuenciales que deben realizarse en la secuencia adecuada con un conjunto adecuado de comandos. Por esta razón, muchos compiladores también tienen una ayuda ejecutiva, conocida como Makefile y programa. El Makefile enumera las líneas de comando del compilador y del enlazador y los archivos de código fuente del programa, pero puede tomar una entrada de menú de línea de comando simple (por ejemplo, "Make 3") que selecciona el tercer grupo (conjunto) de instrucciones y luego emite los comandos al compilador y al enlazador que alimenta los archivos de código fuente especificados.

Distribución

Un compilador convierte el código fuente en instrucciones binarias para la arquitectura de un procesador específico, lo que lo hace menos portable . Esta conversión se realiza solo una vez, en el entorno del desarrollador, y luego el mismo binario se puede distribuir a las máquinas del usuario donde se puede ejecutar sin más traducción. Un compilador cruzado puede generar código binario para la máquina del usuario incluso si tiene un procesador diferente al de la máquina donde se compila el código.

Un programa interpretado puede distribuirse como código fuente. Debe traducirse en cada máquina final, lo que lleva más tiempo pero hace que la distribución del programa sea independiente de la arquitectura de la máquina. Sin embargo, la portabilidad del código fuente interpretado depende de que la máquina de destino tenga un intérprete adecuado. Si el intérprete debe proporcionarse junto con el código fuente, el proceso de instalación general es más complejo que la entrega de un ejecutable monolítico, ya que el intérprete en sí es parte de lo que se debe instalar.

El hecho de que los humanos puedan leer y copiar fácilmente el código interpretado puede ser un motivo de preocupación desde el punto de vista de los derechos de autor . Sin embargo, existen varios sistemas de cifrado y ofuscación . La entrega de código intermedio, como el bytecode, tiene un efecto similar a la ofuscación, pero el bytecode podría decodificarse con un descompilador o desensamblador . [ cita requerida ]

Eficiencia

La principal desventaja de los intérpretes es que un programa interpretado normalmente se ejecuta más lentamente que si hubiera sido compilado . La diferencia en velocidades puede ser minúscula o grande; a menudo de un orden de magnitud y, a veces, más. Generalmente, lleva más tiempo ejecutar un programa con un intérprete que ejecutar el código compilado, pero puede llevar menos tiempo interpretarlo que el tiempo total requerido para compilarlo y ejecutarlo. Esto es especialmente importante cuando se crean prototipos y se prueba el código, cuando un ciclo de edición-interpretación-depuración a menudo puede ser mucho más corto que un ciclo de edición-compilación-ejecución-depuración. [6] [7]

La interpretación del código es más lenta que la ejecución del código compilado porque el intérprete debe analizar cada enunciado del programa cada vez que se ejecuta y luego realizar la acción deseada, mientras que el código compilado simplemente realiza la acción dentro de un contexto fijo determinado por la compilación. Este análisis en tiempo de ejecución se conoce como "sobrecarga interpretativa". El acceso a las variables también es más lento en un intérprete porque la asignación de identificadores a ubicaciones de almacenamiento debe realizarse repetidamente en tiempo de ejecución en lugar de en tiempo de compilación . [6]

Existen varios compromisos entre la velocidad de desarrollo cuando se utiliza un intérprete y la velocidad de ejecución cuando se utiliza un compilador. Algunos sistemas (como algunos Lisp ) permiten que el código interpretado y compilado se llamen entre sí y compartan variables. Esto significa que una vez que una rutina se ha probado y depurado bajo el intérprete, se puede compilar y, por lo tanto, beneficiarse de una ejecución más rápida mientras se desarrollan otras rutinas. [ cita requerida ] Muchos intérpretes no ejecutan el código fuente tal como está, sino que lo convierten en una forma interna más compacta. Muchos intérpretes BASIC reemplazan las palabras clave con tokens de un solo byte que se pueden usar para encontrar la instrucción en una tabla de saltos . [6] Algunos intérpretes, como el intérprete PBASIC , logran niveles aún más altos de compactación del programa utilizando una estructura de memoria de programa orientada a bits en lugar de una orientada a bytes, donde los tokens de comandos ocupan quizás 5 bits, las constantes nominalmente de "16 bits" se almacenan en un código de longitud variable que requiere 3, 6, 10 o 18 bits, y los operandos de dirección incluyen un "desplazamiento de bits". Muchos intérpretes BASIC pueden almacenar y leer su propia representación interna tokenizada.

Un intérprete podría utilizar el mismo analizador léxico y sintáctico que el compilador y luego interpretar el árbol sintáctico abstracto resultante. En el recuadro se muestran ejemplos de definiciones de tipos de datos para este último y un intérprete de juguete para árboles sintácticos obtenidos a partir de expresiones en C.

Regresión

La interpretación no puede utilizarse como único método de ejecución: aunque un intérprete puede ser interpretado por sí mismo, etc., se necesita un programa ejecutado directamente en algún lugar en la parte inferior de la pila porque el código que se está interpretando no es, por definición, el mismo que el código de máquina que la CPU puede ejecutar. [8] [9]

Variaciones

Intérpretes de código de bytes

Existe un espectro de posibilidades entre la interpretación y la compilación, dependiendo de la cantidad de análisis realizado antes de que se ejecute el programa. Por ejemplo, Emacs Lisp se compila a bytecode , que es una representación altamente comprimida y optimizada del código fuente de Lisp, pero no es código máquina (y por lo tanto no está ligado a ningún hardware en particular). Este código "compilado" es luego interpretado por un intérprete de bytecode (escrito en C ). El código compilado en este caso es código máquina para una máquina virtual , que no se implementa en hardware, sino en el intérprete de bytecode. Dichos intérpretes de compilación a veces también se denominan compreters . [10] [11] En un intérprete de bytecode, cada instrucción comienza con un byte y, por lo tanto, los intérpretes de bytecode tienen hasta 256 instrucciones, aunque no se pueden usar todas. Algunos bytecodes pueden ocupar varios bytes y pueden ser arbitrariamente complicados.

Las tablas de control , que no necesariamente necesitan pasar por una fase de compilación, dictan el flujo de control algorítmico apropiado a través de intérpretes personalizados, de manera similar a los intérpretes de código de bytes.

Intérpretes de código enhebrado

Los intérpretes de código enhebrado son similares a los intérpretes de bytecode, pero en lugar de bytes utilizan punteros. Cada "instrucción" es una palabra que apunta a una función o una secuencia de instrucciones, posiblemente seguida de un parámetro. El intérprete de código enhebrado realiza un bucle para obtener instrucciones y llamar a las funciones a las que apuntan, o bien obtiene la primera instrucción y salta a ella, y cada secuencia de instrucciones termina con una búsqueda y un salto a la siguiente instrucción. A diferencia del bytecode, no hay un límite efectivo en el número de instrucciones diferentes, aparte de la memoria disponible y el espacio de direcciones. El ejemplo clásico de código enhebrado es el código Forth utilizado en los sistemas Open Firmware : el lenguaje fuente se compila en "código F" (un bytecode), que luego es interpretado por una máquina virtual . [ cita requerida ]

Intérpretes de árboles de sintaxis abstracta

En el espectro entre la interpretación y la compilación, otro enfoque es transformar el código fuente en un árbol de sintaxis abstracta (AST) optimizado, luego ejecutar el programa siguiendo esta estructura de árbol, o usarlo para generar código nativo just-in-time . [12] En este enfoque, cada oración necesita ser analizada solo una vez. Como ventaja sobre el bytecode, el AST mantiene la estructura global del programa y las relaciones entre las declaraciones (que se pierden en una representación de bytecode), y cuando se comprime proporciona una representación más compacta. [13] Por lo tanto, el uso de AST se ha propuesto como un mejor formato intermedio para compiladores just-in-time que el bytecode. Además, permite que el sistema realice un mejor análisis durante el tiempo de ejecución.

Sin embargo, para los intérpretes, un AST causa más sobrecarga que un intérprete de bytecode, debido a que los nodos relacionados con la sintaxis no realizan ningún trabajo útil, a que la representación es menos secuencial (requiere atravesar más punteros) y a que hay sobrecarga al visitar el árbol. [14]

Recopilación justo a tiempo

Otro aspecto que difumina aún más la distinción entre intérpretes, intérpretes de bytecode y compilación es la compilación just-in-time (JIT), una técnica en la que la representación intermedia se compila en código de máquina nativo en tiempo de ejecución. Esto confiere la eficiencia de ejecutar código nativo, a costa del tiempo de arranque y del mayor uso de memoria cuando se compila por primera vez el bytecode o AST. El primer compilador JIT publicado se atribuye generalmente al trabajo de John McCarthy en LISP en 1960. [15] La optimización adaptativa es una técnica complementaria en la que el intérprete perfila el programa en ejecución y compila sus partes ejecutadas con más frecuencia en código nativo. Esta última técnica tiene algunas décadas de antigüedad, y apareció en lenguajes como Smalltalk en la década de 1980. [16]

La compilación justo a tiempo ha ganado la atención generalizada entre los implementadores de lenguajes en los últimos años, con Java , .NET Framework , la mayoría de las implementaciones modernas de JavaScript y Matlab que ahora incluyen compiladores JIT. [ cita requerida ]

Intérprete de plantillas

Para hacer que la distinción entre compiladores e intérpretes sea aún más vaga, existe un diseño especial de intérprete conocido como intérprete de plantillas. En lugar de implementar la ejecución de código en virtud de una gran sentencia switch que contiene todos los códigos de bytes posibles, mientras se opera en una pila de software o en un recorrido de árbol, un intérprete de plantillas mantiene una gran matriz de códigos de bytes (o cualquier representación intermedia eficiente) asignada directamente a las instrucciones de máquina nativas correspondientes que se pueden ejecutar en el hardware host como pares clave-valor (o en diseños más eficientes, direcciones directas a las instrucciones nativas), [17] [18] conocidas como una "plantilla". Cuando se ejecuta el segmento de código en particular, el intérprete simplemente carga o salta a la asignación del código de operación en la plantilla y la ejecuta directamente en el hardware. [19] [20] Debido a su diseño, el intérprete de plantillas se parece mucho a un compilador justo a tiempo en lugar de a un intérprete tradicional, sin embargo, técnicamente no es un JIT debido al hecho de que simplemente traduce el código del lenguaje en llamadas nativas, un código de operación a la vez, en lugar de crear secuencias optimizadas de instrucciones ejecutables de CPU a partir de todo el segmento de código. Debido al diseño simple del intérprete de simplemente pasar llamadas directamente al hardware en lugar de implementarlas directamente, es mucho más rápido que cualquier otro tipo, incluso los intérpretes de código de bytes, y hasta cierto punto menos propenso a errores, pero como compensación es más difícil de mantener debido a que el intérprete tiene que admitir la traducción a múltiples arquitecturas diferentes en lugar de una máquina/pila virtual independiente de la plataforma. Hasta la fecha, las únicas implementaciones de intérpretes de plantillas de lenguajes ampliamente conocidos que existen son el intérprete dentro de la implementación de referencia oficial de Java, la Sun HotSpot Java Virtual Machine, [17] y el Ignition Interpreter en el motor de ejecución de javascript Google V8.

Auto-intérprete

Un autointérprete es un intérprete de lenguaje de programación escrito en un lenguaje de programación que puede interpretarse a sí mismo; un ejemplo es un intérprete de BASIC escrito en BASIC. Los autointérpretes están relacionados con los compiladores autoalojados .

Si no existe un compilador para el lenguaje a interpretar, la creación de un intérprete propio requiere la implementación del lenguaje en un lenguaje anfitrión (que puede ser otro lenguaje de programación o ensamblador ). Al disponer de un primer intérprete como este, se arranca el sistema y se pueden desarrollar nuevas versiones del intérprete en el propio lenguaje. Fue de esta forma que Donald Knuth desarrolló el intérprete TANGLE para el lenguaje WEB del sistema de composición tipográfica TeX , estándar de facto .

La definición de un lenguaje informático suele hacerse en relación con una máquina abstracta (la llamada semántica operacional ) o como una función matemática ( semántica denotacional ). Un lenguaje también puede definirse mediante un intérprete en el que se da la semántica del lenguaje anfitrión. La definición de un lenguaje por parte de un autointérprete no está bien fundamentada (no puede definir un lenguaje), pero un autointérprete le informa al lector sobre la expresividad y la elegancia de un lenguaje. También permite al intérprete interpretar su código fuente, el primer paso hacia la interpretación reflexiva.

Una dimensión de diseño importante en la implementación de un intérprete automático es si una característica del lenguaje interpretado se implementa con la misma característica en el lenguaje anfitrión del intérprete. Un ejemplo es si un cierre en un lenguaje tipo Lisp se implementa utilizando cierres en el lenguaje del intérprete o se implementa "manualmente" con una estructura de datos que almacena explícitamente el entorno. Cuantas más características implemente la misma característica en el lenguaje anfitrión, menos control tendrá el programador del intérprete; por ejemplo, no se puede lograr un comportamiento diferente para tratar con desbordamientos de números si las operaciones aritméticas se delegan a operaciones correspondientes en el lenguaje anfitrión.

Algunos lenguajes como Lisp y Prolog tienen elegantes auto-intérpretes. [21] Se han llevado a cabo muchas investigaciones sobre auto-intérpretes (particularmente intérpretes reflexivos) en el lenguaje de programación Scheme , un dialecto de Lisp. En general, sin embargo, cualquier lenguaje Turing-completo permite escribir su propio intérprete. Lisp es un lenguaje de este tipo, porque los programas Lisp son listas de símbolos y otras listas. XSLT es un lenguaje de este tipo, porque los programas XSLT están escritos en XML. Un subdominio de la metaprogramación es la escritura de lenguajes específicos de dominio (DSL).

Clive Gifford introdujo [22] una medida de la calidad de los autointérpretes (el eigenratio), el límite de la relación entre el tiempo de computadora empleado en ejecutar una pila de N autointérpretes y el tiempo empleado en ejecutar una pila de N − 1 autointérpretes a medida que N tiende al infinito. Este valor no depende del programa que se esté ejecutando.

El libro Structure and Interpretation of Computer Programs presenta ejemplos de interpretación metacircular para Scheme y sus dialectos. Otros ejemplos de lenguajes con un intérprete propio son Forth y Pascal .

Microcódigo

El microcódigo es una técnica muy utilizada "que impone un intérprete entre el hardware y el nivel arquitectónico de una computadora". [23] Como tal, el microcódigo es una capa de instrucciones a nivel de hardware que implementan instrucciones de código de máquina de nivel superior o secuenciación de máquina de estados interna en muchos elementos de procesamiento digital . El microcódigo se utiliza en unidades centrales de procesamiento de propósito general , así como en procesadores más especializados como microcontroladores , procesadores de señales digitales , controladores de canal , controladores de disco , controladores de interfaz de red , procesadores de red , unidades de procesamiento gráfico y en otro hardware.

El microcódigo suele residir en una memoria especial de alta velocidad y traduce instrucciones de máquina, datos de máquina de estados u otra entrada en secuencias de operaciones detalladas a nivel de circuito. Separa las instrucciones de máquina de la electrónica subyacente para que las instrucciones se puedan diseñar y modificar con mayor libertad. También facilita la creación de instrucciones complejas de varios pasos, al tiempo que reduce la complejidad de los circuitos informáticos. La escritura de microcódigo a menudo se denomina microprogramación y el microcódigo en una implementación de procesador particular a veces se denomina microprograma .

Una microcodificación más extensa permite que microarquitecturas pequeñas y simples emulen arquitecturas más potentes con longitudes de palabra más amplias , más unidades de ejecución , etc., lo cual es una forma relativamente sencilla de lograr compatibilidad de software entre diferentes productos en una familia de procesadores.

Procesador de computadora

Incluso un procesador de computadora sin microcodificación puede considerarse un intérprete de ejecución inmediata escrito en un lenguaje de descripción de hardware de propósito general como VHDL para crear un sistema que analiza las instrucciones del código de máquina y las ejecuta inmediatamente.

Comprender el desempeño del intérprete

Los intérpretes, como los escritos en Java, Perl y Tcl, son necesarios actualmente para una amplia gama de tareas computacionales, incluidas la emulación binaria y las aplicaciones de Internet. El rendimiento de los intérpretes sigue siendo una preocupación a pesar de su adaptabilidad, en particular en sistemas con recursos de hardware limitados. Los enfoques avanzados de instrumentación y seguimiento proporcionan información sobre las implementaciones de los intérpretes y la utilización de los recursos del procesador durante la ejecución mediante evaluaciones de intérpretes adaptados al conjunto de instrucciones MIPS y lenguajes de programación como Tcl, Perl y Java. Las características de rendimiento se ven influidas por la complejidad del intérprete, como lo demuestran las comparaciones con el código compilado. Está claro que el rendimiento del intérprete depende más de los matices y las necesidades de recursos del intérprete que de la aplicación particular que se está interpretando.

Aplicaciones

  • Los intérpretes se utilizan con frecuencia para ejecutar lenguajes de comandos y lenguajes de pegamento , ya que cada operador ejecutado en el lenguaje de comandos suele ser una invocación de una rutina compleja, como un editor o un compilador. [ cita requerida ]
  • El código automodificable se puede implementar fácilmente en un lenguaje interpretado. Esto se relaciona con los orígenes de la interpretación en Lisp y la investigación en inteligencia artificial . [ cita requerida ]
  • Virtualización . El código de máquina destinado a una arquitectura de hardware se puede ejecutar mediante una máquina virtual . Esto se suele utilizar cuando la arquitectura prevista no está disponible o, entre otros usos, para ejecutar varias copias.
  • Sandboxing : si bien algunos tipos de sandboxes dependen de las protecciones del sistema operativo, a menudo se utiliza un intérprete o una máquina virtual. La arquitectura de hardware real y la arquitectura de hardware prevista originalmente pueden ser o no la misma. Esto puede parecer inútil, excepto que los sandboxes no están obligados a ejecutar realmente todas las instrucciones del código fuente que están procesando. En particular, pueden negarse a ejecutar código que viole las restricciones de seguridad bajo las que están operando. [ cita requerida ]
  • Emuladores para ejecutar software de computadora escrito para hardware obsoleto y no disponible en equipos más modernos.

Véase también

Referencias

  1. ^ En este sentido, la CPU también es un intérprete de instrucciones de la máquina.
  2. ^ Aunque este esquema (que combina las estrategias 2 y 3) se utilizó para implementar ciertos intérpretes BASIC ya en la década de 1970, como el eficiente intérprete BASIC del ABC 80 , por ejemplo.
  3. ^ Bennett, JM; Prinz, DG; Woods, ML (1952). "Subrutinas interpretativas". Actas de la Conferencia Nacional de la ACM, Toronto .
  4. ^ Según lo informado por Paul Graham en Hackers & Painters , p. 185, McCarthy dijo: "Steve Russell dijo, mira, ¿por qué no programo este eval ...?", y yo le dije, ho, ho, estás confundiendo teoría con práctica, este eval está pensado para leer, no para computar. Pero él siguió adelante y lo hizo. Es decir, compiló el eval de mi artículo en código de máquina IBM 704 , corrigió el error y luego lo promocionó como un intérprete de Lisp, lo que ciertamente era. Así que en ese momento Lisp tenía esencialmente la forma que tiene hoy..."
  5. ^ "¿Por qué se escribió el primer compilador antes que el primer intérprete?". Ars Technica . 8 de noviembre de 2014 . Consultado el 9 de noviembre de 2014 .
  6. ^ abc Este artículo se basa en material tomado de Interpreter en el Diccionario gratuito en línea de informática antes del 1 de noviembre de 2008 e incorporado bajo los términos de "renovación de la licencia" del GFDL , versión 1.3 o posterior.
  7. ^ "Compiladores vs. intérpretes: explicación y diferencias". Guía digital de IONOS . Consultado el 16 de septiembre de 2022 .
  8. ^ Theodore H. Romer, Dennis Lee, Geoffrey M. Voelker, Alec Wolman, Wayne A. Wong, Jean-Loup Baer, ​​Brian N. Bershad y Henry M. Levy, La estructura y el desempeño de los intérpretes
  9. ^ Terence Parr, Johannes Luber, La diferencia entre compiladores e intérpretes Archivado el 6 de enero de 2014 en Wayback Machine.
  10. ^ Kühnel, Claus (1987) [1986]. "4. Kleincomputer - Eigenschaften und Möglichkeiten" [4. Microcomputadora - Propiedades y posibilidades]. En Erlekampf, Rainer; Mönk, Hans-Joachim (eds.). Mikroelektronik in der Amateurpraxis [ Microelectrónica para el aficionado práctico ] (en alemán) (3 ed.). Berlín: Militärverlag der Deutschen Demokratischen Republik  [de] , Leipzig. pag. 222.ISBN 3-327-00357-2.7469332.
  11. ^ Heyne, R. (1984). "Basic-Compreter für U880" [Compredor BÁSICO para U880 (Z80)]. radio-fernsehn-elektronik  [Delaware] (en alemán). 1984 (3): 150-152.
  12. ^ Representaciones intermedias de AST, foro Lambda the Ultimate
  13. ^ Kistler, Thomas; Franz, Michael (febrero de 1999). "Una alternativa basada en árboles a los códigos de bytes de Java" (PDF) . Revista internacional de programación paralela . 27 (1): 21–33. CiteSeerX 10.1.1.87.2257 . doi :10.1023/A:1018740018601. ISSN  0885-7458. S2CID  14330985 . Consultado el 20 de diciembre de 2020 . 
  14. ^ Surfin' Safari - Blog Archive » Anunciamos SquirrelFish. Webkit.org (2008-06-02). Consultado el 2013-08-10.
  15. ^ Aycock 2003, 2. Técnicas de compilación JIT, 2.1 Génesis, pág. 98.
  16. ^ L. Deutsch, A. Schiffman, Implementación eficiente del sistema Smalltalk-80, Actas del 11º simposio POPL, 1984.
  17. ^ ab "openjdk/jdk". GitHub . 18 de noviembre de 2021.
  18. ^ "Descripción general del entorno de ejecución de HotSpot". Openjdk.java.net . Consultado el 6 de agosto de 2022 .
  19. ^ "Desmitificando la JVM: Variantes de la JVM, Cppinterpreter y TemplateInterpreter". metebalci.com .
  20. ^ "Intérprete de plantillas JVM". ProgrammerSought .
  21. ^ Bondorf, Anders. "Logimix: un evaluador parcial autoaplicable para Prolog". Síntesis y transformación de programas lógicos. Springer, Londres, 1993. 214-227.
  22. ^ Gifford, Clive. "Razones propias de los autointérpretes". Blogger . Consultado el 10 de noviembre de 2019 .
  23. ^ Kent, Allen; Williams, James G. (5 de abril de 1993). Enciclopedia de informática y tecnología: volumen 28, suplemento 13. Nueva York: Marcel Dekker, Inc. ISBN 0-8247-2281-7. Recuperado el 17 de enero de 2016 .

Fuentes

  • Aycock, J. (junio de 2003). "Una breve historia del sistema justo a tiempo". ACM Computing Surveys . 35 (2): 97–113. CiteSeerX  10.1.1.97.3985 . doi :10.1145/857076.857077. S2CID  15345671.
  • Página de intérpretes de tarjetas IBM en la Universidad de Columbia
  • Fundamentos teóricos para la programación totalmente funcional práctica (Capítulo 7 especialmente) Tesis doctoral que aborda el problema de formalizar qué es un intérprete
  • Animación breve que explica la diferencia conceptual clave entre intérpretes y compiladores. Archivado en ghostarchive.org el 9 de mayo de 2022.
Retrieved from "https://en.wikipedia.org/w/index.php?title=Interpreter_(computing)&oldid=1250787345"