Articulo de referencia

Diseño e implementación de lenguajes de programación

Los lenguajes de programación se crean típicamente diseñando una forma de representación de un programa informático y escribiendo una implementación para el concepto desarrollad...

Los lenguajes de programación se crean típicamente diseñando una forma de representación de un programa informático y escribiendo una implementación para el concepto desarrollado, [ 1 ] generalmente un intérprete o compilador . Los intérpretes están diseñados para leer programas, generalmente en alguna variación de un formato de texto , y realizar acciones basadas en lo que leen, mientras que los compiladores convierten el código a una forma de nivel inferior, como el código objeto. [ 2 ]

Diseño

En el diseño de lenguajes de programación, existe una amplia variedad de factores a considerar. Algunos factores pueden ser mutuamente excluyentes (por ejemplo, seguridad frente a velocidad). Puede ser necesario considerar si un lenguaje de programación tendrá mejor rendimiento interpretado o compilado, si debe ser de tipado dinámico o estático, si incluirá herencia y la sintaxis general del lenguaje. [ 3 ] Muchos factores relacionados con el diseño de un lenguaje pueden estar determinados por los objetivos del mismo. Es importante considerar el público objetivo del lenguaje, sus características únicas y su propósito. [ 4 ] Es una buena práctica analizar las carencias o dificultades de los lenguajes existentes para asegurar que un lenguaje cumpla su propósito. [ 4 ]

Diversos expertos han sugerido principios de diseño útiles:

  • Como último párrafo de un artículo publicado en 1972, Tony Hoare ha proporcionado algunos consejos generales para cualquier proyecto de software: [ 5 ]
    "En resumen, mi consejo para los diseñadores e implementadores del software del futuro es el siguiente: no decidan exactamente qué van a hacer hasta que sepan cómo hacerlo; y no decidan cómo hacerlo hasta que hayan evaluado su plan según todos los criterios de calidad deseados. Y si no pueden hacerlo, simplifiquen su diseño hasta que lo logren."
  • En un simposio de SIGPLAN en 1973, Tony Hoare analizó con cierto detalle diversos aspectos del lenguaje. [ 6 ] También identificó una serie de deficiencias en los lenguajes de programación (entonces) vigentes.
    "Un lenguaje de programación es una herramienta que debe ayudar al programador en los aspectos más difíciles de su oficio, a saber, el diseño del programa, la documentación y la depuración."
    "Los criterios objetivos para un buen diseño de lenguaje se pueden resumir en cinco frases clave: simplicidad, seguridad, traducción rápida, código objeto eficiente y legibilidad."
    Es absurdo realizar comprobaciones de seguridad exhaustivas en las pruebas de depuración, cuando no se confía en los resultados, y luego eliminarlas en las pruebas de producción, cuando un resultado erróneo podría ser costoso o desastroso. ¿Qué pensaríamos de un aficionado a la vela que usa su chaleco salvavidas durante el entrenamiento en tierra firme, pero se lo quita en cuanto sale al mar?
  • En el Congreso IFIP de 1974, Niklaus Wirth , diseñador de Pascal , presentó un artículo titulado "Sobre el diseño de lenguajes de programación". [ 7 ] Wirth enumeró varias sugerencias contrapuestas, entre las que destacaba que un lenguaje debía ser fácil de aprender y usar, que debía poder utilizarse sin añadir nuevas características, que el compilador debía generar código eficiente, que el compilador debía ser rápido y que un lenguaje debía ser compatible con las bibliotecas, el sistema en el que se ejecutaba y los programas escritos en otros lenguajes.

Muchos lenguajes de programación tienen características de diseño destinadas a facilitar la implementación, al menos, de la primera versión del compilador o intérprete. Por ejemplo, Pascal, Forth y muchos lenguajes ensambladores están diseñados específicamente para admitir la compilación en una sola pasada .

