Compilador

Programa de computadora que traduce código de un lenguaje de programación a otro.

En informática , un compilador es un programa informático que traduce el código informático escrito en un lenguaje de programación (el lenguaje fuente ) a otro lenguaje (el lenguaje destino ). El nombre "compilador" se utiliza principalmente para los programas que traducen el código fuente de un lenguaje de programación de alto nivel a un lenguaje de programación de bajo nivel (por ejemplo , lenguaje ensamblador , código objeto o código máquina ) para crear un programa ejecutable . [1] [2] : p1  [3]

Existen muchos tipos diferentes de compiladores que producen resultados en diferentes formas útiles. Un compilador cruzado produce código para una CPU o un sistema operativo diferente de aquel en el que se ejecuta el compilador cruzado. Un compilador de arranque suele ser un compilador temporal, que se utiliza para compilar un compilador más permanente o mejor optimizado para un lenguaje.

El software relacionado incluye descompiladores , programas que traducen desde lenguajes de bajo nivel a lenguajes de nivel superior; programas que traducen entre lenguajes de alto nivel, usualmente llamados compiladores de fuente a fuente o transpiladores ; reescritores de lenguaje , usualmente programas que traducen la forma de expresiones sin un cambio de lenguaje; y compiladores-compiladores , compiladores que producen compiladores (o partes de ellos), a menudo de una manera genérica y reutilizable para poder producir muchos compiladores diferentes.

Es probable que un compilador realice algunas o todas las siguientes operaciones, a menudo llamadas fases: preprocesamiento , análisis léxico , análisis sintáctico , análisis semántico ( traducción dirigida por sintaxis ), conversión de programas de entrada a una representación intermedia , optimización de código y generación de código específico de la máquina . Los compiladores generalmente implementan estas fases como componentes modulares, lo que promueve el diseño eficiente y la corrección de las transformaciones de la entrada de origen a la salida de destino. Los fallos del programa causados ​​por un comportamiento incorrecto del compilador pueden ser muy difíciles de rastrear y solucionar; por lo tanto, los implementadores de compiladores invierten un esfuerzo significativo para garantizar la corrección del compilador . [4]

Los compiladores no son el único procesador de lenguaje utilizado para transformar programas fuente. Un intérprete es un software informático que transforma y luego ejecuta las operaciones indicadas. [2] : p2  El proceso de traducción influye en el diseño de los lenguajes informáticos, lo que lleva a una preferencia de compilación o interpretación. En teoría, un lenguaje de programación puede tener tanto un compilador como un intérprete. En la práctica, los lenguajes de programación tienden a estar asociados con solo uno (un compilador o un intérprete).

Historia

Diagrama del funcionamiento de un compilador típico multilenguaje y multiobjetivo

Los conceptos teóricos de computación desarrollados por científicos, matemáticos e ingenieros formaron la base del desarrollo de la computación digital moderna durante la Segunda Guerra Mundial. Los lenguajes binarios primitivos evolucionaron porque los dispositivos digitales solo entienden unos y ceros y los patrones de circuitos en la arquitectura de la máquina subyacente. A fines de la década de 1940, se crearon lenguajes ensambladores para ofrecer una abstracción más viable de las arquitecturas de las computadoras. [5] La capacidad de memoria limitada de las primeras computadoras generó desafíos técnicos sustanciales cuando se diseñaron los primeros compiladores. Por lo tanto, el proceso de compilación necesitaba dividirse en varios programas pequeños. Los programas front-end producen los productos de análisis utilizados por los programas back-end para generar el código de destino. A medida que la tecnología informática proporcionó más recursos, los diseños de compiladores pudieron alinearse mejor con el proceso de compilación.

Por lo general, resulta más productivo para un programador utilizar un lenguaje de alto nivel, por lo que el desarrollo de lenguajes de alto nivel surgió de manera natural a partir de las capacidades que ofrecen las computadoras digitales. Los lenguajes de alto nivel son lenguajes formales que se definen estrictamente por su sintaxis y semántica , que forman la arquitectura del lenguaje de alto nivel. Los elementos de estos lenguajes formales incluyen:

  • Alfabeto , cualquier conjunto finito de símbolos;
  • Cadena , una secuencia finita de símbolos;
  • Idioma , cualquier conjunto de cadenas de un alfabeto.

Las oraciones de un idioma pueden definirse mediante un conjunto de reglas llamadas gramática. [6]

La forma Backus–Naur (BNF) describe la sintaxis de las "oraciones" de un lenguaje. Fue desarrollada por John Backus y utilizada para la sintaxis de Algol 60 . [7] Las ideas derivan de los conceptos de gramática libre de contexto del lingüista Noam Chomsky . [8] "BNF y sus extensiones se han convertido en herramientas estándar para describir la sintaxis de las notaciones de programación. En muchos casos, partes de los compiladores se generan automáticamente a partir de una descripción BNF". [9]

Entre 1942 y 1945, Konrad Zuse diseñó el primer lenguaje de programación (algorítmico) para computadoras llamado Plankalkül ("Cálculo de planos"). Zuse también imaginó un Planfertigungsgerät ("Dispositivo de ensamblaje de planos") para traducir automáticamente la formulación matemática de un programa en una película perforada legible por máquina . [10] Si bien no se produjo una implementación real hasta la década de 1970, presentó conceptos que luego se vieron en APL diseñado por Ken Iverson a fines de la década de 1950. [11] APL es un lenguaje para cálculos matemáticos.

Entre 1949 y 1951, Heinz Rutishauser propuso Superplan , un lenguaje de alto nivel y traductor automático. [12] Sus ideas fueron refinadas posteriormente por Friedrich L. Bauer y Klaus Samelson . [13]

El diseño de lenguajes de alto nivel durante los años de formación de la informática digital proporcionó herramientas de programación útiles para una variedad de aplicaciones:

  • FORTRAN (Formula Translation) para aplicaciones científicas y de ingeniería se considera uno de los primeros lenguajes de alto nivel implementados y el primer compilador optimizador. [14] [ se necesita una fuente de terceros ]
  • COBOL (Lenguaje común orientado a los negocios) evolucionó a partir de A-0 y FLOW-MATIC para convertirse en el lenguaje de alto nivel dominante para aplicaciones comerciales. [15]
  • LISP (Procesador de listas) para cálculo simbólico. [16]

La tecnología de compiladores surgió de la necesidad de una transformación estrictamente definida del programa fuente de alto nivel en un programa de destino de bajo nivel para la computadora digital. El compilador podría considerarse como una interfaz para analizar el código fuente y una interfaz de usuario para sintetizar el análisis en el código de destino. La optimización entre la interfaz de usuario y la interfaz de usuario podría producir un código de destino más eficiente. [17]

