Logo EPFL
LAMP
Ecole Polytechnique Fédérale de Lausanne
Compilation 2005/2006
French only
Partie 1 : Analyseur lexical

A présenter lors de la séance du 10 novembre 2005.

Le but de cette partie est d'écrire un analyseur lexical pour le langage Zwei.

Cet analyseur doit pouvoir déterminer si un fichier donné est conforme à la grammaire lexicale de Zwei. Si c'est le cas, il doit en outre retourner la suite de lexèmes constituant le fichier. Sinon, il doit signaler une ou éventuellement plusieurs erreurs.

Pour réaliser votre analyseur lexical, nous vous fournissons un certain nombre de fichiers (voir ci-dessous). Votre travail consiste à compléter deux de ces fichiers.

L'objet Tokens définit toutes les classes de lexèmes. Pour l'instant, seules quelques unes sont définies. Il vous faut donc examiner la grammaire syntaxique de Zwei afin de déterminer quelles sont les classes de lexèmes manquantes (à chaque symbole terminal de la grammaire syntaxique correspond une classe de lexèmes).

Après cela, il ne vous restera plus qu'à compléter les méthodes nextToken et readToken de la classe Scanner. Ces méthodes ont pour but de sauter les commentaires et les espaces blancs et de lire le prochain lexème. Elles doivent placer la classe de ce lexème dans la variable d'instance token et la position du premier caractère le constituant dans la variable d'instance start. Et, si le lexème est un identificateur, un nombre ou une chaîne de caractères, elles doivent en plus stocker les caractères qui le constituent dans la variable d'instance chars. Pour assembler ces caractères en une chaîne, vous pouvez utiliser la variable d'instance buf.

Pour détecter la fin du fichier source, on utilise le caractère spécial EOF_CH. Celui-ci est retourné lorsque la fin du fichier a été atteinte. On peut donc faire comme si la grammaire lexicale avait été modifiée comme suit :

      input   = { inputelement } EOF_CH.
    

On modifie également la grammaire syntaxique comme suit :

      Program = { ClassDecl } Expression EOF_CH.
    

Le caractère EOF_CH devient ainsi un symbole terminal de la grammaire syntaxique. Il doit donc exister une classes de lexèmes qui lui corresponde. Cette classe est la classe EOF définie dans Tokens.

Le caractère EOF_CH définit un pseudo-lexème, car, dans la grammaire lexicale, EOF_CH apparaît dans la définition de input et non comme une alternative de token comme c'est le cas pour tous les vrais lexèmes. L'analyseur lexical doit donc retourner tous les lexèmes reconnus dans le fichier source plus le pseudo-lexème EOF pour signaler la fin du fichier.

Idéalement, un compilateur devrait toujours signaler autant d'erreurs que possible et non s'arrêter systématiquement après la première d'entre elles. Toutefois, pour cette première partie, nous ne vous forçons pas à implanter cela. Vous pouvez donc vous contenter de signaler le première erreur rencontrée et ensuite stopper le compilateur.

Pour signaler des erreurs utilisez les méthodes error et fail fournies par l'objet Scala Report.

Les méthodes fail impriment un message d'erreur et terminent l'exécution du programme. Utilisez ces méthodes si une erreur empêche toute poursuite de l'analyse ou si votre but est de ne signaler que la première erreur rencontrée. Si au contraire vous aimeriez essayer de continuer l'analyse après l'erreur utilisez les méthodes error. Dans tous les cas, utilisez autant que possible les versions incluant la position de l'erreur.

Le programme de test ScannerTest.scala vous permet de tester votre analyseur lexical. Il lit un fichier source et imprime la séquence de lexèmes reconnue par l'analyseur. Le fichier source est spécifié comme argument fourni au programme.

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

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

Vous trouverez ci-dessous les fichiers dont vous aurez besoin pour cette première partie.

Veuillez créer un nouveau répertoire dédié à ce projet. Placez-y le fichier Makefile et, afin de pouvoir utiliser ce dernier, placez tous les fichiers Scala dans un sous-répertoire nommé sources/zweic. Les fichiers générés lors de la compilation seront placés dans un sous-répertoire nommé classes (ne placez aucun fichier important dans ce répertoire car celui-ci est détruit par la commande gmake clean).

Finalement vous devez éditer votre Makefile afin d'y entrer votre numéro de groupe. Si vous êtes dans le groupe 5, la première definition de variable dans le Makefile devrait être comme suit (n'oubliez pas d'ajouter un zéro initial si votre numéro de groupe est inférieur à 10) :

        GROUP_NUMBER = 05
      
Vous pouvez maintenant redéfinir les permissions du répertoire de votre projet de telle façon que seul vous et les membres de votre groupe puissent accéder aux fichiers qu'il contient. Vous pouvez faire cela grâce au Makefile avec la commande qui suit :
        gmake fix-permissions
      