A menudo, los nuevos lenguajes de programación se diseñan para solucionar problemas (percibidos) de lenguajes anteriores, generalmente añadiendo características que (si bien pueden complicar el intérprete o el compilador) simplifican los programas escritos en dichos lenguajes. Por ejemplo, lenguajes con gestión automática de memoria y recolección de basura integradas; lenguajes con matrices asociativas integradas ; etc.

Por otro lado, algunos lenguajes de programación fueron diseñados específicamente para facilitar la escritura de un compilador autoalojado , generalmente omitiendo deliberadamente características que dificultan la compilación, como BCPL , Pascal y RPython .

Implementación

Existen dos enfoques generales para la implementación de lenguajes de programación: [ 8 ]

Además de estos dos extremos, muchas implementaciones utilizan enfoques híbridos, como la compilación justo a tiempo y los intérpretes de código de bytes.

Los intérpretes tienen algunas ventajas sobre los compiladores JIT y los compiladores de ejecución anticipada. [ 10 ] Por lo general, los intérpretes admiten un bucle de lectura-evaluación-impresión que hace que el desarrollo de nuevos programas sea mucho más rápido; los compiladores obligan a los desarrolladores a utilizar un bucle mucho más lento de edición-compilación-ejecución-depuración.

Un programa típico, cuando se compila con un compilador de compilación anticipada (AIT), se ejecutará (después de la compilación) más rápido que el mismo programa procesado y ejecutado con un compilador JIT; que a su vez puede ejecutarse más rápido que ese mismo programa parcialmente compilado en un lenguaje intermedio de código P, como un bytecode , e interpretado por una máquina virtual de aplicaciones ; que a su vez se ejecuta mucho más rápido que un intérprete puro. [ 11 ]

En teoría, primero se puede especificar un lenguaje de programación y luego implementar un intérprete o compilador (modelo en cascada). En la práctica, a menudo lo aprendido durante la implementación del lenguaje influye en versiones posteriores de su especificación, lo que da lugar a un diseño e implementación combinados.

Tanto los intérpretes como los compiladores suelen implementar algún tipo de tabla de símbolos .

Intérpretes

Un intérprete es un programa que lee otro programa, normalmente como texto, [ 4 ] como se ve en lenguajes como Python . [ 2 ] Los intérpretes leen el código y producen el resultado directamente. [ 12 ] Los intérpretes suelen leer el código línea por línea y lo analizan para convertirlo y ejecutarlo como operaciones y acciones. [ 13 ]

Un intérprete se compone de dos partes: un analizador sintáctico y un evaluador . Después de que un intérprete lee un programa como entrada, el analizador sintáctico lo procesa. El analizador sintáctico divide el programa en componentes de lenguaje para formar un árbol de análisis . El evaluador luego utiliza el árbol de análisis para ejecutar el programa. [ 14 ]

Máquina virtual

Una máquina virtual es un tipo especial de intérprete que interpreta el código de bytes. [ 9 ] El código de bytes es un código portátil de bajo nivel similar al código máquina, aunque generalmente se ejecuta en una máquina virtual en lugar de una máquina física. [ 15 ] Para mejorar su eficiencia, muchos lenguajes de programación como Java , [ 15 ] Python , [ 16 ] y C# [ 17 ] se compilan a código de bytes antes de ser interpretados.

Compilador justo a tiempo

Algunas máquinas virtuales incluyen un compilador justo a tiempo (JIT) para mejorar la eficiencia de la ejecución del código de bytes. Mientras la máquina virtual ejecuta el código de bytes, si el compilador JIT determina que una parte del código se utilizará repetidamente, compila esa parte específica a código máquina. A continuación, el compilador JIT almacena el código máquina en la memoria para que la máquina virtual pueda utilizarlo. Los compiladores JIT buscan un equilibrio entre un mayor tiempo de compilación y un tiempo de ejecución más rápido. [ 9 ]

Compiladores