Algunos hitos tempranos en el desarrollo de la tecnología de compiladores:

  • Mayo de 1952 : el equipo de Grace Hopper en Remington Rand escribió el compilador para el lenguaje de programación A-0 (y acuñó el término compilador para describirlo), [18] [19] [20] aunque el compilador A-0 funcionaba más como un cargador o enlazador que como la noción moderna de un compilador completo. [21] [22] [23]
  • 1952, antes de septiembre : Un compilador Autocode desarrollado por Alick Glennie para la computadora Manchester Mark I de la Universidad de Manchester es considerado por algunos como el primer lenguaje de programación compilado. [24]
  • 1954–1957 : Un equipo dirigido por John Backus en IBM desarrolló FORTRAN , que suele considerarse el primer lenguaje de alto nivel. En 1957, completaron un compilador de FORTRAN al que generalmente se le atribuye haber introducido el primer compilador inequívocamente completo. [25]
  • 1959 : La Conferencia sobre lenguaje de sistemas de datos (CODASYL) inició el desarrollo de COBOL . El diseño de COBOL se basó en A-0 y FLOW-MATIC. A principios de la década de 1960, COBOL se compiló en múltiples arquitecturas.
  • 1958–1960 : Algol 58 fue el precursor de ALGOL 60. Introdujo bloques de código , un avance clave en el auge de la programación estructurada . ALGOL 60 fue el primer lenguaje en implementar definiciones de funciones anidadas con alcance léxico . Incluía recursión . Su sintaxis se definió utilizando BNF . ALGOL 60 inspiró a muchos lenguajes que lo siguieron. Tony Hoare comentó: "... no solo fue una mejora con respecto a sus predecesores, sino también con respecto a casi todos sus sucesores". [26] [27]
  • 1958–1962 : John McCarthy diseñó LISP en el MIT . [28] Las capacidades de procesamiento de símbolos proporcionaron características útiles para la investigación de inteligencia artificial. En 1962, la versión 1.5 de LISP incluyó algunas herramientas: un intérprete escrito por Stephen Russell y Daniel J. Edwards, un compilador y ensamblador escrito por Tim Hart y Mike Levin. [29]

Los primeros sistemas operativos y software se escribieron en lenguaje ensamblador. En la década de 1960 y principios de la de 1970, el uso de lenguajes de alto nivel para la programación de sistemas todavía era controvertido debido a las limitaciones de recursos. Sin embargo, varias investigaciones y esfuerzos de la industria iniciaron el cambio hacia lenguajes de programación de sistemas de alto nivel, por ejemplo , BCPL , BLISS , B y C.

BCPL (Basic Combined Programming Language) diseñado en 1966 por Martin Richards en la Universidad de Cambridge fue desarrollado originalmente como una herramienta de escritura de compiladores. [30] Se han implementado varios compiladores, el libro de Richards proporciona información sobre el lenguaje y su compilador. [31] BCPL no solo fue un lenguaje de programación de sistemas influyente que todavía se usa en la investigación [32] sino que también proporcionó una base para el diseño de los lenguajes B y C.

BLISS (Basic Language for Implementation of System Software) fue desarrollado para una computadora PDP-10 de Digital Equipment Corporation (DEC) por el equipo de investigación de la Universidad Carnegie Mellon (CMU) de WA Wulf. El equipo de la CMU desarrolló el compilador BLISS-11 un año después, en 1970.

Multics (Multiplexed Information and Computing Service), un proyecto de sistema operativo de tiempo compartido, involucró al MIT , Bell Labs , General Electric (más tarde Honeywell ) y fue dirigido por Fernando Corbató del MIT. [33] Multics fue escrito en el lenguaje PL/I desarrollado por IBM e IBM User Group. [34] El objetivo de IBM era satisfacer los requisitos de programación empresarial, científica y de sistemas. Había otros lenguajes que podrían haberse considerado, pero PL/I ofrecía la solución más completa a pesar de que no se había implementado. [35] Durante los primeros años del proyecto Multics, un subconjunto del lenguaje podía compilarse en lenguaje ensamblador con el compilador Early PL/I (EPL) de Doug McIlory y Bob Morris de Bell Labs. [36] EPL apoyó el proyecto hasta que se pudo desarrollar un compilador de arranque para el PL/I completo. [37]

Bell Labs abandonó el proyecto Multics en 1969 y desarrolló un lenguaje de programación de sistemas B basado en conceptos BCPL, escrito por Dennis Ritchie y Ken Thompson . Ritchie creó un compilador de arranque para B y escribió el sistema operativo Unics (Uniplexed Information and Computing Service) para un PDP-7 en B. Unics finalmente se deletreó Unix.

Bell Labs comenzó el desarrollo y la expansión de C basándose en B y BCPL. El compilador BCPL había sido trasladado a Multics por Bell Labs y BCPL era un lenguaje preferido en Bell Labs. [38] Inicialmente, se utilizó un programa front-end para el compilador B de Bell Labs mientras se desarrollaba un compilador C. En 1971, un nuevo PDP-11 proporcionó el recurso para definir extensiones a B y reescribir el compilador. En 1973, el diseño del lenguaje C estaba esencialmente completo y el núcleo Unix para un PDP-11 se reescribió en C. Steve Johnson comenzó el desarrollo de Portable C Compiler (PCC) para soportar la reorientación de los compiladores C a nuevas máquinas. [39] [40]

La programación orientada a objetos (POO) ofrecía algunas posibilidades interesantes para el desarrollo y mantenimiento de aplicaciones. Los conceptos de POO se remontan a tiempos más antiguos, pero formaban parte de la ciencia del lenguaje LISP y Simula . [41] Bell Labs se interesó en la POO con el desarrollo de C++ . [42] C++ se utilizó por primera vez en 1980 para la programación de sistemas. El diseño inicial aprovechó las capacidades de programación de sistemas del lenguaje C con conceptos de Simula. Las funciones orientadas a objetos se añadieron en 1983. [43] El programa Cfront implementó una interfaz de C++ para el compilador del lenguaje C84. En los años siguientes se desarrollaron varios compiladores de C++ a medida que crecía la popularidad de C++.

En muchos ámbitos de aplicación, la idea de utilizar un lenguaje de nivel superior se impuso rápidamente. Debido a la creciente funcionalidad admitida por los lenguajes de programación más nuevos y a la creciente complejidad de las arquitecturas informáticas, los compiladores se volvieron más complejos.

