Qu’est-ce qu’une conception de compilateur ? Types, outils de construction, exemple

⚡ Résumé intelligent

Les phases du compilateur décrivent les six étapes qui transforment le code source en code machine : analyse lexicale, analyse syntaxique, analyse sémantique, génération de code intermédiaire, optimisation du code et génération de code, prises en charge par la gestion de la table des symboles et la gestion des erreurs.

  • 🔤 Analyse lexicale : Le scanner regroupe les caractères en jetons et les enregistre dans la table des symboles.
  • 🌳 Analyse syntaxique : L'analyseur syntaxique vérifie la grammaire et construit un arbre d'analyse hiérarchique à partir des jetons.
  • ✔️ Analyse sémantique : Cette phase vérifie la compatibilité des types et la signification à l'aide de l'arbre syntaxique.
  • ⚙️ Code Génération: Le code intermédiaire est généré, optimisé, puis finalement traduit en code machine cible.
  • 📋 Routines de support : Une table des symboles et une routine de gestion des erreurs interagissent avec chaque phase de compilation.

Phases de la compilation avec exemple : Processus et étapes de la compilation

Quelles sont les phases de conception du compilateur ?

A Compilateur Le compilateur fonctionne en plusieurs phases, chacune transformant le programme source d'une représentation à une autre. Chaque phase reçoit les données de la phase précédente et transmet sa sortie à la phase suivante. Un compilateur comporte six phases. Chacune d'elles contribue à la conversion du langage de haut niveau en code machine. Les phases d'un compilateur sont :

  1. Analyse lexicale
  2. Analyse syntaxique
  3. Analyse sémantique
  4. Générateur de code intermédiaire
  5. Code optimiseur
  6. Code générateur

Phases du compilateur

Phases du compilateur

Toutes ces phases transforment le code source en le divisant en jetons, en créant des arbres d'analyse syntaxique et en optimisant le code source à travers différentes phases.

Phase 1 : Analyse lexicale

L'analyse lexicale est la première phase, durant laquelle le compilateur parcourt le code source. Ce processus s'effectue de gauche à droite, caractère par caractère, et regroupe ces caractères en jetons.

Ici, le flux de caractères provenant du programme source est regroupé en séquences significatives grâce à l'identification des jetons. Les jetons correspondants sont ensuite insérés dans la table des symboles et transmis à l'étape suivante.

Les principales fonctions de cette phase sont :

  • Identifier les unités lexicales dans un code source.
  • Classez les unités lexicales en catégories telles que les constantes et les mots réservés, et enregistrez-les dans des tables distinctes. Les commentaires du programme source seront ignorés.
  • Identifiez un jeton qui ne fait pas partie de la langue.

Exemple : x = y + 10

Token Type
X identifiant
= Opérateur d'assignation
Y identifiant
+ Opérateur d'addition
10 Numéro

Phase 2 : Analyse syntaxique

L'analyse syntaxique consiste à découvrir la structure du code. Elle détermine si un texte respecte le format attendu. L'objectif principal de cette phase est de s'assurer de la validité du code source écrit par le programmeur.

L'analyse syntaxique repose sur les règles du langage de programmation concerné et consiste à construire l'arbre d'analyse syntaxique à l'aide de jetons. Elle détermine également la structure du langage source ainsi que sa grammaire ou syntaxe.

Voici la liste des tâches effectuées au cours de cette phase :

  • Obtenez les jetons à partir de l'analyseur lexical.
  • Vérifiez si l'expression est syntaxiquement correcte ou non.
  • Signalez toutes les erreurs de syntaxe.
  • Construisez une structure hiérarchique appelée arbre d'analyse syntaxique.

Exemple

Tout identificateur/nombre est une expression. Si x est un identificateur et y+10 une expression, alors x = y+10 est une instruction. Considérons l'arbre d'analyse syntaxique pour l'exemple suivant :

(a+b)*c

Exemple d'analyse syntaxique

Dans l'arbre d'analyse syntaxique :

  • Nœud interne : enregistrement avec un champ opérateur et deux champs pour les enfants.
  • Feuille : enregistrements comportant 2 champs ou plus ; un pour le jeton et d’autres informations concernant le jeton.
  • Veillez à ce que les composantes du programme s'articulent de manière cohérente.
  • Recueille les informations de type et vérifie la compatibilité des types.
  • Les opérandes de vérification sont autorisés par le langage source.

Phase 3 : Analyse sémantique

L'analyse sémantique vérifie la cohérence sémantique du code. Il utilise l'arbre syntaxique de la phase précédente ainsi que la table des symboles pour vérifier que le code source donné est sémantiquement cohérent. Il vérifie également si le code véhicule une signification appropriée.

L'analyseur sémantique vérifiera les incompatibilités de types, les opérandes incompatibles, une fonction appelée avec des arguments incorrects, une variable non déclarée, etc.

Les fonctions de la phase d'analyse sémantique sont les suivantes :

  • Permet de stocker les informations de type collectées et de les enregistrer dans la table des symboles ou l'arbre syntaxique.
  • Permet d'effectuer une vérification de type.
  • En cas d'incompatibilité de type, lorsqu'il n'existe pas de règles de correction de type exactes permettant de réaliser l'opération souhaitée, une erreur sémantique est affichée.
  • Collecte les informations de type et vérifie la compatibilité des types.
  • Vérifie si le langage source autorise ou non les opérandes.

Exemple

float x = 20.2;
float y = x*30;

Dans le code ci-dessus, l'analyseur sémantique convertira l'entier 30 en nombre flottant 30.0 avant la multiplication.

