Computer Science Department
Programming Methods Laboratory
(LAMP)
Ecole Polytechnique Federale de Lausanne
Programmation Avancée et Compilation     semestre d'été 2000

Projet - Séance 3 (07.04.2000)

Analyseur lexical
Commentaires
Chaînes de caractères
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.


Teaching
LAMP homepage
Last modified: 27.04.2000, Philippe Altherr <philippe.altherr@epfl.ch>