Langages réguliers et hors-contexte

Cours M1 INaLCO, 2008-2009

Interrogation écrite du 18/12/08 : Commentaires sur les commentaires...

  1. Les commentaires en fin de ligne : "//[^\n]*\n"
  2. Commentaires à la C et en fin de ligne : réunion des deux e.r.
  3. Contre-épreuve

L'énoncé

Les expressions régulières qu'on utilise en pratique mettent en jeu la plupart du temps des classes de caractères,
désignées soit par des paires de crochets (par exemple [0-9]), soit par des abrévations conventionnelles (par exemple \d).
Mais, pour dessiner des automates sans se tromper, il faut revenir à des transitions provoquées par des lettres individuelles.
C'est en effet la seule manière de s'assurer que l'automate est déterministe en vérifiant que chaque état reçoit son lot complet de transitions, sans manque et sans doublon. Il faut donc avant toute chose représenter par une seule lettre chaque classe de caractères en jeu, et s'assurer que ces classes sont disjointes.
Les deux automates à construire ici vont illustrer ce procédé.

1. Les commentaires en fin de ligne : "//[^\n]*\n"

Nous désignerons par n le saut de ligne et par a la classe [^\n] des caractères autres que n et que /.
L'alphabet de notre automate est donc à trois lettres {/, n, a}.

Automate Orignal
Comme le montre l'inspection des 3 transitions de chaque sommet, cet automate est déterministe.
Ce que le logiciel automate confirme à sa manière, en le dessinant comme suit (avec renumérotation des états) :

Automate déterministe

Il est en outre manifestement minimal, aucun état ne jouant le même rôle qu'un autre.
À nouveau, automate confirme en le dessinant tel quel, sauf l'état-poubelle.

Automate minimal

2. Commentaires à la C et en fin de ligne : réunion des deux e.r.

Cette fois nous avons besoin des 3 caractères individuels /, *, n, et d'une classe a qui désigne tous les autres
(et pas seulement "ni /, ni *" comme dans la donnée de l'automate des commentaires à la C,
et pas non plus "autre que n" comme ci-dessus).
L'alphabet de notre automate à construire est donc à quatre lettres {/, *, n, a}.

Puisqu'on demande un automate déterministe, la construction va suivre le principe donné au cours n° 5,
en prenant pour états les couples d'états des deux automates, et en se bornant à ceux qui peuvent être atteints
au cours d'un calcul commençant en l'état initial (0,0).
Les deux automates en question sont
Par la lettre / , l'état initial (0,0) est envoyé sur le couple (1,1).
Par les 3 autres lettres, il est envoyé sur le couple (4,5),  qui aura clairement un comportement de poubelle
dans notre nouvel automate.

Par la lettre / , l'état (1,1) est envoyé sur le couple (2,5), par la lettre * sur le couple (4,2)et par n, a sur la poubelle (4,5).
Vu la nature des deux états-poubelle 4 et 5, à partir de (2,5) le calcul va se poursuivre comme dans le premier automate
(puisque la seconde composante restera fixée à 5),
et à partir de (4,2) comme dans le second (puisque la première composante restera fixée à 4).

Du côté du premier, nous aurons donc une boucle sur (2,5) avec les lettres /, *, a, et une transition vers (3,5) par n.
L'état (3,5) est terminal, et toutes les lettres l'envoient sur la poubelle (4,5).

Du côté du second, nous aurons une boucle sur (4,2)avec les lettres /, n, a, et une transition vers (4,3) par *.
Une boucle sur (4,3) par *, une transition de retour de (4,3) vers (4,2) par n, a, et une transition vers (4,4) par /.
L'état (4,4) est terminal, et toutes les lettres l'envoient sur la poubelle (4,5).

On obtient ainsi l'automate déterministe à 8 états dont une poubelle et 2 terminaux que voici :

Union

Cet automate n'est pas minimal car les deux états terminaux jouent manifestement le même rôle, on peut donc les confondre.
Mais on ne peut pas aller plus loin dans la réduction.

En numérotant (0,0) -> 0, (1,1) -> 1, (2,5) -> 2, (3,5) -> 3, (4,2) -> 4, (4,3) -> 5, (4,4) -> 6, 
et (4,5) -> 7, on obtient dans automate le portrait flatteur que voici :

Union orig.
 Comme précédemment, le logiciel confirme que cet automate est déterministe en le redessinant de manière plus concise :

Union Det

et il le réduit sous la forme :

Union Min

3. Contre-épreuve :

À partir de cet automate minimal, le logiciel engendre l'expression régulière suivante :

//(a|/|*)*n|/*(n|a|/)**(*|(n|a)(n|a|/)**)*/

Évidemment cette expression (en syntaxe standard) est ambiguë car le générateur n'a pas su distinguer la lettre '*' de l'opérateur homonyme.
Mais nous pouvons lever l'ambiguïté à la main, en plaçant les occurrences de la lettre entre crochets. Il vient :

//(a|/|[*])*n|/[*](n|a|/)*[*]([*]|(n|a)(n|a|/)*[*])*/

où nous retrouvons bien nos deux composantes "//(a|/|[*])*n"
et  "/[*](n|a|/)*[*]([*]|(n|a)(n|a|/)*[*])*/".

Pour donner cette expression à automate, il faut demander la syntaxe étendue à la LEX,
ce qui entraîne par défaut la construction directe d'un automate déterministe.
Cet automate s'avère être minimal :
Union Bis