Un compilador traduce programas escritos en un lenguaje a otro. La mayoría de los compiladores se organizan en tres etapas: un front-end , un optimizador y un back-end . El front-end se encarga de comprender el programa. Se asegura de que el programa sea válido y lo transforma en una representación intermedia , una estructura de datos que el compilador utiliza para representar el programa. El optimizador mejora la representación intermedia para aumentar la velocidad o reducir el tamaño del ejecutable que finalmente produce el compilador. El back-end convierte la representación intermedia optimizada al lenguaje de salida del compilador. [ 18 ]

Si un compilador de un lenguaje de alto nivel dado produce otro lenguaje de alto nivel, se le llama transpilador . Los transpiladores se pueden usar para extender lenguajes existentes o para simplificar el desarrollo de compiladores aprovechando implementaciones portátiles y bien optimizadas de otros lenguajes (como C ). [ 9 ]

Son posibles muchas combinaciones de interpretación y compilación, y muchas implementaciones de lenguajes de programación modernos incluyen elementos de ambos. Por ejemplo, el lenguaje de programación Smalltalk se implementa convencionalmente mediante compilación a bytecode , que luego es interpretado o compilado por una máquina virtual . Dado que el bytecode de Smalltalk se ejecuta en una máquina virtual, es portable entre diferentes plataformas de hardware. [ 19 ]

Múltiples implementaciones

Los lenguajes de programación pueden tener múltiples implementaciones. Las diferentes implementaciones pueden estar escritas en diferentes lenguajes y pueden usar diferentes métodos para compilar o interpretar el código. Por ejemplo, las implementaciones de Python incluyen: [ 20 ]

Proceso

Los procesos para crear un lenguaje de programación pueden variar de un desarrollador a otro; sin embargo, aquí se presenta un proceso general sobre cómo se podría crear un lenguaje de programación, que incluye conceptos comunes:

  • Diseño: Se consideran aspectos de diseño, como tipos, sintaxis, semántica y uso de bibliotecas para desarrollar un lenguaje. [ 21 ]
  • Consideración: Se tienen en cuenta la sintaxis, la implementación y otros factores. Lenguajes como Python interpretan el código en tiempo de ejecución, mientras que lenguajes como C++ siguen un enfoque que basa su compilador en el compilador de C. [ 22 ]
  • Crear una implementación: Se escribe una primera implementación. Los compiladores la convertirán a otros formatos, generalmente hasta llegar a lenguaje ensamblador de bajo nivel, incluso a binario. [ 23 ]
  • Mejora tu implementación: Las implementaciones deben mejorarse. Amplía el lenguaje de programación, con el objetivo de que tenga la funcionalidad suficiente para autoarrancarse , de modo que un lenguaje de programación sea capaz de escribir una implementación de sí mismo.
  • Arranque inicial: Si se utiliza un compilador, un desarrollador puede emplear el proceso de arranque inicial, en el que se reescribe el compilador del lenguaje de programación. [ 24 ] Esto resulta útil para la detección de errores y para demostrar su capacidad. [ 25 ] El arranque inicial también ofrece la ventaja de que, a partir de ese momento, solo es necesario programar el lenguaje en sí.

