Il est possible (si fastidieux) d’écrire du code machine direct. Peut-être écrivez-vous le programme en assembleur sur un morceau de papier, puis traduisez-le à la main en instructions numériques de code machine que vous avez entrées dans la mémoire de la machine. Vous pouvez même ignorer l'étape d'assemblage sur papier si vous avez mémorisé les valeurs numériques de toutes les instructions de code machine. Ce n'est pas inhabituel à cette époque, croyez-le ou non!
Les tout premiers ordinateurs ont été directement programmés en binaire en basculant des commutateurs physiques. Ce fut une grande amélioration de la productivité lorsque le matériel informatique a évolué pour permettre au programmeur (ou à l’assistant de saisie) de saisir le code en nombres hexadécimaux via un clavier!
Un assembleur de logiciels n'est devenu pertinent que lorsque davantage de mémoire est devenue disponible (le code de l'assembleur prenant plus de place que le code machine brut) et le matériel a évolué pour permettre une entrée alphanumérique. Les premiers assembleurs ont donc été écrits directement par des personnes parlant couramment le code machine.
Lorsque vous avez un assembleur, vous pouvez écrire un compilateur pour un langage de niveau supérieur dans l'assembleur.
L'histoire de C comporte plusieurs étapes. Le premier compilateur C a été écrit en B (un prédécesseur de C) qui à son tour a été écrit en BCPL. BCPL est un langage assez simple (par exemple, il n’a pas du tout de types), mais reste un pas en avant de l’assembleur brut. Vous voyez donc comment des langages de plus en plus complexes sont construits dans des langages plus simples jusqu’à assembleur. Et lui-même C est un langage assez petit et simple par rapport aux normes actuelles.
Aujourd'hui, le premier compilateur pour un nouveau langage est souvent écrit en C, mais lorsque le langage atteint une certaine maturité, il est souvent réécrit "en soi". Le premier compilateur Java a été écrit en C, mais réécrit plus tard en Java. Le premier compilateur C # a été écrit en C ++, mais il a récemment été réécrit en C #. Le compilateur / interprète Python est écrit en C, mais le projet PyPy tente de le réécrire en Python.
Cependant, il n'est pas toujours possible d'écrire un compilateur / interprète pour une langue dans la langue elle-même. Un interpréteur JavaScript écrit en JavaScript existe, mais les compilateurs / interprètes des navigateurs actuels sont toujours écrits en C ou C ++ pour des raisons de performances. JavaScript écrit en JavaScript est tout simplement trop lent.
Mais vous n'avez pas à utiliser C comme "langage de départ" pour un compilateur. Le premier compilateur F # a été écrit en OCaml, l’autre langage le plus proche de F #. Une fois le compilateur terminé, il a été réécrit en F #. Le premier compilateur pour Perl 6 a été écrit en Haskell (un langage fonctionnel pur très différent de Perl), mais a maintenant un compilateur écrit en C.
Rust, où le premier compilateur a été écrit en OCaml (il est maintenant réécrit en Rust) est un cas intéressant. Ceci est notable car OCaml est généralement considéré comme étant de niveau supérieur à Rust, langage de systèmes plus proche du métal. Ce ne sont donc pas toujours des langages de niveau supérieur implémentés dans des langages de niveau inférieur, ce pourrait également être l'inverse.