En 1970, la DARPA (Defense Advanced Research Projects Agency) patrocinó un proyecto de compilador con el equipo de investigación de la CMU de Wulf. El diseño del compilador de calidad de producción-compilador PQCC produciría un compilador de calidad de producción (PQC) a partir de definiciones formales del lenguaje fuente y el lenguaje de destino. [44] PQCC intentó extender el término compilador-compilador más allá del significado tradicional como generador de analizadores sintácticos (por ejemplo, Yacc ) sin mucho éxito. Sería más apropiado hacer referencia a PQCC como un generador de compiladores.

La investigación de PQCC sobre el proceso de generación de código buscó construir un sistema de escritura de compiladores verdaderamente automático. El esfuerzo descubrió y diseñó la estructura de fases del PQC. El compilador BLISS-11 proporcionó la estructura inicial. [45] Las fases incluyeron análisis (front end), traducción intermedia a máquina virtual (middle end) y traducción al destino (back end). TCOL fue desarrollado para la investigación de PQCC para manejar construcciones específicas del lenguaje en la representación intermedia. [46] Las variaciones de TCOL soportaron varios lenguajes. El proyecto PQCC investigó técnicas de construcción automatizada de compiladores. Los conceptos de diseño demostraron ser útiles para optimizar compiladores y compiladores para el lenguaje de programación (desde 1995, orientado a objetos) Ada .

El documento STONEMAN de Ada [a] formalizó el entorno de soporte de programas (APSE) junto con el núcleo (KAPSE) y el mínimo (MAPSE). Un intérprete de Ada NYU/ED apoyó los esfuerzos de desarrollo y estandarización con el Instituto Nacional Estadounidense de Estándares (ANSI) y la Organización Internacional de Estándares (ISO). El desarrollo inicial del compilador de Ada por parte de los Servicios Militares de los EE. UU. incluyó los compiladores en un entorno de diseño integrado completo en línea con el documento STONEMAN . El Ejército y la Marina trabajaron en el proyecto Ada Language System (ALS) orientado a la arquitectura DEC/VAX mientras que la Fuerza Aérea comenzó con el Ada Integrated Environment (AIE) orientado a la serie IBM 370. Si bien los proyectos no proporcionaron los resultados deseados, sí contribuyeron al esfuerzo general en el desarrollo de Ada. [47]

Otros proyectos de compilación de Ada se pusieron en marcha en Gran Bretaña, en la Universidad de York, y en Alemania, en la Universidad de Karlsruhe. En los EE. UU., Verdix (posteriormente adquirida por Rational) entregó el Verdix Ada Development System (VADS) al Ejército. VADS proporcionó un conjunto de herramientas de desarrollo que incluía un compilador. Unix/VADS podía alojarse en una variedad de plataformas Unix, como DEC Ultrix y Sun 3/60 Solaris, destinado a Motorola 68020 en una evaluación CECOM del Ejército. [48] Pronto hubo muchos compiladores de Ada disponibles que pasaron las pruebas de validación de Ada. El proyecto GNU de la Free Software Foundation desarrolló la GNU Compiler Collection (GCC), que proporciona una capacidad básica para soportar múltiples lenguajes y objetivos. La versión de Ada GNAT es uno de los compiladores de Ada más utilizados. GNAT es gratuito, pero también hay soporte comercial; por ejemplo, AdaCore, se fundó en 1994 para proporcionar soluciones de software comerciales para Ada. GNAT Pro incluye GNAT basado en GNU GCC con un conjunto de herramientas para proporcionar un entorno de desarrollo integrado .

Los lenguajes de alto nivel siguieron impulsando la investigación y el desarrollo de compiladores. Las áreas de enfoque incluyeron la optimización y la generación automática de código. Las tendencias en lenguajes de programación y entornos de desarrollo influyeron en la tecnología de compiladores. Se incluyeron más compiladores en distribuciones de lenguajes (PERL, Java Development Kit) y como un componente de un IDE (VADS, Eclipse, Ada Pro). La interrelación e interdependencia de las tecnologías aumentó. La llegada de los servicios web promovió el crecimiento de los lenguajes web y los lenguajes de script. Los scripts se remontan a los primeros días de las interfaces de línea de comandos (CLI), donde el usuario podía ingresar comandos para que los ejecutara el sistema. Los conceptos de User Shell se desarrollaron con lenguajes para escribir programas de shell. Los primeros diseños de Windows ofrecían una capacidad de programación por lotes simple. La transformación convencional de estos lenguajes usaba un intérprete. Si bien no se usan ampliamente, se han escrito compiladores Bash y Batch. Más recientemente, los lenguajes interpretados sofisticados se convirtieron en parte del kit de herramientas de los desarrolladores. Los lenguajes de script modernos incluyen PHP, Python, Ruby y Lua. (Lua se usa ampliamente en el desarrollo de juegos). Todos ellos tienen soporte de intérprete y compilador. [49]

"Cuando el campo de la compilación comenzó a fines de los años 50, su enfoque se limitaba a la traducción de programas de lenguaje de alto nivel a código de máquina... El campo de los compiladores está cada vez más entrelazado con otras disciplinas, incluidas la arquitectura informática, los lenguajes de programación, los métodos formales, la ingeniería de software y la seguridad informática". [50] El artículo "Compiler Research: The Next 50 Years" destacó la importancia de los lenguajes orientados a objetos y Java. La seguridad y la computación paralela se mencionaron entre los futuros objetivos de investigación.

Construcción del compilador

Un compilador implementa una transformación formal de un programa fuente de alto nivel a un programa de destino de bajo nivel. El diseño del compilador puede definir una solución de extremo a extremo o abordar un subconjunto definido que interactúa con otras herramientas de compilación, por ejemplo, preprocesadores, ensambladores y enlazadores. Los requisitos de diseño incluyen interfaces rigurosamente definidas, tanto internamente entre los componentes del compilador como externamente entre los conjuntos de herramientas de apoyo.

En los primeros tiempos, el enfoque adoptado para el diseño de compiladores se veía afectado directamente por la complejidad del lenguaje informático que se iba a procesar, la experiencia de la(s) persona(s) que lo diseñaban y los recursos disponibles. Las limitaciones de recursos llevaron a la necesidad de revisar el código fuente más de una vez.

Un compilador para un lenguaje relativamente simple escrito por una sola persona puede ser una pieza de software única y monolítica. Sin embargo, a medida que el lenguaje fuente se vuelve más complejo, el diseño puede dividirse en varias fases interdependientes. Las fases separadas proporcionan mejoras de diseño que centran el desarrollo en las funciones del proceso de compilación.