Referencias

  1. Tomassetti, Federico (8 de mayo de 2019). "¿Cómo podría crear un lenguaje de programación?" . Strumenta . Consultado el 3 de marzo de 2023 .
  2. 1 2 "Compilador vs Intérprete" . Geeks For Geeks . 17 de enero de 2022. Consultado el 3 de marzo de 2023 .
  3. "Lenguajes de programación y aprendizaje" . Washington EDU . Universidad de Washington . Consultado el 2 de marzo de 2023 .
  4. 1 2 3 "¿Cómo se crean los lenguajes de programación?" . GoNoCode . 8 de diciembre de 2021 . Consultado el 2 de marzo de 2023 .
  5. Hoare, CAR (1972). "La calidad del software" . Software: Práctica y experiencia . 2 (2): 103– 105. doi : 10.1002/spe.4380020202 . S2CID 62662609 . 
  6. "Sugerencias sobre el diseño de lenguajes de programación" (PDF) . 1973. Consultado el 7 de marzo de 2023 .
  7. "Sobre el diseño de lenguajes de programación" (PDF) . 1974. Consultado el 9 de marzo de 2023 .
  8. Ranta, Aarne (6 de febrero de 2012). Implementación de lenguajes de programación (PDF) . Publicaciones universitarias. págs. 16-18 . ISBN  9781848900646. Archivado (PDF) del original el 7 de noviembre de 2020. Recuperado el 22 de marzo de 2020 .
  9. 1 2 3 4 5 Baker, Greg. "Implementaciones de lenguaje" . Ciencias de la Computación - Universidad Simon Fraser . Archivado del original el 8 de marzo de 2019. Recuperado el 22 de marzo de 2020 .
  10. KernelTrap. "Intérpretes de código de bytes más eficientes en lugar de compilación justo a tiempo" .
  11. Larry Fish. "La historia detrás de Apex/XPL0 y el Grupo 6502" .
  12. Diver, Laurence (7 de diciembre de 2021). "Publicado el 7 de diciembre de 2021 Interpretando la(s) regla(s) del código: rendimiento, performatividad y producción" . MIT Computational Law Report .
  13. Rathi, Mukul (31 de marzo de 2017). "Cómo escribí mi propio lenguaje de programación "correcto"" . mukulrathi . Consultado el 2 de marzo de 2023 .
  14. Evans, David (19 de agosto de 2011). Introducción a la informática (PDF) . Universidad de Virginia. pág. 211. Recuperado el 22 de marzo de 2020 . 
  15. 1 2 Sridhar, Jay (29 de agosto de 2017). "Por qué la máquina virtual de Java ayuda a que su código se ejecute mejor" . MakeUseOf . Recuperado el 22 de marzo de 2020 .
  16. Bennett, James (23 de abril de 2018). "Una introducción al bytecode de Python" . Opensource.com . Consultado el 22 de marzo de 2020 .
  17. Ali, Mirza Farrukh (12 de octubre de 2017). "Common Language Runtime (CLR) DotNet" . Medium . Consultado el 22 de marzo de 2020 .
  18. Cooper, Keith; Torczon, Linda (7 de febrero de 2011). Ingeniería de un compilador (2.ª ed.). Morgan Kaufmann. págs. 6-9 . ISBN   9780120884780.
  19. Lewis, Simon (11 de mayo de 1995). El arte y la ciencia de la conversación informal (PDF) . Prentice Hall. págs. 20-21 . ISBN  9780133713459Consultado el 23 de marzo de 2020 .
  20. "Implementaciones alternativas de Python" . Python.org . Consultado el 23 de marzo de 2020 .
  21. Chouchanian, Vic. "Lenguajes de programación" . Universidad Estatal de California Northridge . Consultado el 2 de marzo de 2023 .
  22. Stroustrup, Bjarne. "Una historia de C++ : 1979-1991" (PDF) . Archivado (PDF) del original el 2 de febrero de 2019. Recuperado el 18 de julio de 2013 . 
  23. Ferguson, Andrew. "Historia de los lenguajes de programación informática" . Universidad de Brown . Consultado el 2 de marzo de 2023 .
  24. Glück, Robert (2012). "Bootstrapping compiler generators from partial evaluators". En Clarke, Edmund; Virbitskaite, Irina; Voronkov, Andrei (eds.). Perspectives of Systems Informatics: 8th International Andrei Ershov Memorial Conference, PSI 2011, Novosibirsk, Rusia, 27 de junio - 1 de julio de 2011, Revised Selected Papers . Lecture Notes in Computer Science. Vol. 7162. Springer. pp. 125– 141. doi : 10.1007/978-3-642-29709-0_13 . Getting started presenta el problema del huevo y la gallina familiar de la construcción de compiladores: se necesita un compilador para arrancar un compilador, y el arranque de generadores de compiladores no es una excepción.  
  25. "Instalación de GCC: Compilación" . Proyecto GNU - Fundación del Software Libre (FSF) .
  • Logotipo de Wikimedia CommonsContenido multimedia relacionado con la recopilación y la creación de enlaces en Wikimedia Commons.