Salut,
Le seul langage qu’un CPU comprend est le langage machine (qui est implémenté de façon physique au niveau du matériel lui-même, chaque instruction en langage machine correspond directement à un signal électrique dans les circuits du CPU). Comme le langage machine est une suite de bits que la machine peut comprendre directement, il n’y a pas besoin de "le programmer", on peut directement donner les nombres à la machine et elle exécute les instructions correspondantes.
Évidemment, fournir directement des nombres à la machine est très fastidieux et source d’erreur. Donc assez vite, on a exprimé les programmes en langage d’assemblage, qui est quasiment une représentation textuelle du langage machine. Il est trivial de compiler ce langage d’assemblage en langage machine puisqu’il y a quasiment une correspondance directe avec le langage machine, donc on peut se permettre d’écrire les "compilateurs" (appelés assembleurs) en langage machine. Une fois qu’on a un assembleur qui fonctionne, on peut le réécrire en langage d’assemblage et utiliser le premier assembleur implémenté en langage machine pour assembler le second assembleur implémenté en langage d’assemblage. On s’est alors déjà affranchi du besoin d’écrire des programmes en langage machine directement, et on peut s’appuyer sur le langage d’assemblage pour écrire des programmes un peu plus complexes.
Mais évidemment, le langage d’assemblage lui-même est aussi austère et fastidieux à manipuler directement comme il n’offre quasiment aucune abstraction par-dessus le fonctionnement de la machine et le langage machine. Les programmes écrits ne sont aussi valables que pour une architecture donnée, donc c’est pas très pratique.
Rapidement, on a développé des langages de plus haut-niveau (i.e. de plus haut niveau d’abstraction par rapport au matériel), comme FORTRAN en 1957, en écrivant des compilateurs en langage d’assemblage qui sont capables de traduire un programme écrit en FORTRAN en langage machine. Au départ, les compilateurs ne génèrent pas du code machine très efficaces, et les langages ne sont pas à un niveau d’abstraction très éloigné de la machine pour qu’écrire un compilateur en langage d’assemblage ne soit pas une tâche insurmontable. Le langage le plus célèbre de cette époque est sûrement le C (1972), qui a encore un succès énorme en tant que langage système qui permet de manipuler des considérations bas niveau (importantes pour les performances) tout en offrant un niveau d’abstraction relativement acceptable pour développer des applications conséquentes. De la même façon qu’avec le langage d’assemblage où on a juste besoin d’écrire un premier assembleur en langage machine pour ensuite écrire l’assembleur lui-même en langage d’assemblage, on peut écrire un premier compilateur C en langage d’assemblage, puis utiliser celui-ci pour compiler un compilateur écrit en C lui-même.
Sont ensuite venus des langages à plus haut-niveau d’abstraction, soit qu’on ne se fatigue même pas à compiler complètement vers du langage machine mais qu’on compile en bytecode pour une machine virtuelle (comme Java ou Python), soit que l’on compile toujours vers du langage machine mais qui s’appuient sur les progrès en théorie des langages et l’expérience accumulé avec C dans l’objectif de nous rendre la vie plus facile qu’en C (comme Zig ou Rust). Note que la première implémentation d’un langage peut être faite en n’importe quel langage existant, et qu’elle n’a même pas besoin d’être bonne puisqu’on peut facilement passer à d’autres langages (incluant le nouveau langage qu’on vient de créer) si cela est intéressant. Par exemple, Rust a été développé en OCaml originellement (en s’appuyant sur la LLVM développé en C++ pour la génération de code machine), et est maintenant développé en Rust lui-même (toujours en s’appuyant sur la LLVM).
Ça c’est pour la question de comment on implémente un langage, mais la partie difficile derrière l’invention d’un langage est surtout le design du langage. Inventer un langage est beaucoup plus que simplement écrire un compilateur pour ce langage, il faut d’abord le spécifier pour savoir où l’on va. Il faut se donner une grammaire et une syntaxe, définir un modèle mémoire, un modèle de calcul, quelles seront les features du langage, et bien sûr à quel besoin de l’industrie on essaie de répondre dans le cas où on n’est pas sur un projet jouet.