Analyse lexicale

Créez un répertoire pour le projet. Là-dedans créez un réez un répertoire sources et un autre répertoire classes. Dans sources créez encore un répertoire scanner où vous stockez finalement les trois fichiers Token.java, Scanner.java et ScannerTest.java.

Compilez les trois fichiers à partir du répertoire projet en utilisant
      javac -d classes sources/scanner/*.java
      
Vous obtiendrez un répertoire scanner dans classes qui contient les fichiers Token.class, Scanner.class et ScannerTest.class.

Testez le fonctionnement du programme sur le fichier test1 que vous stockez dans le répertoire projet en utilisant
      java -classpath classes scanner.ScannerTest test1
      
Le code dans Token.java ainsi que dans Scanner.java doit encore être complété. Il est conseillé de commencer par Token.java, et d'ajouter ensuite fonctionnement par fonctionnement à Scanner.java.


Les lexèmes

Token.java définit une interface pour les lexèmes. Les lexèmes (rappel: les lexèmes sont l'alphabet de l'analyse syntaxique) sont représentés par des nombres entiers. Par exemple, ERROR = 0, EOF = 1, NUM = 2, etc.

Il est recommandé d'arranger les lexèmes dans des groupes (mots clé, parenthèses, etc.), surtout s'il s'agit d'un langage avec une large syntaxe.

Pour pouvoir plus facilement rajouter d'autres lexèmes plus tard, il est souvent utile de donner des valeurs relatifs, c-à-d on définit ERROR = 0, EOF = ERROR + 1, NUM = EOF + 1, etc.

Ou bien on commence des groupes de lexèmes avec des nombres fixes, mais en gardant les valeurs relatifs dans le groupe, par exemple ERROR = 0, EOF = ERROR + 1, NUM = 100, ID = NUM + 1, etc.

Symboles spéciaux

Le lexème EOF signifie la terminaison des données. Dans notre cas, cela seront plutôt un fichier, mais cela peut aussi être par exemple une instruction sur la ligne de commandes.

Le lexème ERROR peut être utilisé pour un traîtement d'erreurs. Nous n'allons probablement pas nous en servir.

Opérateurs

Les opérateurs numériques ("+", "-", "*", "/") sont déjà réalisés dans Token.java. Il reste à ajouter des représentations pour les opérateurs de comparaison ("=", "<") et pour les opérateurs booléens ("&", "!").

Mots clé

Il faut des lexèmes pour les mots clé sur le niveau des processus ("proc"), les boucles ("while", "loop") et les conditionnels ("if", "then", "else").

Commandes

Il y a l'assignation (":="), les actions ([toto]), et les délimiteurs (";", "||").

Remarque: Il est un choix de design de reconnaître les actions comme tels dans l'analyse lexicale. C'est possible parce que les actions se composent d'une simple séquence de caractères.
On pourrait argumenter que ce traîtement est conceptuellement plutôt une affaire de l'analyse syntaxique. De l'autre côté cela permet de distinguer les actions des suites d'identificateurs, de garder les espaces vides et d'accepter plus de symboles sans que l'analyseur syntaxique devrait faire la distinction.
Il n'existe finalement pas un seul concept de concevoir des analyseurs lexicaux.

Parenthèses

La syntaxe du langage comprend des parenthèses ("(", ")") ainsi que des accolades ("{", "}").

Ce qui est à faire

Rajoutez les lexèmes qui manquent à Token.java. Choisissez des noms qui sont faciles à mémoriser mais néanmoins pas trop longs (on ne les écrire pas qu'une seule fois).

Ensuite complétez la fonction representation(int symbol) dans ScannerTest.java. Autrement vous obtiendrez "" pour la plupart des données. N'oubliez pas à faire imprimer les actions (comme c'est déjà fait pour les nombres et identificateurs) par la fonction main.

Il est conseillé de récompiler les fichiers plusieurs fois pendant les travaux de modification pour mieux tenir en compte des éventuels messages d'erreur.


L'analyse lexicale

Fonctions et variables publiques

L'analyse lexicale réalise des fonctions de service pour l'analyse syntaxique. Elle retourne une suite de lexèmes décrivant les donnés.
      /** lexeme **/
      int symbol;

      /** lire un mot et stocker le lexeme correspondant
       *  dans symbol **/
      void next();
      
Un identificateur "x11" par exemple donne le lexéme ID. Comme celui-ci n'est qu'un entier et ne contient naturellement pas d'information sur le nom de l'identificateur, celui-ci est stocké dans une variable.
      String chars;
      
Il n'est biensû pas nécessaire de stocker de l'information dans chars lors la lecture d'un mot clé par exemple. Celui-ci est déjà suffisamment décrit par le lexème.

Pour aider l'utilisateur lors des messages d'erreur, nous rajoutons une variable
      int pos;
      
qui tient compte de la position (la ligne de code) actuelle.

La fonction
      void close();
      
sert à fermer le flux d'entrée.

La fonction
      void error(String msg);
      
peut être utiliser aussi par les phases suivantes pour signaler des erreurs. Biensûr que c'est un traîtement un peu simpliste. Alternativement on peut créer des classes d'erreur pour des traîtements divers et plus spécifics.

Fonctions et variables privées

L'analyseur lexicale lit les données caractèver par caractère en utilisant la fonction nextChar() qui les stocke dans la variable c.
      /** le dernier caractere lu **/
      private char c;

      /** lire les donnees **/
      private void nextChar();
      

Ce qui est à faire

Complétez la fonction next() dans Scanner.java. Travaillez en étapes. Par exemple, rajoutez d'abord la reconnaissance des opérateurs booléens et testez-les par des exemples, avant de continuer.

Opérateurs booléens, parenthèses: La réalisation est simple, lors qu'il s'agit des caractères singuliers. La réalisation des opérateurs numériques peut servir comme exemple. N'oubliez pas à avancer après avoir spécifié le symbole.

Assignation, composition parallèle: Dans ces cas, deux caractères doivent être pris en compte. Pour le deuxième il n'y a pas de choix; voir traîtement dans le tableau sur le transparent 24 du cours.

Nombres et identificateurs: Les identificateurs sont déjà réalisés en utilisant un tampon de caractères StringBuffer buf. Il manque désormais le code pour quelques-uns des mots clé. Pour le fonctionnement des tampons en Java, consultez la spécification d'API.
La réalisation des nombres y correspond largement.

Actions: Une action commence par "[" et finit par "]". On accepte des lettres et des chiffres ainsi que des symboles spéciaux. Pour représenter "]" dans le texte d'une action, il faut l'échapper par "\".

Position: Pour l'instant, chaque symbole est retourné avec la même position. Modifiez nextChar() et next() (actions!) pour obtenir les bonnes positions.


Christine Röckl
Last modified: Fri Apr 12 14:49:08