Compiladores de una sola pasada frente a compiladores de múltiples pasadas

La clasificación de los compiladores por número de pasadas tiene su origen en las limitaciones de recursos de hardware de las computadoras. La compilación implica realizar mucho trabajo y las primeras computadoras no tenían suficiente memoria para contener un programa que hiciera todo este trabajo. Como resultado, los compiladores se dividieron en programas más pequeños que realizaban cada uno una pasada sobre el código fuente (o alguna representación del mismo) y realizaban parte del análisis y las traducciones necesarias.

La capacidad de compilar en una sola pasada se ha considerado clásicamente como una ventaja porque simplifica el trabajo de escribir un compilador y los compiladores de una sola pasada generalmente realizan compilaciones más rápido que los compiladores de múltiples pasadas . Por lo tanto, en parte debido a las limitaciones de recursos de los primeros sistemas, muchos de los primeros lenguajes se diseñaron específicamente para que pudieran compilarse en una sola pasada (por ejemplo, Pascal ).

En algunos casos, el diseño de una característica del lenguaje puede requerir que un compilador realice más de una pasada sobre el código fuente. Por ejemplo, considere una declaración que aparece en la línea 20 del código fuente que afecta la traducción de una declaración que aparece en la línea 10. En este caso, la primera pasada debe recopilar información sobre las declaraciones que aparecen después de las declaraciones a las que afectan, y la traducción real se realiza durante una pasada posterior.

La desventaja de compilar en una sola pasada es que no es posible realizar muchas de las optimizaciones sofisticadas necesarias para generar código de alta calidad. Puede resultar difícil contar exactamente cuántas pasadas realiza un compilador optimizador. Por ejemplo, las diferentes fases de optimización pueden analizar una expresión muchas veces, pero solo analizar otra expresión una vez.

Dividir un compilador en programas pequeños es una técnica que utilizan los investigadores interesados ​​en producir compiladores demostrablemente correctos. Demostrar la corrección de un conjunto de programas pequeños suele requerir menos esfuerzo que demostrar la corrección de un programa más grande, único y equivalente.

Estructura del compilador de tres etapas

Diseño del compilador

Independientemente del número exacto de fases en el diseño del compilador, las fases se pueden asignar a una de tres etapas. Las etapas incluyen una etapa inicial, una etapa intermedia y una etapa final.

  • El front-end escanea la entrada y verifica la sintaxis y la semántica de acuerdo con un lenguaje fuente específico. Para lenguajes tipados estáticamente, realiza la verificación de tipos mediante la recopilación de información de tipos. Si el programa de entrada es sintácticamente incorrecto o tiene un error de tipo, genera mensajes de error y/o advertencia, que generalmente identifican la ubicación en el código fuente donde se detectó el problema; en algunos casos, el error real puede estar (mucho) antes en el programa. Los aspectos del front-end incluyen análisis léxico, análisis sintáctico y análisis semántico. El front-end transforma el programa de entrada en una representación intermedia (IR) para su posterior procesamiento por el middle-end. Esta IR suele ser una representación de nivel inferior del programa con respecto al código fuente.
  • El extremo intermedio realiza optimizaciones en el IR que son independientes de la arquitectura de CPU a la que se dirige. Esta independencia del código fuente/código de máquina tiene como objetivo permitir que se compartan optimizaciones genéricas entre versiones del compilador que admiten diferentes lenguajes y procesadores de destino. Algunos ejemplos de optimizaciones del extremo intermedio son la eliminación de código inútil ( eliminación de código muerto ) o inalcanzable ( análisis de accesibilidad ), el descubrimiento y la propagación de valores constantes ( propagación constante ), la reubicación del cálculo a un lugar que se ejecuta con menos frecuencia (por ejemplo, fuera de un bucle) o la especialización del cálculo en función del contexto, lo que finalmente produce el IR "optimizado" que utiliza el extremo posterior.
  • El back end toma el IR optimizado del middle end. Puede realizar más análisis, transformaciones y optimizaciones que son específicas para la arquitectura de CPU de destino. El back end genera el código ensamblador dependiente del destino, realizando la asignación de registros en el proceso. El back end realiza la programación de instrucciones , que reordena las instrucciones para mantener ocupadas las unidades de ejecución paralela llenando las ranuras de retardo . Aunque la mayoría de los problemas de optimización son NP-hard , las técnicas heurísticas para resolverlos están bien desarrolladas e implementadas en compiladores de calidad de producción. Normalmente, la salida de un back end es un código de máquina especializado para un procesador y un sistema operativo en particular.

Este enfoque front-end/middle/back-end permite combinar front-ends para diferentes lenguajes con back-ends para diferentes CPU mientras se comparten las optimizaciones del middle-end. [51] Ejemplos prácticos de este enfoque son GNU Compiler Collection , Clang ( compilador C/C++ basado en LLVM ), [52] y Amsterdam Compiler Kit , que tienen múltiples front-ends, optimizaciones compartidas y múltiples back-ends.

Interfaz

Ejemplo de analizador léxico y analizador sintáctico para C. A partir de la secuencia de caracteres " if(net>0.0)total+=net*(1.0+tax/100.0);", el analizador compone una secuencia de tokens y clasifica cada uno de ellos, por ejemplo, como identificador , palabra reservada , literal numérico u operador . El analizador sintáctico transforma la última secuencia en un árbol sintáctico , que luego es tratado por las fases restantes del compilador. El analizador y el analizador sintáctico manejan las partes regulares y las que no están debidamente contextualizadas de la gramática para C , respectivamente.

El front-end analiza el código fuente para crear una representación interna del programa, denominada representación intermedia (IR). También administra la tabla de símbolos , una estructura de datos que asigna cada símbolo del código fuente a información asociada, como ubicación, tipo y alcance.

