Logo EPFL
LAMP
Ecole Polytechnique Fédérale de Lausanne
Compilation 2005/2006
French only

Partie 4 : Analyse des noms et des types

A présenter lors de la séance du 10 janvier 2006.

Le but de cette partie est d'ajouter la phase d'analyse sémantique au compilateur Zwei.

Cette analyse sémantique effectue deux tâches : l'analyse des noms et l'analyse des types. Au même titre que l'analyse lexicale se basait sur la grammaire lexicale et l'analyse syntaxique sur la grammaire syntaxique, l'analyse sémantique se base sur les règles de typage de Zwei, qui ont été vues au cours.

L'analyse des noms consiste à associer à chaque identificateur du programme sa définition, et donc à vérifier que les identificateurs utilisés dans le programme sont effectivement définis.

L'analyse des types consiste à vérifier que les types des différentes parties du programme sont corrects.

Une fois l'analyse sémantique effectuée avec succès, la production de code pourra enfin commencer, car le programme sera garanti correct par rapport aux règles statiques du langage.

L'analyse des noms consiste à associer à chaque utilisation d'un nom la définition de ce nom. Dans l'arbre de syntaxe abstraite, les définitions et les utilisations de noms sont représentés par des instances de la classe Name. Dans ces instances de la classe Name, le champ sym de type Symbol est initialement indéfini. Un symbole représente les attributs d'un nom. Il y a une sorte de symbole pour chaque sorte de nom (classe, champ, méthode, variable). Un des buts de l'analyseur sémantique est de donner une valeur à ce champ sym pour toutes les instances de la classe Name apparaissant dans l'arbre de syntaxe abstraite. Lors d'une déclaration de nom, on crée un nouveau symbole, lors d'une utilisation de nom, on récupère un symbole existant dans un des environnements de typage.

Si le programmeur désire utiliser des chaînes de caractères dans son programme Zwei, il doit lui-même définir les classes List, Nil et Cons dans le source Zwei. Voici une implantation minimale (vous êtes libres d'ajouter d'autres fonctionnalités; voir scala.List) de ces trois classes:

     
   class List {
     Int isEmpty() { return this.isEmpty() }
     Int head() { return this.head() }
     List tail() { return this.tail() }
     List cons(Int x) { return this.cons(x) }
   }

   class Cons extends List {
     Int head;
     List tail;
     Int isEmpty() { return false }
     Int head() { return this.head }
     List tail() { return this.tail }
     List cons(Int x) { return new Cons(x, this) }
   }

   class Nil extends List {
     Int isEmpty() { return true }
     List cons(Int x) { return new Cons(x, this) }
   }
      

L'analyse des types calcule le type des différentes parties du programme et vérifie qu'ils sont corrects.

On utilise deux représentations pour les types dans le compilateur Zwei. Celle définie dans le fichier Tree.scala et celle définie dans le fichier Type.scala. La première est utilisée pour représenter les types qui apparaissent dans le programme source écrit par l'utilisateur, la deuxième est utilisée en interne par le compilateur pour faire son analyse.

Les types de Zwei sont :

  • les types primitifs Int et Null,
  • les types de classe définis par l'utilisateur.

Ces types sont liés entre eux par une relation de sous-typage. L'interprétation de cette relation est qu'un type T1 est un sous-type d'un autre type T2 si des valeurs de type T1 peuvent être utilisées partout où des valeurs de type T2 sont attendues. On écrit alors T1 <: T2.

Le type Null est le type de la valeur null qui représente une référence nulle d'objet. Comme null peut être utilisé partout où l'on attend un objet, son type, Null, doit être un sous-type de tous les types de classe et de lui-même (mais pas du type Int).

Lors des phases précédentes, vous étiez autorisé à vous arrêter après la première erreur. Cela n'est pas le cas pour cette phase; cette fois-ci, toutes les erreurs doivent être signalées. Il peut arriver qu'à cause d'une erreur, il soit impossible de calculer le type d'un nœud. Dans ce cas, vous pouvez lui attribuer le type IBadType défini dans la classe Type. Il peut aussi arriver que, malgré une erreur, on puisse donner un type à un nœud. Par exemple, on pourra toujours donner le type Int à l'expression e1 + e2, même si l'une des deux opérandes est mal typée.

Le programme de test AnalyzerTest.scala vous permet de tester votre analyseur sémantique. Il lit un fichier source et affiche les erreurs rencontrées s'il y en a. Si le programme analysé est correct, l'analyseur doit terminer sans rien afficher.

Pour lancer le programme à partir du répertoire racine de votre projet, utilisez la commande suivante :

      scala -cp ./classes zweic.AnalyzerTest fichier-source
    

Pour cette partie, nous vous fournissons les fichiers suivants :

Canevas Scala

Type-partial.scala (à renommer en Type.scala) contient la définition de la classe Type et de ses sous-classes, qui modélisent les types internes de Zwei. Ces classes définissent des méthodes permettant de comparer deux types et de calculer le maximum de deux types. Sont à compléter les méthodes isSubtype, isSametype et lub.

Symbol.scala contient la classe Symbol, qui modélise les symboles.

Analyzer-partial.scala (à renommer en Analyzer.scala) contient les fonctions d'analyse sémantique. Il s'agit du principal fichier à compléter.

AnalyzerTest.scala contient le programme de test.

Le fichier compressé part4.zip contient tous les fichiers dont vous avez besoin pour la partie 4 (part4.zip contient également les fichiers fournis pour les parties 1 à 3).

Canevas Java

Les fichiers fournis avec le canevas Java sont: Makefile, build.xml, Symbol.java, Type-partial.java, DefaultVisitor.java, Analyzer-partial.java, AnalyzerTest.java.

Le fichier compressé part4-java.zip contient tous les fichiers Java dont vous avez besoin pour la partie 4 (part4-java.zip contient également les fichiers fournis pour les parties 1 à 3).