Afin de garantir que chaque nouveau fichier que vous créez dans le repértoire du projet soit accessible par tous les membres du groupe, il vous faut exécutez la commande suivante :
        chmod g+s <projectdir>
      
Chaque fois que vous commencer à travailler sur le projet, il est recommandé de démarrer un nouveau terminal et d'exécuter la commande  umask 007. Les éditeurs et compilateurs devraient ensuite être lancés à partir de ce terminal. Ceci vous garantit que tous les fichiers que vous créez ne sont accessibles que par les membres de votre groupe et personne d'autre.

Si vous ne voulez pas utiliser le makefile pour définir les permissions, vous pouvez directement utiliser les commandes Unix correspondantes. Le nom de votre groupe Unix est compilxxxx est un nombre à deux chiffres correspondant à votre numéro de groupe. Utilisez la commande Unix groups pour afficher tous les groupes Unix dont vous êtes membre.

Canevas Scala

Makefile : L'utilisation de ce Makefile est décrite ci-dessous.

build.xml : L'utilisation du script Ant build.xml est décrite ci-dessous.

Position.scala : Cet objet fournit des méthodes pour encoder la position dans un fichier source (ligne & colonne) à l'aide d'un seul entier.

Report.scala : Cet objet fournit des services pour signaler des erreurs.

Tokens-partial.scala (à renommer en Tokens.scala) : Cet objet définit toutes les classes de lexèmes. Pour l'instant elle n'en contient que quelques unes. A vous de la compléter.

Scanner-partial.scala (à renommer en Scanner.scala) : Cette classe implante l'analyseur lexical. Une grande partie du code manque. A vous de l'écrire.

ScannerTest.scala : Cet objet permet de tester votre analyseur lexical.

Le fichier compressé part1.zip contient tous les fichiers Scala dont vous avez besoin pour la partie 1.

Canevas Java

Les fichiers fournis avec le canevas Java sont: Makefile, build.xml, Position.java, Report.java, Tokens-partial.java, Scanner-partial.java, ScannerTest.java.

Le fichier compressé part1-java.zip contient tous les fichiers Java dont vous avez besoin pour la partie 1.

Attention, ce Makefile nécessite GNU Make. Vous devez donc utiliser la commande gmake au lieu de make. Ci-dessous, la liste des différentes commandes qu'il fournit :

gmake (ou gmake all) : compile tous les fichiers modifiés depuis la dernière compilation réussie et place les fichiers .class dans le sous-répertoire ./classes qui est créé si nécessaire.

gmake force : compile de tous les fichiers (y compris ceux qui n'ont pas changés depuis la dernière compilation réussie). Cette opération est parfois nécessaire. Si vous avez des erreurs très étranges à l'exécution, pensez à faire un gmake force, c'est souvent suffisant pour résoudre le problèmes ou du moins pour localiser sa cause.

gmake clean : efface tous les fichiers générés et supprime le répertoire ./classes (ne placez jamais rien d'important dans ce répertoire).

gmake distclean : même chose que gmake clean, mais en plus supprime tous les fichiers *.class *~ core dans le répertoire courant et tous ses sous-répertoires.

gmake grep ARGS=<regexp> : recherche l'expression régulière regexp dans tous les fichiers sources.

gmake wc : compte le nombre de lignes, mots et caractères dans tous les fichiers sources.

gmake fix-permissions : définit les permissions des fichiers dans le repértoire de votre projet de telle façon que seul vous et les membres de votre groupe puissent y accéder.

La commande ant (Ant 1.6.x) permet d'exécuter les instructions contenues dans le fichier build.xml. Vous pouvez personnaliser certains paramètres du script build.xml en modifiant la valeur des propriétés définies dans le fichier de configuration build.properties.

Par exemple, vous devez indiquer votre numéro de groupe dans la propriété "group.number".

D'autre part, la propriété "scala.home.unix" est préconfigurée pour les machines de la salle INF 3; sa valeur (resp. celle de "scala.home.win" pour les utilisateurs Windows) peut être adaptée à d'autres environnements.

Remarque: si la propriété "scala.home.*" correspondant à votre plateforme n'est pas définie, alors le script build.xml utilise la variable SCALA_HOME de votre envrionnement utilisateur.

ant (ou ant build) : voir commande gmake.

ant clean : voir commande gmake clean.

ant clean : voir commande gmake distclean.

ant run : exécute la phase du compilateur Zwei spécifiée par la propriété "compiler.main" (par défaut zweic.ScannerTest) pour le fichier source spécifié par la propriété "test.file" (par défaut Factorial.zwei) présent dans le répertoire "examples.dir" (par défaut examples/).