|
En principe, votre analyseur lexical devrait maintenant être
terminé. Au cours de cette séance, vous devriez bien avancer dans
votre analyseur syntaxique, voir le terminer.
Analyseur lexical
Si vous avez encore quelques problèmes avec les commentaires ou
les chaînes de caractères, vous pouvez lire les sections
ci-dessous. Mais, dans tous les cas, le plus important est que les
principaux lexèmes soient reconnus. Ce n'est pas très grave si
certains commentaires un peu bizarres ne sont pas reconnus ou si
les chaînes de caractères ne peuvent pas contenir de caractères
d'échapement (\n, \", etc).
Commentaires
Lors des deux dernières séances, vous avez pu constater qu'il
n'est pas très facile de construire une expression régulière qui
fonctionne pour tous les commentaires. Ou alors, si vous avez
repris celle du cours (ou plutôt la version corrigée donnée ici), vous aurez
probablement remarqué qu'elle paraît bien plus compliquée que
nécessaire. Cette section montre comment cette expression peut
être construite. Elle vous permettra également de comprendre
pourquoi cette expression est aussi compliquée.
Définition
Avant de pouvoir construire une expression régulière pour les
commentaires, il faut se donner une définition précise de se qu'on
appelle un commentaire. La voici: un commentaire est une séquence
de caractères qui débute par la séquence /* et se termine
avec la première séquence */ la suivant.
Exemples
Ci-dessous quelques fichiers que nous utiliserons pour tester nos
expressions régulières. Chaque fichier contient un commentaire et
est accompagné d'un fichier contenant le résultat que devrait
afficher le programme ScannerTest.
Tests
Afin de pouvoir tester les expressions régulières, nous
utiliserons le code ci-dessous à l'intérieur de l'action
correspondant au commentaire.
System.out.println("#" + yytext() + "#");
Ainsi, toutes les séquences reconnues comme des commentaires
seront affichées entourées par des caractères #. Le
tableau ci-dessous montre ce que nos quatre fichiers d'exemple
devraient afficher.
Fichier source |
Résultat affiché |
/* abc xyz */ |
#/* abc xyz */# |
/* abc */ xyz */ |
#/* abc */# |
/* abc *** xyz */ |
#/* abc *** xyz */# |
/* abc ***/ xyz ***/ |
#/* abc ***/# |
Construction
À priori, les commentaires ne paraissent pas très compliqués à
décrire avec une expression régulière. Après tout, il s'agit
simplement d'une séquence qui commence par /*, continue
avec une suite arbitraire de caractères et se termine par
*/. On obtient donc l'expression regulière
"/*".*"*/" (les expressions régulières sont données avec
les notations de jlex et peuvent donc être recopiées sans
autre dans le fichier jex.lex afin d'être testées). En
testant cette expression sur nos quatre exemples, on obtient les
résultats suivants:
Expression: "/*".*"*/" |
Fichier source |
Résultat affiché |
/* abc xyz */ |
#/* abc xyz */# |
/* abc */ xyz */ |
#/* abc */ xyz */# |
/* abc *** xyz */ |
#/* abc *** xyz */# |
/* abc ***/ xyz ***/ |
#/* abc ***/ xyz ***/# |
On constate qu'il y a un problème, car les commentaires ne
s'arrètent pas à la première séquence */. Ceci est dû au
fait que pour une expression donnée, l'analyseur retourne toujours
la plus longue chaîne possible. Une solution à ce problème est de
remplacer la séquence arbitraire de caractères (.*) par
une séquence ne contenant pas de caractère *
([^\*]). On obtient alors l'expression
"/*"([^\*])*"*/" et les résultats suivants (l'indication
<pas de commentaire> signifie qu'aucun commentaire n'a été
trouvé):
Expression: "/*"([^\*])*"*/" |
Fichier source |
Résultat affiché |
/* abc xyz */ |
#/* abc xyz */# |
/* abc */ xyz */ |
#/* abc */# |
/* abc *** xyz */ |
<pas de commentaire> |
/* abc ***/ xyz ***/ |
<pas de commentaire> |
Cette nouvelle expression fonctionne correctement pour les
commentaires ne contenant pas de caractères *. Par contre
les commentaires qui en contiennent ne sont plus reconnus. Pour
cela, on modifie l'expression pour indiquer qu'un commentaire peut
aussi contenir une suite de caractères * qui n'est pas
directement suivie d'un caractère / ("*"+[^\/]).
On obtient ainsi l'expression "/*"([^\*]|"*"+[^/])*"*/"
qui donne les résultats suivants:
Expression: "/*"([^\*]|"*"+[^/])*"*/" |
Fichier source |
Résultat affiché |
/* abc xyz */ |
#/* abc xyz */# |
/* abc */ xyz */ |
#/* abc */# |
/* abc *** xyz */ |
#/* abc *** xyz */# |
/* abc ***/ xyz ***/ |
#/* abc ***/ xyz ***/# |
L'expression ne fonctionne pas pour le dernier exemple. Pourtant
notre expression spécifie que les suites de caractères *
ne doivent pas être suivies par un caractère /. Un
commentaire ne devrait donc pas pouvoir contenir la séquence
***/. Pourquoi donc, le commentaire contient-il quand
même cette séquence ? La raison est que seuls les deux
premiers caractères * sont vus comme une suite de
caractères * ("*"+). Le troisème est reconnu
comme un caractère différent de / ([^\/]) et le
caractère / est reconnu comme un caractère différent de
*([^\*]). Comme l'analyseur retourne toujours la
plus longue chaîne possible, il ne s'arrète qu'à la seconde
séquence */. Pour résoudre ce problème, il nous faut
spécifier que la suite de caractères * ne peut pas non
plus être suive d'un caractère * ([^\/\*]). Cela
nous donne l'expression "/*"([^\*]|"*"+[^\/\*])*"*/" et
les résultats suivants:
Expression: "/*"([^\*]|"*"+[^\/\*])*"*/" |
Fichier source |
Résultat affiché |
/* abc xyz */ |
#/* abc xyz */# |
/* abc */ xyz */ |
#/* abc */# |
/* abc *** xyz */ |
#/* abc *** xyz */# |
/* abc ***/ xyz ***/ |
<pas de commentaire> |
Cette dernière solution semble presque pire que la précédente étant
donné qu'elle ne reconnaît plus du tout de commentaire dans le dernier
exemple. Le problème est qu'avec notre expression toute suite de
caractères * doit être directement suivie d'un caractère
différent de * et de /. Une telle suite ne peut donc
pas directement précédé la séquence de terminaison */ car
entre deux il doit y avoir au moins un caractère différent de
* et de /. Pour résoudre ce dernier problème, il
faut spécifier que la séquence */ peut-être précédée d'une
suite de caractère *. Ce qui nous donne l'expression
"/*"([^\*]|"*"+[^\/\*])*"*"*"*/" qui est équivalente à
"/*"([^\*]|"*"+[^\/\*])*"*"+"/". Celle-ci nous donne bien les
résultats escomptés:
Expression: "/*"([^\*]|"*"+[^\/\*])*"*"+"/" |
Fichier source |
Résultat affiché |
/* abc xyz */ |
#/* abc xyz */# |
/* abc */ xyz */ |
#/* abc */# |
/* abc *** xyz */ |
#/* abc *** xyz */# |
/* abc ***/ xyz ***/ |
#/* abc ***/# |
Chaînes de caractères
Si vous avez des problèmes pour construire une expression
régulière pour les chaînes de caractères, procédez comme il a été
fait pour les commentaires; commencez par une expression qui
fonctionne pour les cas généraux, déterminez les cas qui ne
fonctionne pas et rafinez votre expression.
|