Phase 4 : Intermédiaire Code Génération

Une fois la phase d'analyse sémantique terminée, le compilateur génère du code intermédiaire pour la machine cible. Ce code représente un programme pour un certain nombre d'abstractions.tracmachine t.

Le code intermédiaire se situe entre le langage de haut niveau et le langage machine. Ce code intermédiaire doit être généré de manière à faciliter sa traduction en code machine cible.

Fonctions intermédiaires Code génération:

  • Elle devrait être générée à partir de la représentation sémantique du programme source.
  • Contient les valeurs calculées lors du processus de traduction.
  • Permet de traduire le code intermédiaire dans le langage cible.
  • Permet de conserver l'ordre de priorité du langage source.
  • Il contient le nombre correct d'opérandes de l'instruction.

Exemple

Par exemple :

total = count + rate * 5

Le code intermédiaire obtenu grâce à la méthode du code d'adresse est :

t1 := int_to_float(5)
t2 := rate * t1
t3 := count + t2
total := t3

Lot 5: Code Optimisation

La phase suivante consiste à optimiser le code intermédiaire. Cette phase supprime les lignes de code inutiles et réorganise la séquence d'instructions afin d'accélérer l'exécution du programme sans gaspiller de ressources. L'objectif principal est d'améliorer le code intermédiaire pour générer un code plus rapide et moins gourmand en espace.

Les principales fonctions de cette phase sont :

  • Cela vous aide à établir un compromis entre la vitesse d'exécution et la vitesse de compilation.
  • Améliore le temps d'exécution du programme cible.
  • Génère un code simplifié, toujours sous forme de représentation intermédiaire.
  • Suppression du code inaccessible et des variables inutilisées.
  • Suppression des instructions qui n'ont pas été modifiées dans la boucle.

Exemple : Considérez le code suivant :

a = intofloat(10)
b = c * a
d = e + b
f = d

Peut devenir :

b = c * 10.0
f = e + b

Lot 6: Code Génération

Code La génération est la dernière phase d'un compilateur. Elle reçoit les données issues de la phase d'optimisation du code et produit le code objet. Son objectif est d'allouer de la mémoire et de générer du code machine relogeable.

Il alloue également des emplacements mémoire pour les variables. Les instructions du code intermédiaire sont converties en instructions machine. Cette phase convertit le code optimisé ou intermédiaire dans le langage cible.

Le langage cible est le code machine. Par conséquent, toutes les adresses mémoire et tous les registres sont sélectionnés et alloués lors de cette phase. Le code généré est ensuite exécuté pour recevoir des entrées et produire les sorties attendues.

Exemple

a = b + 60.0 pourrait éventuellement se traduire en registres comme suit :

MOVF a, R1
MULF #60.0, R2
ADDF R1, R2

Gestion des tables de symboles

Une table des symboles contient un enregistrement pour chaque identificateur, avec des champs pour ses attributs. Ce composant facilite la recherche et la récupération rapide des identificateurs par le compilateur. La table des symboles simplifie également la gestion de la portée. La table des symboles et le gestionnaire d'erreurs interagissent avec toutes les phases d'exécution, et la table des symboles est mise à jour en conséquence.

Routine de gestion des erreurs

Lors de la conception d'un compilateur, des erreurs peuvent survenir dans toutes les phases suivantes :

  • Analyseur lexical : Jetons mal orthographiés.
  • Analyseur syntaxique : Parenthèse manquante.
  • Générateur de code intermédiaire : Opérandes incompatibles pour un opérateur.
  • Code Optimiseur: Lorsque la requête est inaccessible.
  • Code Generator: Lorsque la mémoire est pleine ou que les registres appropriés ne sont pas alloués.
  • Tableaux de symboles : Erreur liée à la présence de plusieurs identificateurs déclarés.

Les erreurs les plus courantes sont les séquences de caractères invalides lors de l'analyse, les séquences de jetons invalides lors de la saisie, les erreurs de portée et les erreurs d'analyse sémantique.

L'erreur peut survenir à n'importe quelle étape du processus. Après la détection des erreurs, l'étape concernée doit les traiter pour poursuivre la compilation. Ces erreurs doivent être signalées au gestionnaire d'erreurs, qui les prend en charge afin de mener à bien la compilation. Généralement, les erreurs sont signalées sous forme de message.

FAQ

Oui. Les assistants IA peuvent expliquer chaque étape. tracIls expliquent comment un exemple de code est transformé en jetons et en arbres d'analyse syntaxique, et signalent les erreurs de syntaxe ou de sémantique. Ce sont des outils d'étude utiles, mais vérifiez les explications à l'aide du contenu du cours.

L'apprentissage automatique permet de prédire quelles passes d'optimisation produisent le code le plus rapide pour un programme et une machine donnés, affinant ainsi des choix qui étaient auparavant basés sur des heuristiques manuelles. Le compilateur doit néanmoins garantir que le programme optimisé se comporte exactement comme l'original.

Le front-end gère l'analyse lexicale, syntaxique et sémantique, ainsi que le code intermédiaire, et dépend du langage source. Le back-end gère l'optimisation et la génération de code, et dépend de la machine cible. Cette séparation simplifie le reciblage.

Non. L'analyse lexicale (ou balayage) regroupe les caractères en jetons, tandis que l'analyse syntaxique (ou analyse syntaxique) organise ces jetons en un arbre d'analyse selon les règles grammaticales. L'analyse lexicale s'exécute en premier et fournit ses jetons à l'analyseur syntaxique.

Résumez cet article avec :