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