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 :

ÉtapeEntrée → sortie
Lexercode source → liste de tokens
Parsertokens → arbre syntaxique (AST)
Analyse sémantiqueAST → AST vérifié (variables, types…)
Génération de TACKYAST → code intermédiaire « à trois adresses »
Génération d’assembleurTACKY → instructions assembleur
Émission du codeinstructions → 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 :

TokenExpression régulière
Identifiant[a-zA-Z_]\w*\b
Constante[0-9]+\b
Mot-clé intint\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.