¿Cómo se escriben los lenguajes de programación de alto nivel, como C o Java? ¿Se crean con un lenguaje de bajo nivel, como Assembly?

En un momento, esto era cierto, pero en estos días, normalmente escribimos compiladores en C o C ++.

En muchos casos, el desarrollo inicial del lenguaje se realiza en un lenguaje de programación existente, y una vez que el primer compilador es moderadamente estable, se puede reescribir en el lenguaje que se está compilando.

Una vez escribí un compilador de Pascal de esa manera. Conseguí el subconjunto de Pascal más simple posible trabajando en BASIC (!!) y una vez que estuvo funcionando un poco (LENTAMENTE), lo reescribí en mi propio subconjunto de Pascal. Luego utilicé la versión BÁSICA del compilador por última vez, y generé un compilador de subconjunto de Pascal escrito en mi subconjunto de Pascal.

Hice esto porque estaba trabajando en casa en una computadora TRS-80, para la cual el único idioma disponible era BÁSICO. Desarrollar un compilador usando BASIC es muy doloroso.

Una vez que funcionó bien, pude descartar la versión BÁSICA y realizar mejoras incrementales en el idioma, luego en el compilador, luego en el idioma, etc.

El resultado final fue un compilador Pascal completo, escrito en Pascal completo.

¡El único problema fue que era sumamente importante hacer pruebas cuidadosas y control de versiones en el camino! Un resbalón puede hacer que tu compilador se caiga y no hay forma de solucionarlo porque no pudiste recompilarlo. De ahí la necesidad frecuente de retroceder una versión.

Sin embargo, Pascal es un lenguaje relativamente simple, y solo me tomó un par de meses para que funcionara.

Para complicar un poco la historia, al principio no tuve que compilar a Pascal en código de máquina porque no tenía un ensamblador, y la depuración de la salida binaria del compilador hubiera sido ridículamente difícil. Así que en lugar de eso, compilé a Pascal en un pequeño subconjunto de BASIC (!!), y solo lo convertí más tarde para generar el código de máquina Z80.

Este tipo de enfoque de “arranque” no era infrecuente hace 40 o 50 años, pero en estos días hay suficientes implementaciones sólidas de cosas como C y C ++ para permitirle escribir un compilador en uno de esos idiomas y nunca tener que volver a escribirlo en el idioma de destino en absoluto.

Muchos compiladores están escritos en el mismo lenguaje que compilan, generalmente porque a los ingenieros les gusta hacerlo de esa manera. Hace años trabajé para una compiladora de Ada y el compilador se escribió en Ada. Usaste la versión anterior del compilador para compilar la nueva versión, luego la usaste para compilarse para obtener una nueva versión ejecutable.

El compilador Go está escrito en Go, por ejemplo.

Algunos compiladores o intérpretes están escritos en C porque así es como comienzan los autores, y luego nunca se molestan en volver a escribir el compilador / intérprete en el idioma de destino. El compilador de Ada en el que trabajé se escribió originalmente en Pascal hasta que estuvo lo suficientemente completo, luego se reescribió en Ada para que pudiera compilarse.

También hay muchas herramientas de compilación disponibles en C.

Es un problema de huevo y gallina. El primer compilador tiene que estar escrito en otro idioma para el que ya haya un compilador disponible, pero luego se puede usar un compilador anterior para el mismo idioma.

Por ejemplo, el compilador GNU C está escrito en C.

Escribir un compilador para un idioma en el mismo idioma es un buen conjunto de pruebas para el compilador.

Creo que el compilador de Java y JVM están escritos en C, pero estoy perfectamente dispuesto a ser corregido al respecto. Aparte de eso, eres completamente correcto. Java es un caso especial porque está compilado e interpretado, pero el compilador de C está escrito en ensamblador (de nuevo, creo), y simplemente convierte el lenguaje compilado en instrucciones de ensamblaje, al igual que la JVM lo hace con las instrucciones del código de bytes.