Si bien el frontend puede ser una única función o programa monolítico, como en un analizador sin escáner , tradicionalmente se implementaba y analizaba como varias fases, que pueden ejecutarse secuencial o simultáneamente. Este método es el preferido debido a su modularidad y separación de preocupaciones . Lo más común es que el frontend se divida en tres fases: análisis léxico (también conocido como lexing o escaneo), análisis sintáctico (también conocido como escaneo o parsing) y análisis semántico . El lexing y el parsing comprenden el análisis sintáctico (sintaxis de palabras y sintaxis de frases, respectivamente) y, en casos simples, estos módulos (el analizador léxico y el parser) se pueden generar automáticamente a partir de una gramática para el lenguaje, aunque en casos más complejos requieren una modificación manual. La gramática léxica y la gramática de frases suelen ser gramáticas libres de contexto , lo que simplifica significativamente el análisis, y la sensibilidad al contexto se maneja en la fase de análisis semántico. La fase de análisis semántico es generalmente más compleja y se escribe a mano, pero puede automatizarse parcial o totalmente mediante gramáticas de atributos . Estas fases se pueden dividir en: análisis léxico, como escaneo y evaluación, y análisis sintáctico, como construcción de un árbol de sintaxis concreto (CST, parse tree) y luego transformación en un árbol de sintaxis abstracto (AST, syntax tree). En algunos casos se utilizan fases adicionales, en particular reconstrucción de líneas y preprocesamiento, pero son poco frecuentes.

Las fases principales del frontend incluyen las siguientes:

  • La reconstrucción de línea convierte la secuencia de caracteres de entrada a una forma canónica lista para el analizador. Los lenguajes queeliminansus palabras clave o permiten espacios arbitrarios dentro de los identificadores requieren esta fase. Losde arriba hacia abajo,de descenso recursivoy controlados por tablas que se usaban en la década de 1960 normalmente leían la fuente un carácter a la vez y no requerían una fase de tokenización separada.Atlas AutocodeeImp(y algunas implementaciones deALGOLyCoral 66) son ejemplos de lenguajes eliminados cuyos compiladores tendrían unade reconstrucción de línea.
  • El preprocesamiento admite la sustitución de macros y la compilación condicional . Normalmente, la fase de preprocesamiento se produce antes del análisis sintáctico o semántico; por ejemplo, en el caso de C, el preprocesador manipula tokens léxicos en lugar de formas sintácticas. Sin embargo, algunos lenguajes como Scheme admiten sustituciones de macros basadas en formas sintácticas.
  • El análisis léxico (también conocido como lexing o tokenización ) divide el texto del código fuente en una secuencia de pequeños fragmentos llamados tokens léxicos . [53] Esta fase se puede dividir en dos etapas: el escaneo , que segmenta el texto de entrada en unidades sintácticas llamadas lexemas y les asigna una categoría; y la evaluación , que convierte los lexemas en un valor procesado. Un token es un par que consta de un nombre de token y un valor de token opcional. [54] Las categorías de token comunes pueden incluir identificadores, palabras clave, separadores, operadores, literales y comentarios, aunque el conjunto de categorías de token varía en diferentes lenguajes de programación . La sintaxis del lexema es típicamente un lenguaje regular , por lo que se puede usar un autómata de estados finitos construido a partir de una expresión regular para reconocerlo. El software que realiza el análisis léxico se llama analizador léxico . Este puede no ser un paso separado: se puede combinar con el paso de análisis en el análisis sin escáner , en cuyo caso el análisis se realiza a nivel de carácter, no a nivel de token.
  • El análisis sintáctico (también conocido como análisis sintáctico ) implica analizar la secuencia de tokens para identificar la estructura sintáctica del programa. Esta fase generalmente construye un árbol de análisis sintáctico , que reemplaza la secuencia lineal de tokens con una estructura de árbol construida de acuerdo con las reglas de una gramática formal que define la sintaxis del lenguaje. El árbol de análisis sintáctico a menudo se analiza, aumenta y transforma en fases posteriores del compilador. [55]
  • El análisis semántico añade información semántica al árbol de análisis y construye la tabla de símbolos . Esta fase realiza comprobaciones semánticas como la comprobación de tipos (comprobación de errores de tipo), la vinculación de objetos (asociación de referencias de variables y funciones con sus definiciones), o la asignación definitiva (requerimiento de que todas las variables locales se inicialicen antes de su uso), el rechazo de programas incorrectos o la emisión de advertencias. El análisis semántico normalmente requiere un árbol de análisis completo, lo que significa que esta fase sigue lógicamente a la fase de análisis y precede lógicamente a la fase de generación de código , aunque a menudo es posible incorporar varias fases en una sola pasada sobre el código en una implementación del compilador.

Extremo medio

El extremo medio, también conocido como optimizador, realiza optimizaciones en la representación intermedia para mejorar el rendimiento y la calidad del código de máquina producido. [56] El extremo medio contiene aquellas optimizaciones que son independientes de la arquitectura de CPU a la que se apunta.

Las fases principales del final medio incluyen las siguientes:

El análisis del compilador es un requisito previo para cualquier optimización del compilador y ambos trabajan en estrecha colaboración. Por ejemplo, el análisis de dependencia es crucial para la transformación de bucles .

El alcance del análisis y las optimizaciones del compilador varía enormemente; su alcance puede ir desde operar dentro de un bloque básico , hasta procedimientos completos o incluso el programa completo. Existe un equilibrio entre la granularidad de las optimizaciones y el costo de la compilación. Por ejemplo, las optimizaciones de mirilla se realizan rápidamente durante la compilación, pero solo afectan a un pequeño fragmento local del código y se pueden realizar independientemente del contexto en el que aparece el fragmento de código. Por el contrario, la optimización interprocedimental requiere más tiempo de compilación y espacio de memoria, pero permite optimizaciones que solo son posibles considerando el comportamiento de múltiples funciones simultáneamente.

El análisis y las optimizaciones interprocedimentales son comunes en los compiladores comerciales modernos de HP , IBM , SGI , Intel , Microsoft y Sun Microsystems . El software libre GCC fue criticado durante mucho tiempo por carecer de optimizaciones interprocedimentales potentes, pero está cambiando en este sentido. Otro compilador de código abierto con una infraestructura completa de análisis y optimización es Open64 , que es utilizado por muchas organizaciones con fines comerciales y de investigación.

Debido al tiempo y espacio adicionales necesarios para el análisis y las optimizaciones del compilador, algunos compiladores las omiten de forma predeterminada. Los usuarios deben usar opciones de compilación para indicarle explícitamente al compilador qué optimizaciones deben habilitarse.

Parte trasera

El back end es responsable de las optimizaciones específicas de la arquitectura de la CPU y de la generación de código [56] .

Las principales fases del back end incluyen las siguientes:

Corrección del compilador

La corrección del compilador es la rama de la ingeniería de software que trata de intentar demostrar que un compilador se comporta de acuerdo con su especificación de lenguaje . [58] Las técnicas incluyen el desarrollo del compilador utilizando métodos formales y el uso de pruebas rigurosas (a menudo llamadas validación del compilador) en un compilador existente.

Compilado frente a lenguajes interpretados

