Pourquoi
Je fais ce projet pour apprendre, et parce que ça me plaît. Je suis le livre chapitre par chapitre avec une règle : comprendre le raisonnement derrière chaque étape plutôt que recopier du code qui marche. Je garde donc l’approche du livre, par exemple un lexer à base d’expressions régulières, même quand ce n’est pas l’implémentation la plus rapide.
Le chemin d’un programme
Un compilateur transforme un texte en un autre texte, par étapes. Chacune a un rôle précis :
| Étape | Entrée → sortie |
|---|---|
| Lexer | code source → liste de tokens |
| Parser | tokens → arbre syntaxique (AST) |
| Analyse sémantique | AST → AST vérifié (variables, types…) |
| Génération de TACKY | AST → code intermédiaire « à trois adresses » |
| Génération d’assembleur | TACKY → instructions assembleur |
| Émission du code | instructions → fichier .s |
Autour du compilateur, gcc fait le reste : il traite les #include avant (préprocesseur), puis assemble et lie le fichier .s pour produire l’exécutable.
Un premier exemple
Voici le tout premier programme que le compilateur doit savoir traduire :
int main(void) {
return 2;
}
Le lexer le découpe en tokens : int, main, (, void, ), {, return, 2, ;, }. Le parser en fait un arbre : un programme qui contient une fonction main, qui contient une instruction return, qui contient la constante 2. À la fin, le compilateur produit cet assembleur :
.globl _main
_main:
movl $2, %eax
ret
movl $2, %eax place la valeur 2 dans le registre eax, celui qui contient la valeur de retour d’une fonction. ret rend la main à l’appelant. Sur macOS, les noms de fonctions prennent un _ devant (_main) ; sous Linux, ce serait main.
Le livre cible les processeurs x86-64. Sur un Mac à puce Apple, les programmes produits tournent grâce à Rosetta 2.
Le lexer à expressions régulières
Chaque type de token est reconnu par une expression régulière :
| Token | Expression régulière |
|---|---|
| Identifiant | [a-zA-Z_]\w*\b |
| Constante | [0-9]+\b |
Mot-clé int | int\b |
| Parenthèse ouvrante | \( |
| Point-virgule | ; |
À chaque position du texte, le lexer essaie toutes les expressions et garde la correspondance la plus longue. Un piège : return correspond aussi à l’expression des identifiants. Il faut donc vérifier si un identifiant trouvé est en réalité un mot-clé.
Où j’en suis
Le livre fournit une suite de tests pour chaque chapitre : c’est le meilleur moyen de savoir si une étape est vraiment terminée.