Los lenguajes de programación de alto nivel suelen aparecer con un tipo de traducción en mente: ya sea diseñados como lenguaje compilado o lenguaje interpretado . Sin embargo, en la práctica rara vez hay algo en un lenguaje que requiera que sea exclusivamente compilado o exclusivamente interpretado, aunque es posible diseñar lenguajes que dependan de la reinterpretación en tiempo de ejecución. La categorización generalmente refleja las implementaciones más populares o extendidas de un lenguaje; por ejemplo, a veces se dice que BASIC es un lenguaje interpretado y que C es un lenguaje compilado, a pesar de la existencia de compiladores BASIC e intérpretes C.

La interpretación no reemplaza por completo la compilación. Solo la oculta al usuario y la hace gradual. Aunque un intérprete puede ser interpretado por sí mismo, se necesita un conjunto de instrucciones de máquina ejecutadas directamente en algún lugar en la parte inferior de la pila de ejecución (ver lenguaje de máquina ).

Además, para optimizar los compiladores pueden incluir funciones de interpretación, y los intérpretes pueden incluir técnicas de compilación anticipada. Por ejemplo, cuando una expresión se puede ejecutar durante la compilación y los resultados se insertan en el programa de salida, se evita que tenga que volver a calcularse cada vez que se ejecuta el programa, lo que puede acelerar enormemente el programa final. Las tendencias modernas hacia la compilación en tiempo real y la interpretación de bytecode a veces difuminan aún más las categorizaciones tradicionales de compiladores e intérpretes.

Algunas especificaciones de lenguajes establecen que las implementaciones deben incluir una función de compilación; por ejemplo, Common Lisp . Sin embargo, no hay nada inherente en la definición de Common Lisp que impida que se interprete. Otros lenguajes tienen características que son muy fáciles de implementar en un intérprete, pero que hacen que escribir un compilador sea mucho más difícil; por ejemplo, APL , SNOBOL4 y muchos lenguajes de scripts permiten que los programas construyan código fuente arbitrario en tiempo de ejecución con operaciones de cadena regulares y luego ejecuten ese código pasándolo a una función de evaluación especial . Para implementar estas características en un lenguaje compilado, los programas generalmente deben enviarse con una biblioteca de tiempo de ejecución que incluya una versión del compilador en sí.

Tipos

Una clasificación de los compiladores es según la plataforma en la que se ejecuta el código generado, conocida como plataforma de destino.

Un compilador nativo o alojado es aquel cuya salida está destinada a ejecutarse directamente en el mismo tipo de computadora y sistema operativo en el que se ejecuta el compilador. La salida de un compilador cruzado está diseñada para ejecutarse en una plataforma diferente. Los compiladores cruzados se utilizan a menudo al desarrollar software para sistemas integrados que no están destinados a admitir un entorno de desarrollo de software.

La salida de un compilador que produce código para una máquina virtual (VM) puede o no ejecutarse en la misma plataforma que el compilador que lo produjo. Por este motivo, dichos compiladores no suelen clasificarse como compiladores nativos o cruzados.

El lenguaje de nivel inferior que es el objetivo de un compilador puede ser en sí mismo un lenguaje de programación de alto nivel . C, visto por algunos como una especie de lenguaje ensamblador portable, es con frecuencia el lenguaje de destino de tales compiladores. Por ejemplo, Cfront , el compilador original de C++ , utilizó C como su lenguaje de destino. El código C generado por un compilador de este tipo normalmente no está destinado a ser legible y mantenido por humanos, por lo que se ignoran el estilo de sangría y la creación de código intermedio C bonito. Algunas de las características de C que lo convierten en un buen lenguaje de destino incluyen la #linedirectiva, que puede ser generada por el compilador para admitir la depuración de la fuente original, y el amplio soporte de plataformas disponible con los compiladores de C.

Si bien un tipo de compilador común genera código de máquina, existen muchos otros tipos:

Los ensambladores, que traducen el lenguaje ensamblador legible por humanos a instrucciones de código de máquina ejecutadas por hardware, no se consideran compiladores. [66] [b] (El programa inverso que traduce el código de máquina al lenguaje ensamblador se llama desensamblador ).

Véase también

Notas y referencias

  1. ^ Departamento de Defensa de los Estados Unidos (18 de febrero de 1980) Requisitos de Stoneman
  2. ^ "Las numerosas características del lenguaje fuente descritas en la sección anterior dan lugar a una serie de diferencias importantes entre compiladores y ensambladores. En cualquier caso, la distinción puede no ser clara. Además, puede resultar difícil distinguir un compilador simple de un potente macroensamblador. No obstante, las diferencias suelen ser lo suficientemente sustanciales como para que siga existiendo una distinción cualitativa entre ensambladores y compiladores".
  1. ^ "Enciclopedia: Definición de compilador". PCMag.com . Consultado el 2 de julio de 2022 .
  2. ^ Compiladores de ab: principios, técnicas y herramientas de Alfred V. Aho, Ravi Sethi, Jeffrey D. Ullman - Segunda edición, 2007
  3. ^ Sudarsanam, Ashok; Malik, Sharad; Fujita, Masahiro (2002). "Una metodología de compilación reorientable para procesadores de señales digitales integrados utilizando una biblioteca de optimización de código dependiente de la máquina". Lecturas en diseño conjunto de hardware y software . Elsevier. págs. 506–515. doi :10.1016/b978-155860702-6/50045-4. ISBN . 9781558607026Un compilador es un programa de computadora que traduce un programa escrito en un lenguaje de alto nivel (HLL), como C, en un programa en lenguaje ensamblador equivalente [2] .
  4. ^ Sun, Chengnian; Le, Vu; Zhang, Qirun; Su, Zhendong (2016). "Hacia la comprensión de los errores del compilador en GCC y LLVM". Actas del 25.º Simposio internacional sobre pruebas y análisis de software . ISSTA 2016. ACM. págs. 294–305. doi :10.1145/2931037.2931074. ISBN . 9781450343909.S2CID8339241  .
  5. ^ Baghai, Christian (4 de abril de 2023). «La evolución de los lenguajes de programación: del binario primitivo a las abstracciones de alto nivel». Medium . Consultado el 10 de julio de 2024 .
  6. ^ Notas de clase. Compiladores: principios, técnicas y herramientas. Jing-Shin Chang. Departamento de Ciencias de la Computación e Ingeniería de la Información. Universidad Nacional de Chi-Nan
  7. ^ Naur, P. et al. "Informe sobre ALGOL 60". Comunicaciones de la ACM 3 (mayo de 1960), 299–314.
  8. ^ Chomsky, Noam; Lightfoot, David W. (2002). Estructuras sintácticas . Walter de Gruyter. ISBN 978-3-11-017279-9.
  9. ^ Gries, David (2012). "Apéndice 1: Forma Backus-Naur". La ciencia de la programación . Springer Science & Business Media. pág. 304. ISBN 978-1461259831.
  10. ^ Hellige, Hans Dieter, ed. (2004) [noviembre de 2002]. Escrito en Bremen, Alemania. Geschichten der Informatik - Visionen, Paradigmen, Leitmotive (en alemán) (1 ed.). Berlín/Heidelberg, Alemania: Springer-Verlag . págs.45, 104, 105. doi :10.1007/978-3-642-18631-8. ISBN 978-3-540-00217-8. ISBN 3-540-00217-0 . (xii+514 páginas)
  11. ^ Iverson, Kenneth E. (1962). Un lenguaje de programación . John Wiley & Sons. ISBN 978-0-471430-14-8.
  12. ^ Rutishauser, Heinz (1951). "Über automatische Rechenplanfertigung bei programmgesteuerten Rechenanlagen". Zeitschrift für Angewandte Mathematik und Mechanik (en alemán). 31 : 255. doi : 10.1002/zamm.19510310820.
  13. ^ Fothe, Michael; Wilke, Thomas, eds. (2015) [2014-11-14]. Escrito en Jena, Alemania. Keller, Stack und automatisches Gedächtnis – eine Struktur mit Potenzial [ Bodega, pila y memoria automática: una estructura con potencial ] (PDF) (Tagungsband zum Kolloquium, 14 de noviembre de 2014 en Jena). Serie GI: Apuntes de conferencias sobre informática (LNI) - Temáticas (en alemán). vol. T-7. Bonn, Alemania: Gesellschaft für Informatik (GI) / Köllen Druck + Verlag GmbH. págs. 20-21. ISBN 978-3-88579-426-4. ISSN  1614-3213. Archivado (PDF) del original el 12 de abril de 2020 . Consultado el 12 de abril de 2020 .[1] (77 páginas)
  14. ^ Backus, John. «La historia de FORTRAN I, II y III» (PDF) . Historia de los lenguajes de programación . Archivado (PDF) del original el 10 de octubre de 2022. {{cite book}}: |website=ignorado ( ayuda )
  15. ^ Porter Adams, Vicki (5 de octubre de 1981). "Capitana Grace M. Hopper: la madre de COBOL". InfoWorld. 3 (20): 33. ISSN 0199-6649.
  16. ^ McCarthy, J.; Brayton, R.; Edwards, D.; Fox, P.; Hodes, L.; Luckham, D.; Maling, K.; Park, D.; Russell, S. (marzo de 1960). "LISP I Programmers Manual" (PDF). Boston, Massachusetts: Grupo de Inteligencia Artificial, Centro de Computación y Laboratorio de Investigación del MIT.
  17. ^ Principios, técnicas y herramientas de compiladores, segunda edición, de Aho, Lam, Sethi y Ullman ISBN 0-321-48681-1 
  18. ^ Hopper, Grace Murray (1952). "La educación de una computadora". Actas de la reunión nacional de la ACM de 1952 (Pittsburgh) en - ACM '52 . págs. 243–249. doi : 10.1145/609784.609818 . S2CID  10081016.
  19. ^ Ridgway, Richard K. (1952). "Compiling routines". Actas de la reunión nacional de la ACM de 1952 (Toronto) en - ACM '52 . págs. 1–5. doi : 10.1145/800259.808980 . S2CID  14878552.
  20. ^ "Lista de los primeros compiladores y ensambladores".
  21. ^ Hopper, Grace. "Discurso inaugural". Actas de la conferencia ACM SIGPLAN History of Programming Languages ​​(HOPL), junio de 1978. doi : 10.1145/800025.1198341.
  22. ^ Bruderer, Herbert (21 de diciembre de 2022). "¿Grace Hopper creó el primer compilador?".
  23. ^ Strawn, George; Strawn, Candace (2015). "Grace Hopper: compiladores y Cobol". IT Professional . 17 (enero-febrero de 2015): 62–64. doi :10.1109/MITP.2015.6.
  24. ^ Knuth, Donald E.; Pardo, Luis Trabb, "Desarrollo temprano de los lenguajes de programación", Enciclopedia de Ciencias de la Computación y Tecnología (Marcel Dekker) 7: 419–493
  25. ^ Backus, John (1 de junio de 1978), "La historia de Fortran I, II y III", Historia de los lenguajes de programación , Nueva York, NY, EE. UU.: Association for Computing Machinery, págs. 25-74, doi :10.1145/800025.1198345, ISBN 978-0-12-745040-7, consultado el 9 de octubre de 2024
  26. ^ Hoare, CAR (diciembre de 1973). "Hints on Programming Language Design" (PDF) . pág. 27. Archivado (PDF) desde el original el 10 de octubre de 2022.(Esta afirmación a veces se atribuye erróneamente a Edsger W. Dijkstra , también involucrado en la implementación del primer compilador ALGOL 60).
  27. ^ Abelson, Hal; Dybvig, RK; et al. Rees, Jonathan; Clinger, William (eds.). "Informe revisado(3) sobre el esquema de lenguaje algorítmico (dedicado a la memoria de ALGOL 60)" . Consultado el 20 de octubre de 2009 .
  28. ^ "Funciones recursivas de expresiones simbólicas y su cálculo por máquina", Comunicaciones de la ACM, abril de 1960
  29. ^ McCarthy, John; Abrahams, Paul W.; Edwards, Daniel J.; Hart, Timothy P.; Levin, Michael I. (1965). Manual del programador de Lisp 1.5. The MIT Press. ISBN 978-0-26213011-0.
  30. ^ "BCPL: una herramienta para la escritura de compiladores y la programación de sistemas" M. Richards, Laboratorio Universitario de Matemáticas de Cambridge, Inglaterra, 1969
  31. ^ BCPL: El lenguaje y su compilador, M Richards, Cambridge University Press (primera publicación el 31 de diciembre de 1981)
  32. ^ Guía del usuario de BCPL Cintsys y Cintpos, M. Richards, 2017
  33. ^ Corbató, FJ; Vyssotsky, VA "Introducción y descripción general del sistema MULTICS". Conferencia conjunta de informática de otoño de 1965. Multicians.org.
  34. ^ Informe II del Comité de Desarrollo del Lenguaje Avanzado de SHARE, 25 de junio de 1964
  35. ^ Artículo de Multicians.org "La elección de PL/I", editor: Tom Van Vleck
  36. ^ "PL/I como herramienta para la programación de sistemas", FJ Corbato, Datamation, número del 6 de mayo de 1969
  37. ^ "El compilador Multics PL/1", RA Freiburghouse, GE, Conferencia informática conjunta de otoño de 1969
  38. ^ Dennis M. Ritchie, "El desarrollo del lenguaje C", Segunda conferencia sobre historia de los lenguajes de programación de la ACM, abril de 1993
  39. ^ SC Johnson, "Un compilador C portátil: teoría y práctica", 5.º Simposio POPL de la ACM, enero de 1978
  40. ^ A. Snyder, Un compilador portátil para el lenguaje C, MIT, 1974.
  41. ^ K. Nygaard, Universidad de Oslo, Noruega, "Conceptos básicos en programación orientada a objetos", SIGPLAN Notices V21, 1986
  42. ^ B. Stroustrup: "¿Qué es la programación orientada a objetos?" Actas de la 14.ª conferencia de la ASU, 1986.
  43. ^ Bjarne Stroustrup, "Una descripción general del lenguaje de programación C++", Manual de tecnología de objetos (Editor: Saba Zamir, ISBN 0-8493-3135-8 ) 
  44. ^ Leverett, Cattell, Hobbs, Newcomer, Reiner, Schatz, Wulf: "Una visión general del proyecto compilador-compilador de calidad de producción", CMU-CS-89-105, 1979
  45. ^ W. Wulf, K. Nori, "Enlace retardado en compiladores generados por PQCC", Informe de presentación de investigaciones de la CMU, CMU-CS-82-138, 1982
  46. ^ Joseph M. Newcomer, David Alex Lamb, Bruce W. Leverett, Michael Tighe, William A. Wulf - Carnegie-Mellon University y David Levine, Andrew H. Reinerit - Intermetrics: "TCOL Ada: Informe revisado sobre una representación intermedia para el lenguaje de programación estándar del Departamento de Defensa", 1979
  47. ^ William A. Whitaker, "Ada - el proyecto: el Grupo de trabajo de alto nivel del Departamento de Defensa", ACM SIGPLAN Notices (volumen 28, n.º 3, marzo de 1991)
  48. ^ Centro CECOM para la ingeniería de software, tecnología de software avanzada, "Informe final: evaluación del conjunto de referencia ACEC para aplicaciones en tiempo real", AD-A231 968, 1990
  49. ^ P. Biggar, E. de Vries, D. Gregg, "Una solución práctica para compiladores de lenguajes de programación", artículo enviado a Science of Computer Programming, 2009
  50. ^ M. Hall, D. Padua, K. Pingali, "Investigación sobre compiladores: los próximos 50 años", ACM Communications 2009 Vol 54 #2
  51. ^ Cooper y Torczon 2012, pág. 8
  52. ^ Lattner, Chris (2017). "LLVM". En Brown, Amy; Wilson, Greg (eds.). La arquitectura de las aplicaciones de código abierto . Archivado desde el original el 2 de diciembre de 2016. Consultado el 28 de febrero de 2017 .
  53. ^ Aho, Lam, Sethi, Ullman 2007, pág. 5-6, 109-189
  54. ^ Aho, Lam, Sethi, Ullman 2007, pág. 111
  55. ^ Aho, Lam, Sethi, Ullman 2007, pág. 8, 191-300
  56. ^ ab Blindell, Gabriel Hjort (3 de junio de 2016). Selección de instrucciones: principios, métodos y aplicaciones . Suiza: Springer. ISBN 978-3-31934019-7.OCLC 951745657  .
  57. ^ Cooper y Toczon (2012), pág. 540
  58. ^ "S1-A Simple Compiler", Construcción de compiladores con Java, JavaCC y Yacc , Hoboken, NJ, EE. UU.: John Wiley & Sons, Inc., págs. 289-329, 28 de febrero de 2012, doi : 10.1002/9781118112762.ch12, ISBN 978-1-118-11276-2, consultado el 17 de mayo de 2023
  59. ^ Ilyushin, Evgeniy; Namiot, Dmitry (2016). «Sobre compiladores de código fuente a código fuente». Revista Internacional de Tecnologías de la Información Abierta . 4 (5): 48–51. Archivado desde el original el 13 de septiembre de 2022. Consultado el 14 de septiembre de 2022 .
  60. ^ Aycock, John (2003). "Una breve historia del sistema Just-in-Time". ACM Comput. Surv . 35 (2): 93–113. doi :10.1145/857076.857077. S2CID  15345671.
  61. ^ Swartz, Jordan S.; Betz, Vaugh; Rose, Jonathan (22–25 de febrero de 1998). "Un enrutador rápido basado en enrutamiento para FPGAs" (PDF) . Actas del sexto simposio internacional ACM/SIGDA de 1998 sobre arreglos de puertas programables en campo - FPGA '98 . Monterey, CA: ACM . págs. 140–149. doi :10.1145/275107.275134. ISBN . 978-0897919784. S2CID  7128364. Archivado (PDF) del original el 9 de agosto de 2017.
  62. ^ Personal de Xilinx (2009). «Descripción general de la síntesis XST». Xilinx, Inc. Archivado desde el original el 2 de noviembre de 2016. Consultado el 28 de febrero de 2017 .
  63. ^ Personal de Altera (2017). «Spectra-Q™ Engine». Altera.com. Archivado desde el original el 10 de octubre de 2016. Consultado el 28 de febrero de 2017 .
  64. ^ "Descompiladores: una descripción general | Temas de ScienceDirect" www.sciencedirect.com . Consultado el 12 de junio de 2022 .
  65. ^ Chandrasekaran, Siddharth (26 de enero de 2018). "La compilación cruzada desmitificada". embedjournal.com . Consultado el 5 de marzo de 2023 .
  66. ^ Calingaert y Horowitz 1979, págs. 186-187

Lectura adicional

  • Enfoque incremental para la construcción de compiladores: tutorial en formato PDF
  • Conceptos básicos del diseño de compiladores en Wayback Machine (archivado el 15 de mayo de 2018)
  • Animación corta en YouTube que explica la diferencia conceptual clave entre compiladores e intérpretes
  • Análisis de sintaxis y análisis de LL1 en YouTube
  • Construyamos un compilador, por Jack Crenshaw
  • Foro sobre desarrollo de compiladores en Wayback Machine (archivado el 10 de octubre de 2014)
Retrieved from "https://en.wikipedia.org/w/index.php?title=Compiler&oldid=1250360370"